如何使用 $mdTheming 将主题应用到自定义元素

How to use $mdTheming to apply theme to custom element

我一直在想办法使用 angular-material 库提供的 $mdTheming 服务来应用通过 $mdThemingProvider 配置的主题。

简单来说:

考虑一个元素

<div class="md-primary" theme></div>

theme指令在这种情况下必须注入主要颜色配置为主题。

下面是我在阅读 angular-material 的代码后尝试使用的方法

Just to see how they might have done it

export function ThemeDirective($mdTheming) {
 'ngInject';
 
 let directive = {
   restrict: 'A',
   link: (scope, element) => {
    $mdTheming(element);
   }
 };

  return directive
}

上面的代码似乎没有做任何事情。我确定我忽略了一些东西。需要帮助。

这可能有助于您的研究...您可以使用它来将主题应用到您的指令中。我从 link https://material.angularjs.org/latest/Theming/05_under_the_hood

开始

当您进一步查看 angular 代码时,您会发现常量 $MD_THEME_CSS。这基本上是一个 css 文件,其中包含每个指令的所有样式 - 但它使用插入的标签进行格式化:


    ...
    md-input-container.md-THEME_NAME-theme .md-input {
        color: {{foreground-1}};
        border-color: {{foreground-4}};
        text-shadow: {{foreground-shadow}};
    }

    md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder,md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder {
        color: {{foreground-3}};
    }
    ...

它们用颜色值替换的这些标签显示在 mdThemingProvider 的文档中...


    /* Some Example Valid Theming Expressions
     * =======================================
     * Intention group expansion: (valid for primary, accent, warn, background)
     * {{primary-100}} - grab shade 100 from the primary palette
     * {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
     * {{primary-100-contrast}} - grab shade 100's contrast color
     * {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
     * {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
     * {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
     * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
     * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
     * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
     *
     * Foreground expansion: Applies rgba to black/white foreground text
     * {{foreground-1}} - used for primary text
     * {{foreground-2}} - used for secondary text/divider
     * {{foreground-3}} - used for disabled text
     * {{foreground-4}} - used for dividers
     */

在定义所有主题后,此字符串会在 运行 时由 generateAllThemes() 格式化。这会将这些样式注入到 元素中——正如您在 chrome 中检查您的页面所看到的那样: enter image description here

现在我个人以前从未这样做过,所以我不知道这里的标准,也找不到它的文档。但我假设您可以以某种方式调用 GenerateTheme() 来生成您自己的样式以在您的 html 中使用...或者可能只是借用一些已经为核心代码生成的 类。

然而,我使用我自己的服务而不是他们的服务做了类似的事情。

这是我过去使用过的解决方案的一个可能的开始...在我的 .config() 中定义主题后,我编写了一个简单的提供程序来保存我的主题调色板。然后我写了一个服务来将主题颜色代码连接到实际的 rgb 颜色。希望它不会太hacky。

(function() {
  var app = angular.module('MyApp', ['ngMaterial', 'ngMessages'])

  // The provider... I store the color palette here since a service isn't available during .config();
  .provider('colorPalette', function colorPaletteProvider() {
    var _PALETTE = {};

    this.SetPalette = function(value) {
      _PALETTE = value
    }

    this.$get = [
      function() {
        return _PALETTE;
      }
    ];
  })

  .config(function($mdThemingProvider, colorPaletteProvider) {
    var xoMap = $mdThemingProvider.extendPalette('purple', {
      '500': '833A96'
    });
    $mdThemingProvider.definePalette('XO-Main', xoMap);

    // add a couple of themes
    $mdThemingProvider.theme('default')
      .primaryPalette('XO-Main')
      .accentPalette('pink', {
        "default": "500",
        "hue-1": "50"
      })
      .backgroundPalette('grey');

    $mdThemingProvider.theme('order')
      .primaryPalette('XO-Main')
      .accentPalette('light-blue', {
        "default": "500",
        "hue-1": "50"
      });

    // save the palette so i can see it later
    colorPaletteProvider.SetPalette($mdThemingProvider._PALETTES);
  })

  .run(function($interpolate, themeColorsService) {
    // inject some styles into the head
    var orderTheme = themeColorsService.GetColors("order");
    var myStyle = $interpolate(".nav-icon-order {color: {{accent.default.bg}};}")(orderTheme);
    console.debug(myStyle);
    themeColorsService.AddStyle(myStyle);
    themeColorsService.AddStyle($interpolate("md-toolbar.hpbx-toolbar-accent-order, .panel-heading.hpbx-toolbar-accent-order { border-bottom: 5px solid {{accent.default.bg}};}")(orderTheme));

  });

  // The controller
  app.controller("AppCtrl", function($scope, themeColorsService) {
    $scope.themeColors = themeColorsService.GetColors("default");
  });
})();

// example directive where the theme is passed in
angular.module('MyApp').directive('theme', function (themeColorsService) {
        return {
            restrict: "A",
            transclude: true,
            template: "<div ng-style='{color: themeColors.primary.default.bg}' ng-transclude></div>",
            scope: {
                themeName: "="
            },
            controller: function ($scope, $element, $attrs) {
                $scope.themeColors = themeColorsService.GetColors("default");
            }
        }
    });

// The service
angular.module('MyApp').service("themeColorsService", function(colorPalette, $mdTheming) {
  this.themes = {};

  // tie the color codes together with the palettes
  this.GetColors = function(theme) {
    var returnVal = {};
    if (!this.themes.hasOwnProperty(theme)) {
      theme = $mdTheming.THEMES[theme];

      _.keys(theme.colors).forEach(function(key) {
        returnVal[key] = {};
        var palette = theme.colors[key].name;
        if (!_.isUndefined(palette)) {
          _.keys(theme.colors[key].hues).forEach(function(hue) {
            var c = theme.colors[key].hues[hue];
            var p = colorPalette[palette][c];

            returnVal[key][hue] = {};
            returnVal[key][hue].bg = getRGB(p.value);
            returnVal[key][hue].fg = getRGB(p.contrast);
          });
        }
      });

      this.themes[theme] = _.cloneDeep(returnVal);
    }

    return this.themes[theme];
  };

  var getRGB = function(value) {
    var returnVal = "";

    if (value.length == 4) {
      returnVal = "rgba(" + value[0] + "," + value[1] + "," + value[2] + "," + value[3] + ")";
    } else if (value.length == 3) {
      returnVal = "rgb(" + value[0] + "," + value[1] + "," + value[2] + ")";
    }

    return returnVal;
  };

  // insert a style into the head element
  this.AddStyle = function(styleContent) {
    var head = document.head;
    var firstChild = head ? head.firstElementChild : null;

    var style = document.createElement('style');
    style.setAttribute('xo-theme-style', '');
    style.appendChild(document.createTextNode(styleContent));
    head.insertBefore(style, firstChild);
  };
});
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.css" rel="stylesheet" />

<html ng-app="MyApp">

<body>
  <div ng-controller="AppCtrl">
    <div>I'm unstyled</div>
    
    <!-- style using ng-style -->
    <div ng-style="{color: themeColors.primary.default.bg}">I'm styled with ngStyle</div>
    
    <!-- use your injected css style -->
    <div class="nav-icon-order">I'm using an injected css class</div>
    
    <!-- send it to a directive the way you listed in your example -->
    <div theme>This is a styled directive</div>
    
    <!-- show the themeColors object -->
    <pre>themeColors:{{themeColors | json}}</pre>
  </div>
</body>

</html>