undefined scope into $watch - angularjs

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

Related

$formatter leaves $modelValue empty

I created a stringToDate directive. The element is wrapped in an ng-repeat directive with a filter. As the filter changes, the element with the directive appears and disappears. When debugging the code, the ngModel contains a $viewValue of NaN, and the $modelValue is undefined. Thus after flipping the filter a few times on the ng-repeat the value is empty. Why is the $parser not called? Am I not handling this correctly? The Date function is from DateJS.
.directive('stringToDate', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$parsers.push(function(value) {
return '' + value;
});
ngModel.$formatters.push(function(value) {
var a = ngModel;
if(Boolean(value))
return Date.parse(value).toString('MM/dd/yyyy');
});
}
};
})
I'm not sure this is the proper solution, but I added the following to the directive to clean up the parsers. I also call parseAndValidate to return the $viewValue to the $modelValue.
element.on('$destroy', function(){
ngModel.$$parseAndValidate();
ngModel.$formatters.pop();
ngModel.$parsers.pop();
});

AngularJS Two Way Bind Property in Directive

I have a custom directive:
.directive('test', function () {
return {
scope: {},
link: function (scope, element, attr) {
scope.$parent.$watch(attr.selectedItem, function(newValue, oldValue){
scope.selectedItem = newValue;
});
}
}
This will one way bind my directive's scope's selectedItem property to the value set in the attribute as such
<div test selectedItem="thePropertyOnTheController"></div>
But what if I want to two way bind? Is there an easy way to set this up without $watch'ing the directive's scope's selectedItem property and $parse'ing the attr.selectedItem expression and calling assign witht he parsed expression on scope.$parent?
$scope.thePropertyOnTheController might have some value like "Hello"
HTML
<div ng-repeat="photosets in userPhotoSetList">
<photosets photosetsarray="photosets.photosetDetail">
</div>
script :
.directive('photosets', function () {
return {
scope: {
photosetslist : "=photosetsarray"
},
link: function (scope, element, attr) {
console.log(scope.photosetslist);
//"Hello" is output
}
}
If you see photosetsarray="photosets.photosetDetail"" photosetsarray and
scope: {
photosetslist : "=photosetsarray" **//this name is same as assignee attr**
},
leftside variable name in html must = right side variable name in directive
Be careful with variable naming in these situations. Binding to an attribute that is declared as camel case in the directive cannot be accessed as such from the DOM.
.directive('test', function () {
return {
scope: {
item : "=selectedItem"
},
link: function (scope, element, attr) {
//do some stuff
}
}
So to correctly bind this attribute to a variable on the controller:
<div test selected-item="thePropertyOnTheController"></div>

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.

Change ng-model through directive

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 '='

AngularJS: link function is not being passed a Controller

I am trying to create a directive:
return {
restrict: 'A', // Attribute Directive
ngModel: '^ngModel',
scope: {
'ngModel': '='
},
link: function ($scope: ng.IScope, element, attrs, ctrl) {
var datePickerOptions = {
autoclose: true,
format: attrs.aceDatepickerFormat,
weekStart: attrs.aceDatepickerWeekstart
};
// Attach the datepicker events (must have Bootstrap.DatePicker referenced).
element.datepicker(datePickerOptions).next().on('click', function () {
$(this).prev().focus();
});
element.click(() => {
ctrl.$setViewValue(new Date());
});
}
};
In this example, when the click event occurs on the element, I wish to use ctrl.$setViewValue to the current date (this is a test).
When the link function is called, scope, element and atts are all populated correctly, however the ctrl is null.
The element is with a div with ng-controller set.
<div ng-controller="Controllers.FormElementsController">
<input class="form-control date-picker" id="id-date-picker-1" type="text"
ng-model="DatePickerValue"
ace-datepicker-weekstart="1"
ace-datepicker-format="dd-mm-yyyy"
ace-datepicker="" />
</div>
Why is no controller being passed here?
You have to use require to pull in the controller (ngModelController in your case):
return {
restrict: 'A', // Attribute Directive
require: '^ngModel',
You had it set to ngModel as the property name.
From the docs:
The myPane directive has a require option with value ^myTabs. When a
directive uses this option, $compile will throw an error unless the
specified controller is found. The ^ prefix means that this directive
searches for the controller on its parents (without the ^ prefix, the
directive would look for the controller on just its own element).

Resources