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);
});
}
Related
I am trying to add an input directive in order to trim all text inputs. So far this is the code of my directive:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "ngModel",
link: function link(scope, element, attrs, ctrl) {
element.on("focusout", function triggerChange(event) {
var input = event.target;
if (input.value && input.type === "text") {
ctrl.$setViewValue(input.value.trim());
ctrl.$render();
}
});
}
};
});
My issue is that the ngModel does not seem to be injected, as I get the error:
Error: [$compile:ctreq] Controller 'ngModel', required by directive 'input', can't be found!
Any idea why this happens, and how to fix it?
Update:
Actually, this is the interaction of Kendo Grid and AngularJS. The input I am testing is generated by Kendo Grid. The code of the column is standard:
{ field: "name", title: "titleName" }
You must have some input element in your HTML which does not have ng-model.
You can change your code to require: "?ngModel", and later check if ctrl is undefined or not, like:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "?ngModel",
link: function link(scope, element, attrs, ctrl) {
if (!ctrl) { return ;}
element.on("focusout", function triggerChange(event) {
var input = event.target;
if (input.value && input.type === "text") {
ctrl.$setViewValue(input.value.trim());
ctrl.$render();
}
});
}
};
You should have provided ng-model in your html when you use this directive because you wrote require: 'ngModel', in the directive. so in your case your directive name is input so it will be something like
<input ng-model="something"> </input>
My answer is not perfect, but it is the best I could find:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "ngModel",
link: function link(scope, element, attrs, ctrl) {
element.on("focusout", function triggerChange(event) {
var input = $(event.target);
input.val(input.val().trim());
input.trigger("change");
});
}
};
});
So basically, we trim the input, and use input.trigger("change") to inform the system that the input has changed.
A warning though, it does not work with our validation system (valdr).
Say I have the following directive:
myApp.directive('myDirective', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
scope.$watch('ngModel', function() {
ngModelCtrl.$modelValue = 'foo';
});
}
}
});
And the following html:
<input ng-model="name" my-directive></input>
Basically, whenever the user changes the input, my-directive would ideally change the internal model value to "foo" while leaving the view value untouched.
But when I print out $scope.name in the corresponding controller, it doesn't log "foo", it logs whatever the user entered in.
It would seem that ngModelCtrl.$modelValue is not what the controller is accessing -- am I approaching this problem incorrectly?
(Also watching the ngModel in the scope feels really wrong, but I'm not sure of any other way. Any suggestions would be much appreciated!)
If you are looking for view change, you should never register a watch. ngModelController's $viewChangeListeners are specifically designed for this purpose and to avoid creating any additional watch on the ngModel. You can also remove 2 way binding set up on the ngModel.
I can think of this way.
.directive('myDirective', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
/*Register a viewchange listener*/
ngModelCtrl.$viewChangeListeners.push(function(){
/*Set model value differently based on the viewvalue entered*/
$parse(attrs.ngModel).assign(scope, ngModelCtrl.$viewValue.split(','));
});
}
}
});
Demo
While thinking about it the other way around (Credits #Cody) it becomes more concise and appropriate while using a $parser.
ngModelCtrl.$parsers.push(function(val) { return val.split(',') });
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
Why value of the ng-model is not updated with the expression. Before ng-model is defined value get updated
Value will be updated as soon as phase2 or phase3 changes
<input type="text" name="phase1" value="{{phase2 - phase3}}" ></input>
Value will not be updated
<input type="text" name="phase1" value="{{phase2 - phase3}}" ng-model="phase1"></input>
So I think of writing a directive which will evaluate the expression inside the directive and updated the output to model,
Here is html it will look like
<input type="text" name="phase1" ng-model="phase1" my-value="{{phase2 - phase3}}" my-model-value></input>
Directive:
myApp.directive('myModelValue', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '#myValue'
},
link: function (scope, element, attr, controller) {
scope.model = scope.value;
}
};
});
This directive evaluate only at load time, but I want to continuously update/watch as the dependent fields (phase2 & phase3) changes.
I can update value from controller but I want to do it from html. Please help me, it it possible or against the working of angular
Thanks guys I figure out what I wanted to do. Here is the my final simple but useful directive :)
app.directive('myModelValue', function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel'
},
link: function (scope, element, attr, controller) {
attr.$observe('myModelValue', function (finalValue) {
scope.model = finalValue;
});
}
};
});
Usage:
<input type="text" ng-model="phase1" my-model-value="{{phase2 - phase3}}"></input>
<input type="text" ng-model="phase1.name" my-model-value="{{valid angular expression}}"></input>
In order to continously watch the phase2/3 changes you can make use of $scope.$watch function.
Something like this will work for you:
link: function (scope, element, attr, controller) {
scope.$watchCollection('[phase1,phase2]', function() {
//whatever you want to do over here }
and in the scope pass phase1 and phase2 values as well
This will watch the value expression and update the same when value will change
myApp.directive('myModelValue', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '#myValue'
},
link: function (scope, element, attr, controller) {
scope.$watch('value',function(newValue){
console.log(newValue);
});
}
};
});
here value is a local scope so it will watch the expression
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.