【问题标题】:How do I access the ngStyle key and values in decorator?如何访问装饰器中的 ngStyle 键和值?
【发布时间】:2017-11-30 17:51:47
【问题描述】:

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

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;

});

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

这是plnkr example

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

【问题讨论】:

  • 注意:这个问题已经得到解答,我想为下面的回答者奖励一个写得很好且易于理解的答案。

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


【解决方案1】:

无论如何,我如何访问 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()

JSON.parse() 可以在 HTML 更新时使用,以便键被双引号括起来,这意味着 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。如果键包含colorArray.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() 范围内声明的未使用变量 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>

【讨论】:

  • 嗨,谢谢您的出色回答。我有最后一个问题。我的ng-style 与控制器变量一起使用,如下所示:ng-style="{ color: ctrl.textColour }"textColour 所以在我的getExpression 函数中,str 就是字面意思。有没有办法强制评估表达式,从中取出字符串mango,然后应用我的自定义颜色十六进制值?
  • Hmm... 到目前为止,我有 this example - myColour 在控制器和提供程序之外定义,因此它可以在两个地方使用,但我认为您希望随时更新该样式绑定的控制器模型值发生变化......我猜需要一个观察者来处理它......
  • 嗯,我并不总是知道变量名是什么,它可能是 ctrl.item.colourctrl.borderColour... 这就是为什么我想知道是否可以 $eval表达式并获取实际项目值而不是替换。这可能吗?
  • 嗨,我刚刚意识到我可以从链接函数中调用scope.$eval,它会评估表达式并给我所需的值而不是变量 - 这回答了我的上述问题。非常感谢您写得非常好的答案,我会尽快接受它
  • Noooo - 我去度假了,想为此奖励赏金,但回来发现另一个答案已被授予自动赏金:(我该如何解决这个问题?
【解决方案2】:

实际上,如果你想使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2021-06-05
    • 2021-01-17
    • 1970-01-01
    • 2014-12-31
    • 2011-06-25
    • 1970-01-01
    相关资源
    最近更新 更多