简体   繁体   English

如何在装饰器中访问ngStyle键和值?

[英]How do I access the ngStyle key and values in decorator?

I have a list of colour names in my application. 我的应用程序中有一个颜色名称列表。

let colours = {
  mango: '#e59c09',
  midnight: '#1476a0'
};

I want to extend the ngStyle directive to be able to understand my custom colour names. 我想扩展ngStyle指令,以便能够理解我的自定义颜色名称。 I'm doing this by decorating the ngStyle directive. 我是通过装饰 ngStyle指令来做到这一点的。 However, I've hit an uphill battle on the decorator's compile function. 但是,我在装饰者的编译功能上遇到了一场艰苦的战斗。 I can access the elements' ngStyle attribute, but it comes up as a string (understandably). 我可以访问元素的ngStyle属性,但它是一个字符串(可以理解)。 JSON.parse() doesn't work on it as it isn't always a valid JSON string due to bind once etc... JSON.parse()不起作用,因为它不总是一个有效的JSON字符串,因为绑定一次等...

I simply want to step-in, iterate over all style keys, and if it contains color , I want to check for the value - and replace with hex if it is one of the above custom colour. 我只想介入,迭代所有样式键,如果它包含color ,我想检查值 - 如果它是上述自定义颜色之一,则替换为十六进制。

I can't seem to be able to access any ngStyle internal functions, and the source code is confusing and short; 我似乎无法访问任何ngStyle内部函数, 源代码令人困惑和简短; it seems to just set element CSS - where does the $parse do its job? 它似乎只是设置元素CSS - $ parse在哪里完成它的工作? for example, when ng-style="{color: ctrl.textColor}" - there is nothing in ngStyle source code that is pulling the value of ctrl.textColour . 例如,当ng-style="{color: ctrl.textColor}" - ngStyle源代码中没有任何内容可以提取ctrl.textColour的值。 Am I looking at the wrong place? 我在找错了地方吗?

Anyway, how do I access ng-style key values so that I can change custom colours to its hex codes please? 无论如何,如何访问ng样式的键值,以便我可以将自定义颜色更改为其十六进制代码?

This is what I've got so far in my decorator: 这是我在装饰者中到目前为止所得到的:

$provide.decorator('ngStyleDirective', function($delegate) {

    let directive = $delegate[0];
    let link = directive.link;

    directive.compile = function(element, attrs) {

        // Expression here is a string property
        let expression = attrs.ngStyle;

        return function(scope, elem, attr) {

          // How do I iterate over and update style values here?

          // Run original function
          link.apply(this, arguments);

        }

      }

      return $delegate;

});

I tried using regex to pull out patterns etc. and inspect elements, but, it seems like the wrong way to approach the problem to me as I then have to manually update string and pass it on to base link function. 我尝试使用正则表达式来提取模式等并检查元素,但是,似乎错误的方法来解决问题,因为我必须手动更新字符串并将其传递给基本链接功能。

Here's a plnkr example . 这是一个plnkr的例子

IF there is a better way to do what I'm trying to do, please let me know. 如果有更好的方法来做我想做的事,请告诉我。

Anyway, how do I access ng-style key values so that I can change custom colours to its hex codes please? 无论如何,如何访问ng样式的键值,以便我可以将自定义颜色更改为其十六进制代码?

The ngStyle property can be re-written within the compile function: 可以在compile函数中重写ngStyle属性:

directive.compile = function(element, attrs) {
  let expression = getExpressions(attrs.ngStyle);
  attrs.ngStyle = expression;

  return function(scope, elem, attr) {
    // Run original function
    link.apply(this, arguments);  
  }
}

JSON.parse() JSON.parse()来

JSON.parse() can be used if the HTML is updated so that the keys are surrounded by double quotes, which means the ng-style attribute needs to be delimited with single-quotes (though if one really wanted, one could try to escape the double quotes...) 如果更新HTML以便键被双引号括起来,则可以使用JSON.parse() ,这意味着ng-style属性需要用单引号分隔(尽管如果真的想要,可以试图逃脱双引号......)

<p ng-style='{ "color": "#e59c09" }'>Hello {{name}}!</p>
<p ng-style='{ "padding": "20px 10px", "background-color": "#1476a0", "color": "#ddd" }'>It is dark here</p>

Then parsing that string should yield a valid object, and Object.keys() can be used to iterate over the keys, checking for the word color . 然后解析该字符串应该产生一个有效的对象,并且可以使用Object.keys()迭代键,检查单词颜色 If the key contains color , Array.indexOf can be used to check if the value exists in the colors array. 如果键包含颜色 ,则Array.indexOf可用于检查colors数组中是否存在该值。 If it does exist in the array, then String.replace() can be used to substitute the value for the variable (ie the key in colours ). 如果它确实存在于数组中,则String.replace()可用于替换变量的值(即颜色中的键)。

