Why is ngModel not being loaded into my link function? - angularjs

In my directive, I'm setting ngModel as a dependency with 'require: '?ngModel'. But whenever the link function is called, ngModel is undefined.
Here's my full code:
myModule.directive('myDirective', function() {
return {
restrict: 'E',
require: '?ngModel',
link: function($scope, $element, $attr, ngModel) {
if (!ngModel) return;
console.log("This code never runs!");
}
}
})

You need to set the ng-model attribute for this directive in your HTML file.
Example:
<my-directive ng-model="foo"></my-directive>

Related

Unable to pass interpolated value during transclusion in directives

I am creating an email directive as below,
angular.module('app')
.directive('email', function () {
return {
restrict: 'EA',
transclude: true,
template: '{{email}}',
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope, function (clone) {
scope.email = clone.text();
});
}
};
});
I am using it in the HTML as below,
<email>{{emailId}}</email>
However, I am unable to pass the interpolated value of emailId inside the directive. The output of the directive ends up as,
{{emailId}}
How do we pass the actual interpolated value inside the directive?
Plnkr - http://plnkr.co/edit/AeBfp3VayKihygelKr9C?p=preview
To pass a variable to directive scope, simply use an attribute:
angular.module('app')
.directive('email', function () {
return {
restrict: 'EA',
̶t̶r̶a̶n̶s̶c̶l̶u̶d̶e̶:̶ ̶t̶r̶u̶e̶,̶
template: '{{email}}',
link: function (scope, element, attrs, ctrl, transclude) {
//transclude(scope, function (clone) {
// scope.email = clone.text();
//});
scope.email = scope.$eval(attrs.addr);
//OR
scope.$watch(attrs.addr, function(value) {
scope.email = value;
});
}
};
});
Usage:
<̶e̶m̶a̶i̶l̶>̶{̶{̶e̶m̶a̶i̶l̶I̶d̶}̶}̶<̶/̶e̶m̶a̶i̶l̶>̶
<email addr="emailId"></email>
There is no need to use transclusion to evaluate an Angular Expression.
For more information, see
AngularJS scope.$eval API Reference
AngularJS scope.$watch API Reference
You Just have to add Only on directive template ng-transclude on your element
here is the code
angular.module('app')
.directive('email', function () {
return {
restrict: 'EA',
transclude: true,
template: '<a href="{{email}}" ng-transclude>{{email}}</a>',
link: function (scope, element, attrs, ctrl, transclude) {
}
};
});
here is the link
Plunker http://next.plnkr.co/edit/SY3ih9NwAqz7ZhZg?open=lib%2Fscript.js&preview

Angular Directive would not update ng-model

I can't seem to find a way to update my ng-model in my directive
My directive looks like this
app.directive('DatepickerChanger', function () {
return {
restrict: 'E',
require: 'ngModel',
scope: {
Model: '=',
startDate: '=',
endDate: '=',
},
templateUrl: function(elem,attrs) {
return '/AngularJS/Directives/msDatepickerTpl.html'
},
link: function (scope, element, attrs, ngModel) {
ngModel = new Date();
}
}
});
And i use it like this
<DatepickerChanger ng-model="date" required></DatepickerChanger>
Where date is a other date object
If i console.log the ng-model, i can see that it's change it's value, but can't see it from the place where i call the directive. anyone got a clue to what im doing worng
You are using it wrong. This ngModel is a controller. Your code should look like this:
link: function (scope, element, attrs, ngModel) {
ngModel.$setViewValue(new Date());
}
Also, the directive should be called datepickerChanger and referenced in html as datepicker-changer
EDIT:
You want to update the ngModel after changing the input, for example. One way to do that is use the bind function to capture the events of the input field (I cant see your template, but I imagine that will have one input):
link: function (scope, element, attrs, ngModel) {
element.find('input').bind('change', function() {
// Do something with this.value
ngModel.$setViewValue(newDate);
});
}

ngModelController $modelValue is empty on directive startup

