angularjs : directive and ng-class initialization

2019-08-15 23:35发布

I have an ng-class initialization issue when I move the code which was in my controller, but related to the DOM, to a directive. To be short, here is some code:

view.html

<div ng-repeat="foo in foos" ng-click="rowClick($index)>
    <div ng-class="changeOpacity($index)>
        <span>{{foo.title}}</span>
        <span>{{foo.name}}</span>
        <span>{{foo.date}}</span>
        ...
        // some div with hidden content. Show when clicking on the div
    </div>
<div>

controller.js

$scope.rowClick = function (idx) {
    // unfold the clicked div to show content
};
$scope.changeOpacity = function (idx) {
    if (this is the div clicked) {
        return {dark: true};
    } else {
        return {light: true};
}

So when I have a list of div. When I clicked on a div, all the other divs get darker excepts this one.

Everything is working fine when $scope.rowClick and $scope.changeOpacity are in my controller.

When I move rowClick and changeOpcaity to a directive:

myBar.js

myApp.directive('myBar', function () {
    ...
    return {
        restrict:'A',
        link: function (scope, element) {
           element.bind('click', function () {
               ...
               same code from rowClick
               ...
               scope.changeOpacity = function () {
                   ...
                   same code from changeOpacity
               }
               scope.$apply();
            }
        }),
        changeOpacity: function () {
            ...
            return {light: true} // for all divs
        }
    }
});

Now my view.html is something like that:

<div ng-repeat="foo in foos" ng-click="rowClick($index) my-bar>
    <div ng-class="changeOpacity($index)>
        <span>{{foo.title}}</span>
        <span>{{foo.name}}</span>
        <span>{{foo.date}}</span>
        ...
        // some div with hidden content. Show when clicking on the div
    </div>
<div>

But now the divs are not initialized anymore with the ng-class. I have to click one time on each div to initialize it, with the link function which listen to the click.

How can I initialize the ng-class inside the directive ?

Thanx.

标签: angularjs
2条回答
ら.Afraid
2楼-- · 2019-08-16 00:12

ng-repeat creates a child scope for each item within the array. That means scope within a directive within ng-repeat will be the child scope. Thus you can set properties directly on that child scope and use ng-class, ng-show etc

Following is very basic example of directive to toggle state based on ng-click handler

HTML

<div ng-repeat="foo in foos" ng-click="rowClick($index)"  my-directive parent-array="foos">
    <div ng-class="{activeClass: foo.isActive}" class="item">
        <span>{{foo.name}}</span>
        <div ng-show="foo.isActive">Hidden content {{$index+1}}</div>
    </div>
</div>

Directive

 app.directive('myDirective',function(){
    return function(scope,elem,attrs){
      /* ng-repeat adds properties like `$first` , `$last` that can be helpful*/
      /* set default first element active*/
      if(scope.$first){
        scope.foo.isActive=true;
      }
      var parentArray=scope[attrs.parentArray];
      /* update active state across array*/
      scope.rowClick=function(idx){
        angular.forEach(parentArray,function(item, index){
          item.isActive = idx==index
        })
      }
    }
  })

DEMO

Here's another approach that stores currSelected item in the parent scope ( controller)

 app.directive('myDirective',function(){
    return function(scope,elem,attrs){

      /* set default first element active*/
      if(scope.$first){
        scope.foo.isActive=true;
        scope.$parent.currSelected=scope.foo
      }

      /* reset previous activeitem  and make item clicked the active one*/
      scope.rowClick=function(item){
        scope.$parent.currSelected.isActive=false
        scope.$parent.currSelected=item;
        item.isActive=true
      }
    }
  })

DEMO

查看更多
Rolldiameter
3楼-- · 2019-08-16 00:16

The problem is that you are putting the changeOpacity function in your scope only after a click. Do it in the link function instead like this:

link: function (scope, element) {
    scope.changeOpacity = function () {
        ...
        same code from changeOpacity
    }
}

making it a field of the "directive object" has no effect.

However, in my opinion it would be more elegant to use a model property to indicate if the element is active or not and change this property in an ng-click attribute. You can then refer to this property in the ng-class directive.

Something like this:

<div ng-repeat="foo in foos" ng-click="foo.active = true>
    <div ng-class="{light: active, dark: !active }>
        <span>{{foo.title}}</span>
        <span>{{foo.name}}</span>
        <span>{{foo.date}}</span>
        <div ng-show="foo.active">
            // some div with hidden content. Show when clicking on the div
        </div>
</div>

查看更多
登录 后发表回答