link not called on angular directive - angularjs

The following directive does not generate any exceptions & it does get called, but link never executes. Can you point me in the right direction?
(function() {
var noduplicateModule,
__hasProp = {}.hasOwnProperty;
noduplicateModule = angular.module('noduplicate', []);
noduplicateModule.directive('noduplicate', function() {
return {
restrict: 'A',
scope: {
ngModel: '=',
bindAttr: '='
},
require: 'ngModel',
link: function (scope, elem, attr, ctrl) {
var options;
console.log("test");
//options = getOptions(scope);
console.log("test");
//ngModelCtrl.$validators.noduplicate = function (viewVal) {
//
// console.log("test");
// return true;
//};
elem.on('blur', function () {
console.log("test");
});
elem.on('focus', function () {
console.log("test");
});
}
};
});
}).call(this);
UPDATE - HTML
<select name="test" id="test" data-role="none" class="form-control" ng-model="testfield" required noduplicate="NODUPE"/>

Related

Angular directive calling another directive not working

In this plunk I have directive dir1 calling a method in directive dir2 as described here.
The problem is that the control object (scope.dir2Ctl) is empty in dir1 and I get TypeError: scope.dir2Ctl.call2 is not a function. Any ideas how to fix this?
HTML
<body ng-app="myModule" ng-controller="ctl">
<dir1 x1="1"></dir1>
</body>
Javascript
angular.module("myModule", [])
.controller('ctl', function($scope) {})
.directive('dir1', function ($timeout) {
return {
restrict: 'EA',
scope: {
x1: '='
},
template: '<p>x2 should be 2 = {{x2}} </p>' +
'<dir2 control="dir2Ctl"></dir2>',
link: function (scope, element, attrs) {
scope.dir2Ctl = {};
$timeout(function(){
console.log(scope.dir2Ctl)
scope.x2 = scope.dir2Ctl.call2();
},1000);
}
}
})
.directive('dir2', function () {
return {
restrict: 'EA',
scope: {
control: '='
},
template: '<p>some text in dir2</p>',
link: function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.call2 = function(){
return 2;
};
}
}
});

Controller as with directive not working

I want to accomplish scroll-able content by clicking on Bootstrap module. Its working fine. This is following code of my directive:
'use strict';
angular.module('cbookApp')
.directive('scrollTo', scrollTo);
scrollTo.$inject = ['$anchorScroll'];
function scrollTo($anchorScroll) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (event) {
event.stopPropagation();
var location = attrs.scrollTo;
if (scope.vm.isEdit || typeof scope.vm.isEdit =="undefined" ) {
$anchorScroll(location);
} else {
$anchorScroll(location+'1');
}
});
}
};
}
But only problem is i am not sure how to apply active class to current affix li. This DEMO way i found to apply class active to current li and remove from other. It was working without Controller as but once i added controller as it stopped working and give some error of scope.
var app = angular.module('app', ['directives']);
app.controller('firstController',[function(){
var vm = this;
vm.model = { value: 'dsf'};
}]);
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: true,
scope: {
model: '='
},
transclude: true,
link: function (scope, element, attrs) {
scope.localFunction = function () {
scope.model.value = scope.$id;
};
scope.$watch('model.value', function () {
if (scope.model.value === scope.$id) {
scope.selected = "active";
} else {
scope.selected = '';
}
});
}
};
return directiveDefinitionObject;
});
Can you please add this in your directive.
element.parent().parent().children().each(function() {
$(this).find('a').removeClass('active');
});
element.addClass('active');
http://jsfiddle.net/hngzxmda/1/
I suggest using controllerAs in your directive too
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="vmd.localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: {
model: '=',
$id: '='
},
scope: {},
transclude: true,
controller: function() {
var _this = this;
this.localFunction = function () {
_this.model.value = _this.$id;
};
},
controllerAs: 'vmd'
};
return directiveDefinitionObject;
});

Angularjs controller required by directive can not be found in transclude content