I have an attribute directive that I use on an input=text tag like this:
<input type="text" ng-model="helo" my-directive />
On my directive I'm trying to use the ngModelController to save the initial value of my input, in this case the value of the ng-model associated with it.
The directive is like this:
app.directive('myDirective', function () {
return {
restrict: "A",
scope: {
},
require: "ngModel",
link: function (scope, elm, attr, ngModel) {
console.log("hi");
console.log(ngModel.$modelValue);
console.log(ngModel.$viewValue);
console.log(elm.val());
}
}
});
The problem is that ngModel.$modelValue is empty maybe because at the time the directive is initialized the ngModel wasn't yet updated with the correct value. So, how can I store on my directive the first value that is set on my input field?
How to correctly access ngModel.$modelValue so that it has the correct value?
I'll also appreciate an explanation on why this isn't working as I'm not clearly understanding this from reading the docs.
Plunkr full example: http://plnkr.co/edit/QgRieF
Use $watch in myDirective
app.directive('myDirective', function () {
return {
restrict: "A",
scope: {
},
require: "ngModel",
link: function (scope, elm, attr, ngModel) {
var unwatch = scope.$watch(function(){
return ngModel.$viewValue;
}, function(value){
if(value){
console.log("hi");
console.log(ngModel.$modelValue);
console.log(ngModel.$viewValue);
console.log(elm.val());
unwatch();
}
});
}
}
});
For Demo See This Link

How to add an AngularJS directive to the element object during a directive's link function?

I built two custom directives, datepicker and date-validation, and I'm having trouble with getting datepicker to include the date-validation directive. At first I had the datepicker and date validation directives as...
controls.directive('dateValidation', ['$filter', function ($filter) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ngModel) {
// validation methods here...
}
};
}]);
controls.directive('myDatepicker', [function () {
return {
restrict: 'A',
require: 'ngModel',
replace: true,
template: '<input type="text" date-validation />',
link: function (scope, element, attrs, ngModel) {
// inject date picker here...
}}
};
}]);
and this worked perfectly until I tried it in IE8. In IE8 it has a problem with the way the directive replaces the current element with the template and throws an error.
I then tried making replace equal false and took away the template. Then adding the "date-validation" attribute with jQuery and re-compiling the element using the $compile method like...
controls.directive('myDatepicker', ['$compile', function ($compile) {
return {
restrict: 'A',
require: 'ngModel',
replace: false,
link: function (scope, element, attrs, ngModel) {
element.attr({ 'date-validation': '' });
$compile(element)(scope);
// inject date picker here...
}}
};
}]);
But this didn't work either.
Also, the HTML that uses the directive looks like this...
<input type="text" ng-model="startDate" my-datepicker />
If anyone can advise me on how to include another directive inside a custom directive I would greatly appreciate it.

AngularJS How to access elements inside directive before they get replaced

How do I get the input element from within the directive before the template overwrites the contents?
html
<div xxx>
<input a="1" />
</div>
js
app.directive('xxx', function(){
return {
restrict: 'A',
template: '<p></p>',
replace: true, //if false, just leaves the parent div, still no input
compile: function(element, attrs) {
console.log(element);
return function (scope, iElement, iAttrs) {
}
}
};
});
i am on angular 1.0.x, I cannot pass in optional scope parameters with the '=?' syntax and i want to be able to override a portion of the default template of the directive in a very flexible way. instead of adding a scope variable or attribute everytime that I just plan on passing through the directive, I want to be able to supply the whole element to be used.
edit
the input must retain the scope of the directive, and not the parent.
edit
I am trying to include a partial template inside a directive that will overwrite a piece of the actual template. The piece I am including therefore needs to have access to the directive's scope and not the parent's.
Update
It seems if I do not provide a template or a template URL and instead replace the contents manually using the $templateCache I can have access to the inner elements. I want to let angular handle the template and the replacement though and just want to be able to access the contents in the directive naturally before they get replaced.
Solution
Plunkr
html
<body ng-controller="MainCtrl">
<div editable="obj.email">
<input validate-email="error message" ng-model="obj.email" name="contactEmail" type="text" />
</div>
</body>
js
app.controller('MainCtrl', function($scope) {
$scope.obj = {
email: 'xxx'
};
});
app.directive('editable', function($log){
return {
restrict: 'A',
transclude: true,
template: '<div ng-show="localScopeVar">{{value}}<div ng-transclude></div></div>',
scope: {
value: '=editable'
},
link: function(scope) {
scope.localScopeVar = true;
}
};
});
app.directive('validateEmail', function($log){
return {
restrict: 'A',
require: 'ngModel',
scope: true,
link: function(scope, el, attrs, ctrl) {
console.log(attrs['validateEmail']);
}
};
});
I believe you're looking for the transclude function (link is to 1.0.8 docs). You can see what's going on with:
app.directive('xxx', function($log){
return {
restrict: 'A',
transclude: true,
compile: function(element, attrs, transclude) {
$log.info("every instance element:", element);
return function (scope, iElement, iAttrs) {
$log.info("this instance element:", element);
transclude(scope, function(clone){
$log.info("clone:", clone);
});
}
}
};
});

Resources