Angularjs: how to read object in Directive - angularjs

I have passed an object from controller to directive but when i am reading object in directive i am not able to, it seems in directive object is being read as string.Code is below , i wane to read City and State from the object.
Html File
<div ng-controller="WeatherController">
<div weather ng-object="{{Object}}"></div>
</div>
Controller
.controller('WeatherController', ['$scope', function ($scope) {
$scope.webpartData.OverviewData.Person.Address.City;
$scope.Object = {
City: '',
State: ''
};
$scope.Object.City = 'TestCity';
$scope.Object.State = 'TestState';
});
})
}])
Directive
angular.module('WeatherModule', [])
.directive('Weather', ["$timeout", function($timeout) {
return {
restrict: 'EA',
template: '<div id="weather"></div>',
scope: {
ngObject: '#ngObject'
},
link: function(scope, element, attrs) {
scope.$watch('ngObject', function(value) {
scope.ngObject = value;
});
$timeout(function() {
console.log('Location' + scope.Object.City + ',' + scope.Object.State);
}, 100);
}
};
}])

There are differences between #, = and & in referencing the directive scope members.
1. "#" (Text binding / one-way binding)
2. "=" (Direct model binding / two-way binding)
3. "&" (Behaviour binding / Method binding)
# means that the changes from the controller scope will be reflected in the directive scope but if you modify the value in the directive scope, the controller scope variable will not get affected.
# always expects the mapped attribute to be an expression. This is very important; because to make the “#” prefix work, we need to wrap the attribute value inside {{}}.
= is birectional so if you change the variable in directive scope, the controller scope variable gets affected as well
& is used to bind controller scope method so that if needed we can call it from the directive
In your case you may need to use = instead of #
Have a look on the following fiddle (It's not mine, but it have good and to the point illustration) http://jsfiddle.net/maxisam/QrCXh/
Some Related Questions also:
What is the difference between '#' and '=' in directive scope in AngularJS?
What is the difference between & vs # and = in angularJS

You should use "=" instead "#". And ng-object="Object" instead ng-object="{{Object}}".

Related

Access to a variable in the scope of a directive

How can I access a variable in the scope of a directive?
I have a directive like this:
angular.module('app').directive('any', function() {
return {
restrict: 'E',
scope: {
attr: '#'
},
controller: function($scope) {
$scope.attr = $scope.attr != undefined;
},
template: 'Result (true/false): {{attr}}'
};
});
And in the HTML, for example:
<any attr="bla"></any>
But the variable $scope.attr remains bla, does not change with the value of true or false. How can I do?
Just don't use the same variable to hold the comparison with undefined and the actual attr value.
Every time angular executes its digest loop, it resets the new value of attr in the scope.
When you create an isolate scope in AngularJS, AngularJS provides 3 types of prefixes:
"#" ( Text binding / one-way binding )
"=" ( Direct model binding / two-way binding )
"&" ( Behaviour binding / Method binding )
What you are using is the one-way binding method. For you to use it in your HTML correctly, you need to do this:
<any attr="{{ bla }}"></any>

Accessing ng-repeat scope on a custom directive

I'm having a go at a directive which will dynamically load a template based on a scope value passed into it.
I am using ng-repeat on my directive, and am using the iterated item as an attribute property:
<my-form-field ng-repeat="field in customFields" field="field">
In my directive I have the following code to set the template being used.
(function() {
'use strict';
angular
.module('app')
.directive('myFormField', myFormField);
function myFormField() {
var directive = {
restrict: 'E',
scope: {
field: '='
},
link: function(scope){
scope.getContentUrl = function() {
return 'app/modules/form_components/form_field/' + scope.field.type + '-template.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
};
return directive;
}
})();
Whilst the above works (which I found from other posts), I wonder if there is a better way.
For example I have seen examples of calling a function on the templateUrl config option instead, and then in that function access the scope attributes being passed in. When I tried this way, my field attribute was a literal 'field' string value (they are objects in my customFields array), so I think at that point the scope variables had not yet been evaluated.
With this current solution I am using, all of my templates get wrapped in an extra div since I am using ng-include, so I am just trying to make the rendered markup more succinct.
Any suggestions\advice is appreciated.
Thanks

Isolated scope variable of directive is undefined

I'm trying to access a variable of a controller from a directive but the variable is undefined and I can't figure out why. The variable is undefined immediately on page load which is I'd guess is because the html is loaded before the controller is initialised but I don't understand how it's undefined afterwards when the html is loaded and renderTimetable is called.
angular.module('app')
.directive('timetable',
function() {
return {
restrict: 'E',
scope: {
bookings: '=',
timetableDate: '='
},
link: function(scope, elem, attrs) {
scope.$watch('bookings', function(updatedBookings, unupdatedBookings) {
if (typeof updatedBookings !== 'undefined') {
console.log(scope.timetableDate); // undefined
scope.renderTimetable(updatedBookings);
}
});
// bookings
scope.renderTimetable = function(planes) {
console.log(scope.timetableDate); // undefined
}
};
}
};
});
And the directive has attribute set with controller's scope:
<timetable bookings="bookings" timetableDate="timetableDate"></timetable>
The timetableDate model binded like so:
<input readonly datepicker ng-model="timetableDate" value="timetableDate" />
In the controller the variable is set immediately, console.log gets a date string as expected:
...
function($scope, $http, $location, $interval) {
$scope.bookingModalVisible = false;
$scope.timetableDate = new Date().toDateString();
console.log(timetableDate); // NOT undefined
Change
<timetable bookings="bookings" timetableDate="timetableDate"></timetable>
to
<timetable bookings="bookings" timetable-date="timetableDate"></timetable>
From Angular docs:
Angular normalizes an element's tag and attribute name to determine
which elements match which directives. We typically refer to
directives by their case-sensitive camelCase normalized name (e.g.
ngModel). However, since HTML is case-insensitive, we refer to
directives in the DOM by lower-case forms, typically using
dash-delimited attributes on DOM elements (e.g. ng-model).

how to set an interpolated value in angular directive?

How do I set the interpolated value in a directive? I can read the correct value from the following code, but I have not been able to set it.
js:
app.directive('ngMyDirective', function () {
return function(scope, element, attrs) {
console.log(scope.$eval(attrs.ngMyDirective));
//set the interpolated attrs.ngMyDirective value somehow!!!
}
});
html:
<div ng-my-directive="myscopevalue"></div>
where myscopevalue is a value on my controller's scope.
Whenever a directive does not use an isolate scope and you specify a scope property using an attribute, and you want to change that property's value, I suggest using $parse. (I think the syntax is nicer than $eval's.)
app.directive('ngMyDirective', function ($parse) {
return function(scope, element, attrs) {
var model = $parse(attrs.ngMyDirective);
console.log(model(scope));
model.assign(scope,'Anton');
console.log(model(scope));
}
});
fiddle
$parse works whether or not the attribute contains a dot.
If you want to set a value on the scope but don't know the name of the property (ahead of time), you can use object[property] syntax:
scope[attrs.myNgDirective] = 'newValue';
If the string in the attribute contains a dot (e.g. myObject.myProperty), this won't work; you can use $eval to do an assignment:
// like calling "myscopevalue = 'newValue'"
scope.$eval(attrs.myNgDirective + " = 'newValue'");
[Update: You should really use $parse instead of $eval. See Mark's answer.]
If you're using an isolate scope, you can use the = annotation:
app.directive('ngMyDirective', function () {
return {
scope: {
theValue: '=ngMyDirective'
},
link: function(scope, element, attrs) {
// will automatically change parent scope value
// associated by the variable name given to `attrs.ngMyDirective`
scope.theValue = 'newValue';
}
}
});
You can see an example of this in this Angular/jQuery color picker JSFiddle example, where assigning to scope.color inside the directive automatically updates the variable passed into the directive on the controller's scope.

Isolate scope attributes defined with # are undefined/disappear in directive's link function

The directive has isolate scope, and the scope attributes are being passed with "#".
This is how the directive is called:
<div ng-init="classForIcon = 'vladClass'"></div>
<div ng-init="textForIcon = 'Icon\'s text'"></div>
<div ng-init="routeForIcon = 'www.google.com'"></div>
<div ng-init="tooltipForIcon = 'my tooltip'"></div>
<div ng-init="imageForIcon = 'images/HOME_ICON1.png'"></div>
<rl-icon-widget icon-class="{{classForIcon}}" icon-text = "{{textForIcon}}" icon-tooltip="{{tooltipForIcon}}" icon-route="{{routeForIcon}}" icon-image="{{imageForIcon}}"></rl-icon-widget>
This is how the directive is defined:
'use strict';
fcPortalApp.directive('rlIconWidget', ['localizationAssistant', function(localizationAssistant) {
var obj = {
restrict: 'E',
templateUrl: 'scripts/directives/rliconwidget/rliconwidget.html',
//require: 'ngModel',
scope: {
//ngModel: '#',
iconClass: "#",
iconRoute: "#",
iconText: "#",
iconTooltip: "#",
iconImage: "#"
},
link: function(scope, element, attrs) {
console.log(scope);
console.log(scope.iconImage);
console.log(scope.iconTooltip);
console.log(scope.iconRoute);
}
};
console.log(obj);
return obj;
}]);
When I inspect the scope object (click on the output of console.log(scope_ in firebug), it looks like it has iconImage, iconTooltip and iconRoute properties set correctly.
Yet console.log(scope.iconImage), console.log(scope.iconTooltip) and console.log(scope.iconRoute) print "undefined"
Use $observe to observe the value changes of attributes that contain interpolation (e.g. src="{{bar}}"). Not only is this very efficient but it's also the only way to easily get the actual value because during the linking phase the interpolation hasn't been evaluated yet and so the value is at this time set to undefined. -- directive doc
By the time you manually inspect the scope, the values get defined.
The reason we need to use $observe (actually $watch will also work for isolate scope properties defined with '#') is because a directive will likely need to do something whenever the interpolated value changes. E.g., if the value of textForIcon changes, you may want to modify something in the DOM that is managed by your directive.
If you need the values defined when the linking function runs, you have two options:
Use '=' instead of '#'. This will require that you remove the {{}}s from the HTML.
If the values won't change, pass strings: <rl-icon-widget icon-class="vladClass" ...> Then in your directive, simply use attrs.iconClass -- no need for '#'.

Resources