angularjs - ng-class strange behaviour after after $compile -
i have made following directive:
(function () { 'use strict'; angular .module('app.widgets') .directive('zzforminput', forminput); function forminput($compile) { // usage: // <div zz-forminput></div> function setupdom(element) { var input = element.queryselector("input, textarea, select"); var type = input.getattribute("type"); var name = input.getattribute("name"); if (type !== "checkbox" && type !== "radio") { input.classlist.add("form-control"); } var label = element.queryselector("label"); label.classlist.add("control-label"); element.classlist.add("form-group"); return name; } function addngclass(form, element, name, $compile, scope) { var isexistingngclass = element[0].attributes["data-ng-class"] || element[0].attributes["ng-class"]; if (!isexistingngclass) { var ngclass = "{'has-error':" + form.$name + "." + name + ".$invalid && " + "(" + form.$name + "." + name + ".$dirty || vm.submit), " + "'has-success':" + form.$name + "." + name + ".$valid && " + form.$name + "." + name + ".$dirty}"; element.attr("data-ng-class", ngclass); $compile(element)(scope); } } function link($compile) { return function (scope, element, attrs, form) { var name = setupdom(element[0]); addngclass(form, element, name, $compile, scope); } } return { restrict: 'a', require: '^form', link: link($compile) } } }());
i using directive as:
<div zz-forminput> <label for="firstname" class="col-md-4">first name*</label> <div class="col-md-8"> <input type="text" name="firstname" id="firstname" data-ng-model="vm.userdetails.firstname" required data-ng-maxlength="100"> </div> </div>
angular compiles markup successfully. when enter text in input field, has-success not added div. when clear textbox, has-success class appied div. now, when enter text in input, has-success applied div.
please provide me solution issue
the reason seeing weird behavior because of how handle contents of element hosts directive.
what happens contents, including ng-model
directive on input, compiled twice: once when angular goes on dom (in compile phase), , once when manually invoke $compile
service (in linking phase of directive). causes ng-model
directive register twice - under same name - parent form controller, , long story short, causes weirdness.
the proper way deal contents using transclude
function, provided link
function of directive.
transclude: true, link: function(scope, element, attrs, ctrls, transclude){ transclude(scope, function(clone){ element.append(clone); // clone clone of contents, prebound scope } }
or, simply, <div ng-transclude></div>
via template, since don't need special there
transclude: true, template: '<div ng-transclude></div>`
but don't need of this, since directive puts classes on contents , applies classes itself, trying ng-class
, requires use $compile
. instead of doing this, $watch changes , apply class directly:
link: function(scope, element, attrs, formctrl){ var inputname = setupdom(element[0]); scope.$watch(function(){ return formctrl[inputname].$valid && formctrl[inputname].$dirty; }, function(v){ if (v) element.addclass("has-success"); else element.removeclass("has-success"); }); scope.$watch(function(){ return formctrl[inputname].$invalid && (formctrl[inputname].$dirty || formctrl.$submitted); }, function(v){ if (v) element.addclass("has-error"); else element.removeclass("has-error"); }) }
and there no need $compile
or transclude
Comments
Post a Comment