Change ng-model through directive - angularjs

I'am trying to change ng-model of input inside directive by clicking on Edit anchor in directives template.
Function f() is suppose to access outer controller and bind the editableValue to name or company so i can change it through input.
Input does show persons values but it does not bind to it.
<p edit="person.name"></p>
<p edit="person.company"></p>
<input ng-model="editableValue">
main.controller('editsCtrl', function($scope){
$scope.setToEdit = function(val){
$scope.editableValue = val;
}
});
main.directive('edit', function(){
return{
template:'{{edit}}<a ng-click="f()"> Edit </a>',
restrict:'A',
scope:{
edit:"="
},
replace:false,
link: function(scope, element, attrs, ctrl) {
scope.f = function(){
scope.$parent.setToEdit(scope.edit);
}
}
}
})
Even if i do this, its not binded, just value is passed:
scope.$parent.editableValue = scope.$parent.person.name;
For newbie this becomes bit confusing, what am i missing?

Scope assignment is failing here:
scope.f = function(){
scope.$parent.setToEdit(scope.edit);
}
Because you limited scope here:
scope:{
edit:"="
},
Edit is the only scope parent will see because you have set two way databinding on it with '='

Related

AngularJS directive get inner element

I've got next directive:
(function() {
'use strict';
angular
.module('myApp')
.directive('inner', inner);
function inner () {
return {
restrict: 'A',
scope: false,
link: linkFunc
};
function linkFunc (scope, element, attrs) {
}
}
})();
And HTML:
<span inner>{{vm.number}}</span>
How can I access vm.number's value in linkFunc? I need to take value exactly from content of the span tag.
There are various ways you can do this but here are the 2 most common ways:
ngModel
You could use ng-model like so in your template:
<span inner ng-model="vm.number">{{vm.number}}</span>
In your directive you require the ngModel where you can pull its value:
.directive( 'inner', function(){
return {
require: 'ngModel',
link: function($scope, elem, attrs, ngModel){
var val = ngModel.$modelValue
}
}
})
declare isolate scope properties
<span inner="vm.number">{{vm.number}}</span>
.directive( 'inner', function(){
return {
scope: { inner:'=' } ,
link: function($scope, elem, attrs){
var val = $scope.inner
}
}
})
Some less common ways:
use $parse service to get the value
Using the template again:
<span inner="vm.number">{{vm.number}}</span>
Let's assume you're going to Firstly you'll need to inject the $parse service in your directive's definition. Then inside your link function do the following:
var val = $parse(attrs.inner)
inherited scope for read only
I don't recommend this, because depending on how you defined your directive's scope option, things might get out of sync:
isolate (aka isolated) scopes will not inherit that value and vm.number will probably throw an undefined reference error because vm is undefined in most cases.
inherited scope will inherit the initial value from the parent scope but could diverge during run-time.
no scope will be the only case where it will stay in sync since the directive's $scope reference is the same scope present in the expression {{vm.number}}
Again I stress this is probably not the best option here. I'd only recommend this if you are suffering performance issues from a large number of repeated elements or large number of bindings. More on the directive's scope options - https://spin.atomicobject.com/2015/10/14/angular-directive-scope/
Well, In Angular directive, Link function can do almost everything controller can.
To make it very simple, we use one of them most of the time.
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.number = 5;
}).directive('inner', function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
var number = scope.number;
console.log(number);
}
}
});
Inside html :
<div inner ng-model="number">{{number}}</div>
https://plnkr.co/edit/YbXYpNtu7S3wc0zuBw3u?p=preview
In order to take value from HTML, Angular provides ng-model directive which is works on two way data binding concepts.
There are other ways which is already explain by #jusopi :)
cheers!

Can I two-way bind input field in directive template?

