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

时间:2017-06-27 15:30:38

标签: javascript angularjs angularjs-directive decorator ng-style

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

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

我想扩展 ngStyle 指令,以便能够理解我的自定义颜色名称。我是通过decorating ngStyle 指令来完成的。但是,我在装饰者的编译功能上遇到了一场艰苦的战斗。我可以访问元素' ngStyle 属性,但它是一个字符串(可以理解)。 JSON.parse()不起作用,因为它不一定是有效的JSON字符串,因为绑定一次等...

我只想介入,迭代所有样式键,如果它包含color,我想检查值 - 如果它是上述自定义颜色之一,则替换为十六进制。< / p>

我似乎无法访问任何 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;

});

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

这是一个plnkr example

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

2 个答案:

答案 0 :(得分:4)

  

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

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()来

如果更新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()可以用来迭代键,检查单词 color 。如果密钥包含颜色,则Array.indexOf可用于检查 colors 数组中是否存在该值。如果它确实存在于数组中,则String.replace()可用于替换变量的值(即 colors 中的键)。

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()范围内声明的未使用的变量 colors ,因为它隐藏了对第3行上面定义的变量的访问权限。Here is an updated plunker

&#13;
&#13;
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;
  });
});
&#13;
div + div {
  margin-top: 60px;
}

.comment { 
  font-family: courier;
  font-size: 12px;
  margin: 15px 0;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:3)

实际上,如果你想使用parse - 你应该 - 你可以用它来解析表达式,替换属性,然后将属性转换回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函数(而不是调用其链接函数),因为添加您期望的行为非常简单:

$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