AngularJS - Directive's class name cannot brin

2019-08-18 02:51发布

问题:

I want to make a directive which take a class name conditionally. However, I found that my code can work only if I hardcode the class name into the class attribute. If I try to use it with any expression, its failed to work.

For e.g.

// HTML

// Doesn't work (cannot find class="editable" in the final output template)
<tit-txt ng-class="true ? 'editable' : ''" ng-model="mdEnt.phone"></tit-txt>

// Works! (I can find class="editable" in the final output template)
<tit-txt class="editable" ng-model="mdEnt.phone"></tit-txt>

//JS

.directive('titTxt', function () {
    return {
        restrict: 'E',
        scope: {
            ngModel: '=',
        },
        link: function (scope, element, attrs) {
            scope.editable = element.hasClass('editable') ? 'editable' : '';
        },
        template: '<input ng-class="editable" ng-model="ngModel" />',
    };
})

Anyone can explain to me that why is it happening? How can I use it with expression?


UPDATE 1

// HTML

// Doesn't work
<tit-txt ng-class="{'editable': true}" ng-model="mdEnt.phone"></tit-txt>

//JS

.directive('titTxt', function () {
    return {
        restrict: 'E',
        scope: {
            title: '@',
            fieldName: '@',
            ngModel: '=',
        },
        link: function (scope, element, attrs) {
            console.log(element.hasClass('editable'));
            scope.editable = element.hasClass('editable') ? 'editable' : '';
        },
        template: '<div><span>{{title}}: </span><input id="{{fieldName}}" ng-class="{editable: true}" name="{{fieldName}}" ng-model="ngModel" /></div>',
    };
})

Anyone can explain to me that why I get false in the console.log(element.hasClass('editable'));?

回答1:

With the class attribute, the element's class is set by JavaScript. With the ng-class directive, the class is set by the AngularJS framework. When there are more that one directive on an element, there is no guarantee of the order of execution of the code of the respective directives.

Avoid having AngularJS manipulate the DOM and having subsequent AngularJS manipulate the model based on the state of the DOM. With MVC frameworks the Model should be the single source of truth and the DOM should be directly determined by the Model.

<tit-txt inner-class="true ? 'editable' : ''" my-model="mdEnt.phone">
</tit-txt>
app.directive('titTxt', function () {
    return {
        restrict: 'E',
        scope: {
            title: '@',
            fieldName: '@',
            innerClass: '<',
            myModel: '=',
        },
        link: function (scope, element, attrs) {
            scope.$watch(attrs.innerClass, function(newValue) {
                console.log("inner-class=", newValue);
            });
        },
        template: `<div><span>{{title}}: </span>
                      <input id="{{fieldName}}" ng-class="innerClass"
                             name="{{fieldName}}" ng-model="myModel" />
                   </div>`,
    };
})

Notice how the directive uses one-way, '<', binding to compute the value of the inner-class attribute from an AngularJS Expression.

Also notice that I changed ng-model to my-model. The ng- prefix is reserved for core AngularJS directives. Use of ng-model should be specifically be avoided unless the custom directive properly integrates with the ngModelController.