Having a following template in templateUrl:
<input name="foo" ng-model="test">
directive:
app
.directive('bar', function() {
return {
link: function link(scope, element, attrs, ctrl) {
scope.$watch(scope.test, function(newVal) {
console.log(val);
});
},
restrict: 'E',
templateUrl: 'templates/foo.html'
};
});
can I two-way bind it in directive so I scope.$watch input variable?
I tried using ng-bind and ng-model, but I cannot access that variable in scope of my directive.
Edit
Added directive code.
Change:
scope.$watch(scope.test, ...
to
scope.$watch('test', ...
and it should work. The first argument to $watch is the (so called) watchExpression. It will be evaluated against the relevant scope. When using a string you can basically use everything you would also use in the views/templates.
Mind that this will break again if you start using isolated scopes.

Why does the ngModelCtrl.$valid not update?

I'm trying to create a directive that contains an inputfield with a ng-model and knows if the inputcontrol is valid. (I want to change a class on a label within the directive based on this state.) I want to use the ngModelController.$valid to check this, but it always returns true.
formcontroller.$valid or formcontroller.inputfieldname.$valid do work as exprected, but since im trying to build a reusable component using a formcontroller is not very handy because then i have to determine what field of the form corresponds with the current directive.
I dont understand why one works and one doesnt, because in de angular source it seems to be the same code that should manage these states: The ngModelController.$setValidity function.
I created a test directive that contains a numeric field with required and a min value. As you can see in the fiddle below, the model controller is only triggered during page load and after that never changes.
jsfiddle with example directive
Directive code:
angular.module('ui.directives', []).directive('textboxValid',
function() {
return {
restrict: 'E',
require: ['ngModel', '^form'],
scope: {
ngModel: '='
},
template: '<input type="number" required name="somefield" min="3" ng-model="ngModel" /> '+
'<br>Form controller $valid: {{formfieldvalid}} <br> ' +
'Model controller $valid: {{modelvalid}} <br>'+
'Form controller $valid: {{formvalid}} <br>',
link: function (scope, element, attrs, controllers) {
var ngModelCtrl = controllers[0];
var formCtrl = controllers[1];
function modelvalid(){
return ngModelCtrl.$valid;
}
function formvalid(){
return formCtrl.$valid;
}
scope.$watch(formvalid, function(newVal,oldVal)
{
scope.modelvalid = ngModelCtrl.$valid;
scope.formvalid = formCtrl.$valid;
scope.formfieldvalid = formCtrl.somefield.$valid;
});
scope.$watch(modelvalid, function(newVal,oldVal)
{
scope.modelvalid = ngModelCtrl.$valid;
scope.formvalid = formCtrl.$valid;
scope.formfieldvalid = formCtrl.somefield.$valid;
//This one only gets triggered on pageload
alert('modelvalid ' + newVal );
});
}
};
}
);
Can someone help me understand this behaviour?
I think because you're watching a function and the $watch is only execute when this function is called !!
Watch the model instead like that :
scope.$watch('ngModel', function(newVal,oldVal)
{
scope.modelvalid = ngModelCtrl.$valid;
scope.formvalid = formCtrl.$valid;
scope.formfieldvalid = formCtrl.somefield.$valid;
//This one triggered each time the model changes
alert('modelvalid ' + ngModelCtrl.$valid );
});
I figured it out..
The textboxValid directive has a ng-model directive, and so does the input that gets created by the directive template. However, these are two different directives, both with their own seperate controller.
So, i changed my solution to use an attribute directive like below. This works as expected.
.directive('attributetest',
function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
link: function (scope, element, attrs, ngModelCtrl) {
function modelvalid(){
return ngModelCtrl.$valid;
}
scope.$watch(modelvalid, function(newVal,oldVal){
console.log('scope.modelvalid = ' + ngModelCtrl.$valid );
});
}
};
});

Why isnt my ng-model updating?

I have a directive that I have created. There is a text box and I am trying to update the count of characters in the box. First of all here is the html where my directive is declared.
<div chat-container encounter="selectedEncounter" ng-model="count"></div>
Here is my directive.
angular.module('clinicalApp').directive('chatContainer', function() {
return {
scope: {
encounter: '=',
ngModel: '='
},
controller: 'EncounterCtrl',
templateUrl: 'views/chat.container.html',
link: function(scope, elem, attrs) {
var chatbox = elem.find('textarea');
chatbox.bind('keyup',function() {
scope.updateCount();
});
}
};
});
In my template is a {{count}} that I want to be updated.
In my controller I have a function that I is called in the bind that updates the count. Here is the function in my controller.
$scope.updateCount = function() {
$scope.count = 350;
};
When this function is hit in my controller it does not update the count in my directive. How do I make this work?
Even better, how do I make the directive update the count? I thought the 2-way binding would take care of this, but it isnt. Thanks for the help.
My problem was that I was trying to update the wrong attribute in the template. If you look above I was trying to update {{count}} and I needed to update {{ngModel}}.
You need to notify angular that you updated a value:
$scope.updateCount = function() {
$scope.$apply( function() {
$scope.count = 350;
});
};

undefined scope into $watch

I have a directive named dir with:
ng-model="job.start_date"
comparison-date="job.end_date
Into scope.$watch("comparisonDate... I want to access my ng-model value. The problem is that scope is undefined into watch's callback function. The Question is: How can I get the ng-value inside this function?
.directive("dir", function() {
return {
scope: {
comparisonDate: "=",
ngModel: "="
},
link: function (scope, element, attrs, ctrl) {
var foo = scope.ngModel;
scope.$watch("comparisonDate", function(value, oldValue) {
console.log(value); //comparisonDate showing value properly
console.log(scope.ngModel); //Undefined
console.log(foo) //shows value but it's not refreshing. It shows allways the initial value
})
}
};
})
the view...
<input dir type="text" ng-model="job.start_date" comparison-date="job.end_date"/>
During the linking phase of the directive, the value may not be available. You can use $observe to observe the value change.
attrs.$observe("comparisonDate", function(a) {
console.log(scope.ngModel);
})
ng-model is built-in directive that tells Angular to do two-way data binding. http://docs.angularjs.org/api/ng.directive:ngModel
It looks like you are using the value of properties of the same object job to do comparison. If you want to stick with ng-model, you can use NgModelController: http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
Then change the view to:
<input dir type="text" ng-model="job"/>
and change the directive to:
.directive("dir", function() {
return {
require: '?ngModel', // get a hold of NgModelController
link: function (scope, element, attrs, ngModel) {
// access the job object
ngModel.$formatters.push(function(job){
console.log(job.start_date);
console.log(job.end_date);
});
}
};
})
Or you can change the attribute name from ng-model to some words haven't reserved. For example change the view like:
<input dir type="text" comparison-start-date="job.start_date" comparison-end-date="job.end_date"/>
Try scope.$watch(attrs.comparisonDate, ...) and then use attrs.ngModel

Resources