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

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

我的应用程序中有一个颜色名称列表。

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

我想扩展 ngStyle 指令以便能够理解我的自定义颜色名称。我通过 decorating ngStyle 指令执行此操作。然而,我在装饰器的编译函数上遇到了一场艰苦的战斗。我可以访问元素的 ngStyle 属性,但它以字符串形式出现(可以理解)。 JSON.parse() 对它不起作用,因为它并不总是有效的 JSON 字符串,因为绑定一次等...

我只是想介入,遍历所有样式键,如果它包含 color,我想检查值 - 如果它是上述自定义颜色之一,则用十六进制替换.

我似乎无法访问任何 ngStyle 内部函数,而且 source code 令人困惑且简短;它似乎只是设置元素 CSS - $parse 在哪里完成它的工作?例如,当 ng-style="{color: ctrl.textColor}" - ngStyle 源代码中没有任何东西正在提取 ctrl.textColour 的值。我是不是看错地方了?

无论如何,我如何访问 ng-style 键值,以便我可以将自定义颜色更改为其十六进制代码?

这是我目前在装饰器中得到的:

$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;

});

我尝试使用正则表达式提取模式等并检查元素,但是,这似乎是解决问题的错误方法,因为我必须手动更新字符串并将其传递给基础 link函数。

这是一个plnkr example

如果有更好的方法来完成我想做的事情,请告诉我。

Anyway, how do I access ng-style key values so that I can change custom colours to its hex codes please?

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() 如果更新 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>

然后解析该字符串应该会产生一个有效的对象,并且 Object.keys() can be used to iterate over the keys, checking for the word color. If the key contains color, Array.indexOf can be used to check if the value exists in the colors array. If it does exist in the array, then String.replace() 可用于替换变量的值(即 colours 中的键)。

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

参见下面的示例。顺便说一句,我不得不删除函数 getExpressions() 范围内声明的未使用变量 colours,因为它隐藏了对上面第 3 行定义的变量。Here is an updated 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>

实际上,如果你想使用解析 - 你应该 - 你可以使用它来解析表达式,替换属性,然后将属性转换回 json。

你应该使用 $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';

那么解析JSON就不行了。您应该使用 $parse 解析表达式并使用指令的范围对象调用结果函数。

这就是为什么您的提供者应该如下所示:

$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;
});

您也可以复制原始的 ngStyle 函数(而不是调用其 link 函数),因为添加您期望的行为非常简单:

$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