function getExpressions(str) {
    var parsed = JSON.parse(str);
    Object.keys(parsed).forEach(function(key) {
        if (key.indexOf('color') > -1) {
            if (Object.keys(colours).indexOf(parsed[key]) > -1) {
                str = str.replace(parsed[key], colours[parsed[key]])
            }
         }
    });
    return str;
}

See it demonstrated in the example below. 请参阅下面的示例中演示的内容。 By the way, I had to remove the unused variable colours declared within the scope of the function getExpressions() , as it was hiding access to the variable defined above on line 3. Here is an updated plunker . 顺便说一句,我不得不删除函数getExpressions()范围内声明的未使用的变量颜色 ,因为它隐藏了对第3行上面定义的变量的访问权限。 这是一个更新的plunker

 let app = angular.module('plunker', []); let colours = { mango: '#e59c09', midnight: '#1476a0' }; app.controller('MainCtrl', function($scope) { $scope.name = 'World'; }); app.config(function($provide) { // Extract colour values from the string function getExpressions(str) { var parsed = JSON.parse(str); Object.keys(parsed).forEach(function(key) { if (key.indexOf('color') > -1) { if (Object.keys(colours).indexOf(parsed[key]) > -1) { str = str.replace(parsed[key], colours[parsed[key]]) } } }); return str; } $provide.decorator('ngStyleDirective', function($delegate) { let directive = $delegate[0]; let link = directive.link; directive.compile = function(element, attrs) { let expression = getExpressions(attrs.ngStyle); attrs.ngStyle = expression; return function(scope, elem, attr) { // Run original function link.apply(this, arguments); } } return $delegate; }); }); 
 div + div { margin-top: 60px; } .comment { font-family: courier; font-size: 12px; margin: 15px 0; } 
 <script src="https://code.angularjs.org/1.4.12/angular.js"></script> <div ng-app="plunker" ng-controller="MainCtrl"> <div> <p class="comment">--- with hex --</p> <p ng-style='{ "color": "#e59c09" }'>Hello {{name}}!</p> <p ng-style='{ "padding": "20px 10px", "background-color": "#1476a0", "color": "#ddd" }'>It is dark here</p> </div> <div> <p class="comment">--- with custom colours --</p> <p ng-style='{ "color": "mango" }'>Hello {{name}}!</p> <p ng-style='{ "padding": "20px 10px", "background-color": "midnight", "color": "#ddd" }'>It is dark here</p> </div> </div> 

Actually, if you want to use parse - and you should - you can use it to parse the expression, replace attributes, and then transform the attribute back to json. 实际上,如果你想使用parse - 你应该 - 你可以用它来解析表达式,替换属性,然后将属性转换回json。

You should use $parse because if your code looks like 您应该使用$ parse,因为如果您的代码看起来像

// in the HTML
<p ng-style="{ padding: '20px 10px', 'background-color': myController.color, color: '#ddd' }">It is dark here</p>
// in the JS
myController.color = 'midnight';

Then parsing JSON will not work. 然后解析JSON将无法正常工作。 You should parse the expression using $parse and call the resulting function with your directive's scope object. 您应该使用$ parse解析表达式,并使用指令的作用域对象调用结果函数。

That's why your provider should look like this : 这就是为什么您的提供商应该是这样的:

$provide.decorator('ngStyleDirective', function($delegate, $parse) {
  let directive = $delegate[0];
  let link = directive.link;

  directive.compile = function(element, attrs) {
    return function(scope, elem, attrs) {
      let ngStyleObject = $parse(attrs.ngStyle)(scope, {});

      Object.keys(ngStyleObject).forEach(function(key) {
        if (key.indexOf('color') > -1 && Object.keys(colours).indexOf(ngStyleObject[key]) > -1) {
          ngStyleObject[key] = colours[ngStyleObject[key]];
        }
      });

      attrs.ngStyle = JSON.stringify(ngStyleObject); 
      // Run original function
      link.apply(this, arguments); 
    }
  }
  return $delegate;
});

You could also copy the original ngStyle function (instead of calling its link function) as it's pretty simple to add the behavior you expected: 您还可以复制原始的ngStyle函数(而不是调用其链接函数),因为添加您期望的行为非常简单:

$provide.decorator('ngStyleDirective', function($delegate) {
  let directive = $delegate[0];

  directive.compile = function(element, attrs) {
    return function(scope, elem, attrs) {
      // here, watch will do the $parse(attrs.ngStyle)(scope) and call the callback when values change
      scope.$watch(attrs.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
        if (oldStyles && (newStyles !== oldStyles)) {
          oldStyles.forEach(function(val, style) {  element.css(style, ''); });
        }
        if (newStyles) {
          // instead of just setting the new styles, replace colors with their values
          Object.keys(newStyles).forEach(function(key) { 
            if (key.indexOf('color') > -1 && Object.keys(colours).indexOf(newStyles[key]) > -1) {
              newStyles[key] = colours[newStyles[key]];
            }
          });
          element.css(newStyles);
        }
      }, true);

    }
  }
  return $delegate;
});

You can find the plunker (two versions) here 你可以在这里找到plunker(两个版本)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM