How to use $mdTheming to apply theme to custom element

随声附和 提交于 2021-02-07 04:32:05

问题


I've been trying to figure out a way to use $mdTheming service provided by angular-material library to apply theme configured through $mdThemingProvider.

In Simpler terms:

Consider an element

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

The theme directive in this case has to inject the primary color configure to the theme.

Below is the approach I tried to use, after going through angular-material's code

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
}

The above code doesn't seem to be doing anything. I'm sure I've overlooked something. Need help.


回答1:


This may help in your research... you can use it towards applying a theme to your directive. I started with the link https://material.angularjs.org/latest/Theming/05_under_the_hood

When you look further into the angular code, you'll find the constant $MD_THEME_CSS. This is basically a css file with all the styles for each of their directives - but it is formatted with tags that they interpolate:


    ...
    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}};
    }
    ...

These tags they replace with color values are shown in the documentation of the 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
     */

This string is then formatted at run time by generateAllThemes() after all the themes have been defined. This will inject these styles into the <head> element - as you can see by inspecting your page in chrome: enter image description here

Now I've never done this before personally so i don't know the standards here, and cannot find it documented. But I'm assuming you can call GenerateTheme() somehow to generate your own styles to use in your html... or maybe just borrow some classes that have already been generated for the core code.

I have however done something similar using my own services rather than theirs.

Here's a possible start to your solution that I've used in the past... I wrote a simple provider to save my theme palette after i've defined the theme in my .config(). Then i wrote a service to connect the theme color codes to the actual rgb colors. Hopefully it's not too 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>


来源:https://stackoverflow.com/questions/33691944/how-to-use-mdtheming-to-apply-theme-to-custom-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!