Recently I use angular to develop a directive, there is an directive which like ng-repeat to generate some records, I used transclude to implement it. but it raise an error that "Controller 'aArea', required by directive 'bSpan', can't be found!".
1. ModuleA code
var moduleA = angular.module("moduleA", []);
moduleA.directive("aArea", function () {
return {
restrict: 'E',
transclude:'element',
scope: {
amount:"="
},
template: '<div id=\"cc\" ng-transclude></div>',
controller: function ($scope,$element,$attrs) {
this.getData = function (data) {
return data + " is ok";
}
},
compile: function (tElement, attrs, linker) {
var parentElement = tElement.parent();
return {
pre: function () {
},
post: function (scope) {
linker(scope.$parent,function (clone,scope) {
parentElement.append(clone);
});
linker(scope.$parent, function (clone, scope) {
parentElement.append(clone);
});
linker(scope.$parent, function (clone, scope) {
parentElement.append(clone);
});
}
}
}
}
});
moduleA.directive("bSpan", function () {
return {
restrict: 'E',
scope: {
data: "=",
},
template: '<span style=\"background-color:gray;color:orange\">{{data}}</span>',
require: "^aArea",
link: function ($scope, $element, $attrs, controller) {
var data = "abc";
}
}
});
2. ModuleB COde
var moduleB = angular.module("moduleB", []);
moduleB.directive("myItem", function () {
return {
restrict: 'E',
scope: {
item: "=",
itemTemplate: '='
},
priority: 1000,
terminal:false,
template: '<ng-include src=\"itemTemplate\"/>',
controller: function ($scope, $element, $attrs) {
var data = "";
}
}
})
3. ModuleC Code
var moduleC = angular.module("moduleC", ["moduleA", "moduleB"]);
moduleC.controller("Ctr", function ($scope) {
$scope.item = {};
$scope.item.dataAmount = 1000;
$scope.item.templateUrl = "item-template.html";
})
4. Html Code
<body>
<div ng-app="moduleC">
<div ng-controller="Ctr">
<a-area>
<my-item item="item" item-template="item.templateUrl"></my-item>
</a-area>
</div>
</div>
</body>
5. template code
<div>
<span style="display:block">hello every one</span>
<b-span data="item.dataAmount"></b-span>
</div>
You should not use the transclude function (that you called linker) of the compile function - it is deprecated.
From $compile documentation:
Note: The transclude function that is passed to the compile function is deprecated, as it e.g. does not know about the right outer scope. Please use the transclude function that is passed to the link function instead.
Following this guidance (and a few other minor changes for the better), change the aArea directive as follows:
compile: function(tElement, tAttrs) {
// don't use the template element
//var parentElement = tElement.parent();
return function(scope, element, attrs, ctrls, transclude) {
transclude(function(clone, scope) {
element.after(clone);
});
transclude(function(clone, scope) {
element.after(clone);
});
transclude(function(clone, scope) {
element.after(clone);
});
};
}
In fact, you don't even need the transclude function at all and you don't need to do transclude: "element". You could just change to transclude: true and use <div ng-transclude> 3 times in the template.

Dynamically add ngModels to child elements in angular directive

Have some directive to uncheck previously checked radio in group:
(function (angular, $) {
'use strict';
var radioGroupDirective = function () {
return {
restrict: 'EA',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModelController) {
var $radios = $element.find('input[type="radio"]');
$radios.click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
ngModelController.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
};
};
radioGroupDirective.$inject = [];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);
})(angular, $);
Usage:
<div radio-group ng-model="fruit">
<input type="radio" ng-model="fruit" value="Apple"/>
<input type="radio" ng-model="fruit" value="Banana"/>
<input type="radio" ng-model="fruit" value="Mango"/>
</div>
It works fine, but I want to remove duplicate code of ngModels in child inputs. Like this:
<div radio-group ng-model="fruit">
<input type="radio" value="Apple"/>
<input type="radio" value="Banana"/>
<input type="radio" value="Mango"/>
</div>
So I try to add ngModel to all child inputs dynamically at compile function
(function (angular, $) {
'use strict';
var radioGroupDirective = function ($compile) {
return {
restrict: 'EA',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModelController) {
var $radios = $element.find('input[type="radio"]');
$radios.click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
ngModelController.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
compile: function (tElement, tAttrs) {
var $radios = tElement.find('input[type="radio"]');
angular.forEach($radios, function(radio) {
$(radio).attr('ng-model', tAttrs.ngModel);
});
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
$compile(iElement)(scope);
},
};
},
};
};
radioGroupDirective.$inject = ['$compile'];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);
})(angular, $);
but it causes an infinite compilation loop and a dead of the browser
You try to compile the entire the directive (radioGroup) again from the link function so it causes an infinite loop.
Instead compile only the inputs:
angular.forEach($radios, function(radio) {
$compile(radio)(scope);
});
See this plunker.
Full worked plunker for this directive (for someone who could find it usefull)
var radioGroupDirective = function ($compile) {
return {
restrict: 'EA',
require: 'ngModel',
compile: function (tElement, tAttrs) {
var $radios = tElement.find('input');
angular.forEach($radios, function(radio) {
$(radio).attr('ng-model', tAttrs.ngModel);
});
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
angular.forEach($radios, function(radio) {
$compile(radio)(scope);
});
$($radios).click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
controller.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
};
},
};
};
radioGroupDirective.$inject = ['$compile'];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);

Async function in $parsers doesn't update $modelValue

Was wondering how I should handle async functions in $parsers.
The below code doesn't update the scope.
I'm using AngularJS 1.2 so can't make use of the new and fancy 1.3 features.
http://plnkr.co/edit/uk9VMipYNphzk8l7p9iZ?p=preview
Markup:
<input type="text" name="test" ng-model="test" parse>
Directive:
app.directive('parse', function($timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
$timeout(function() {
return viewValue;
});
});
}
};
});
If you are looking for async validation function, I did something like that some time ago and release it as a library. Check the custom-remote-validator directive here.
The basic idea was use ngModelController $setValidity after receiving validation result from server. This is the directive source code
.directive('customRemoteValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attr, ngModelCtrl) {
var validateFunctionNames = attr["remoteValidateFunctions"].split(",");
var validatorNames = attr["customRemoteValidator"].split(",");
ngModelCtrl.$parsers.push(function (value) {
angular.forEach(validateFunctionNames, function (functionName, index) {
if (!scope[functionName]) {
console.log('There is no function with ' + functionName + ' available on the scope. Please make sure the function exists on current scope or its parent.');
} else {
var result = scope[functionName](value);
if (result.then) {
result.then(function (data) { //For promise type result object
ngModelCtrl.$setValidity(validatorNames[index], data);
}, function (error) {
ngModelCtrl.$setValidity(validatorNames[index], false);
});
}
}
});
return value;
});
}
};
}])

Resources