How to read scope properties from parent scope - javascript

I am creating a simple directive however I am stuck at the beginning. My problem is that I am unable to read the property from scope in the post function. Here is my code:
<div mod type="{{subnet.isDynamic | type }}"></div>
networkInterfaces.directive('mod', function () {
return {
scope: {
type: '#'
},
link: function (scope, iElement, iAttrs, controller) {
console.log(scope.type);
if (scope.type == "Static") {
iElement.css('background', 'blue');
}
if (scope.type == "Dynamic") {
iElement.css('background', 'green');
}
}
}
});

Your scope property is really wrong and you don't need it here.
A working plnkr here : http://plnkr.co/edit/ezbzad?p=preview
Please refer to the docs for understand the scope property inside a directive : http://docs.angularjs.org/guide/directive
But if you really want to use just do that :
app.directive("mod", function(){
return {
scope: {
value: "#value"
},
link: function(scope, element, attrs){
console.log(scope.value);
}
}
});
the scope property with {} create a new isolate scope so you can't access to your value.
Adding value: "#value" the scope is bind to the value property but note that the syntax is a javascript object syntax it's :
value : "#value" and not value = "#value" !!!
Oh and a last thing : if your directive is call mod ... in your div your attribute is mod not someDirective.

Related

How to pass data from directive to parent scope in my case?

I have a question regarding the directive and controller.
I want to pass the data from directive to controller in my case.
template html
<img ng-src=“{{url}}” image-detect />
<div>width: {{width of image}}</div> // how do I show the value here
<div>height: {{height of image}}</div>
directive
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
$scope.imageWidth = $(this).width();
$scope.imageHeight = $(this).height();
//not sure what to do to pass the width and height I calculate in directive to the parent
});
}
})();
How do I pass imageWidth and imageHeight to the parent scope and show it in the template? Thanks a lot!
Two methods come to my mind:
You can have an object something like imageDimention declared on base/parent/whatever scope and then have it shared with directive through scope isolation and then from directive's controller's scope you can access that imageDimention object and set its imageWidth and imageHeight properties. Setting these properties in directive will take effect in base/parent/whatever scope as well:
Example of scope isolation, copied from angular documentation
angular
.module('yourapp')
.directive('myImage', function() {
return {
restrict: 'E',
scope: {
imageDimention: '=imageDimention'
},
controller: 'ImageController'
};
});
Then in ImageController's scope you can access same imageDimention object
Create a ContextService this can be used for sharing data between different angular components not just these image properties. Its good to have a ContextService somewhat generic so that it can be reused/general purpose.
ContextService can be something like:
angular.module('yourapp')
.factory('ContextService', ContextService);
function ContextService() {
var service = {};
var data = {};
service.set = set;
service.get = get;
function set(key, value) {
if(key !== null && key !== undefined){
data[key] = value;
}
}
function get(key) {
return data[key];
}
return service;
}
And then you can inject this service into your angular components(controllers/directives/other services) and access it as some kind of global objects because services are singleton, this will serve you as data sharing module.
In your case, you probably have a controller that is attached to view, so assuming you have that controller, should declare an object say image on this controllers scope:
$scope.image = {
url: 'imageUrl'
width: '0px',
height: '0px',
}
Then your html template should probably be something like this:
<img ng-src="{{image.url}}" image-detect />
<div>width: {{image.width}}</div>
<div>height: {{image.height}}</div>
And your directive should look like this:
(function () {
angular
.module(‘myApp’)
.directive(‘imageDetect’, imageDetect);
function imageDetect() {
var directive = {
'restrict': 'A',
'scope': {
'image': '=image'
},
'controller': imageController
};
return directive;
}
function imageController($scope, $element) {
$element.on('load', function() {
//here you can access image object from scope which is same as controller that is attached to the view
$scope.image.width = $(this).width();
$scope.image.height = $(this).height();
});
}
})();
I hope this might help...

Isolated scope same in both element and attribute directive

How do I setup an element/attribute directive that acts like:
<directive value="string"></directive>
and
<div directive="string"></div>
where the string value is the same in the scope?
restrict: 'EA',
scope: {
value: '=' || '=directive' // ???
}
I know ngInclude does something similar to this with src=""
You can use ngInclude implement code, and it will work in your case because attrs['blob'] gives you String and your value typeof is String.
But if your value was Object or Number you should use $eval or $parse for converting its value from String.
scope.value = scope.$eval(attrs.value || attrs.directive);
Or simply you can do this:
scope: {
directive: '=?',
value: '=?'
},
link: function(scope) {
scope.value = scope.value || scope.directive;
}

Sharing scope object with child directive scopes

I have a parent directive which creates an instance of a third party object during link and that variable needs to be accessible to child directives.
However, there are two limitations:
There can be multiple instances of this per-page, so a singleton at the top of the javascript file won't work.
The child directives are recursive, so they have to create their own scope.
The only way I can think of is to pass that value as an attribute to each child directive. That feels inefficient but given the above constraints, I don't see any alternative.
// Some imaginary third-party object
function Tree() {}
// Root directive which creates an instance of the object, links to the page, and loads the data needed.
app.directive('tree', function() {
return {
restrict: 'E',
replace: true,
template: '<div><nodes nodes="nodes"></nodes></div>',
scope: {
nodes: '='
},
link: function(scope, $element) {
// This value needs to be accessible to all child directives
scope.tree = new Tree();
}
};
});
// A directive to render an array of nodes
app.directive('nodes', function() {
return {
restrict: 'E',
replace: true,
template: '<ol>' +
'<li ng-repeat="node in nodes track by node.id">' +
'<node node="node"></node>' +
'</li>' +
'</ol>',
scope: {
nodes: '='
}
};
});
// A directive to render a single node, and recursively any child nodes
app.directive('node', ['$compile', function($compile) {
return {
restrict: 'E',
replace: true,
scope: {
node: '='
},
template: '<div><span ng-bind="node.text"></span></div>',
link: function(scope, $element) {
if (scope.node.children && scope.node.children.length > 0) {
console.log(scope.node.children);
var tmpl = '<nodes nodes="node.children"></nodes>';
var children = $compile(tmpl)(scope);
$element.append(children);
}
// #todo Here's a good example of where I need access to scope.tree
}
};
}]);
The only solution I can think of is adding tree: '=' to the scope objects and then passing in tree="tree" to each child.
Plunker
One solution would be to not isolate the scope, and that way the child directives would inherit from their parent scope. If the parent has scope.tree = new Tree() then the nested directives would inherit scope.tree.
However, since you mentioned that there can be multiple instances of this per page, that means you probably need an isolated scope (which is what you currently have). The only way to pass data into an isolated scope is through the directive attributes in the markup.
I think you answered your own question :)

How can I bind scope property of transcluded element to parent?

I'm attempting to create a reusable parent container directive. What I want is something like the following:
<parent title="Parent title">
<child></child>
</parent>
where I can have the child elements change a value in the parent's scope.
I've attempted this binding as follows (see this fiddle):
myApp.directive('parent', function() {
return {
scope: {'title': '#'},
transclude: true,
// Manually transclude so we set parent scope to be this scope.
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope.$new(), function(content) {
element.append(content);
});
},
restrict: 'EA',
template: '<div><b>PARENT:</b> {{title}}</div>',
controller: function($scope) {
$scope.inherited = true;
$scope.$watch('inherited', function(newValue, oldValue) {
if(!newValue) {
console.log('Parent: inherited switched');
}
});
}
}
});
myApp.directive('child', function() {
return {
restrict: 'EA',
scope:{
inherited: '='
},
template: '<div><b>Child:</b> inherited attribute = {{inherited}}</div>',
controller: function($scope) {
// Why is inherited not bound to parent here?
console.log($scope.inherited);
// It obviously exists in the parent...
console.log($scope.$parent.inherited);
}
}
});
From my understanding of the API, setting an object hash scope with inherited: '=' should bind to the parent property, but as you can see in the fiddle, it doesn't.
I have two questions:
Why is inherited not bound to the parent property?
How can I achieve this message passing?
Caveats:
I would prefer not to use require: '^parent', since this creates a rather strong dependency of the child on the parent (I might want to create a number of possible parent containers and a number of child directives and mix-and-match).
I absolutely will not use link in the child directive to climb to the $parent. That's a hack and won't work in general for deeply nested directives.
I am willing to use some hackery in the parent.
Thanks for any guidance.
You can either do:
<child inherited="inherited"></child>
where you actually pass in the inherited attribute, since that is what it is looking for when you do
scope:{
inherited: '='
},
Or you could do:
myApp.directive('child', function() {
return {
restrict: 'EA',
scope: false,
so that it uses it's parent's scope.
Either way, you are likely to run into some issues binding to a primitive. You will probably save yourself a lot of headache by making inherited an object, passing the object around, and when you need to display it show the object property:
JS:
$scope.inherited = {is: true};
HTML:
{{inherited.is}}
But then keep passing around the whole object.
http://jsfiddle.net/GQX9z/1/

How to set angular controller object property value from directive in child scope

I have have a directive inside an ng-repeater that should set a scope property.
Please see the fiddle here: http://jsfiddle.net/paos/CSbRB/
The problem is that the scope property is given as an attribute value like this:
<button ng-update1="inputdata.title">click me</button>
The directive is supposed to set the scope property inputdata.title to some string. This does not work:
app.directive('ngUpdate1', function() {
return function(scope, element, attrs) {
element.bind('click', function() {
scope.$apply(function() {
scope[ attrs.ngUpdate1 ] = "Button 1";
});
});
};
});
However, assigning directly works:
scope["inputdata"]["title"] = "Button 1";
Can you please tell me how I can set a scope property with . notation in its name from a directive?
PS: The reason the fiddle is using a repeater is because it makes the directives be in child scopes. When they are in a child scope, you can't write to scope properties that are primitives. That's why I need an object property with "." in the name. See the long explanation here: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
Thank you
$parse will solve your problem.
<button ng-update1="inputdata.title">
app.directive('ngUpdate1', function($parse) {
return function(scope, element, attrs) {
var model = $parse(attrs.ngUpdate1);
console.log(model(scope)); // logs "test"
element.bind('click', function() {
model.assign(scope, "Button 1");
scope.$apply();
});
};
});
Fiddle
Whenever a directive does not use an isolate scope and you specify a scope property using an attribute, and you want to modify the value, use $parse.
If you don't need to modify the value, you can use $eval instead:
console.log(scope.$eval(attrs.ngUpdate1));
Not sure what overall objective is but one way is to create 2 attributes, one for the target object and the other for the property of that object:
<button ng-update1 obj="inputdata" prop="title">
app.directive('ngUpdate1', function() {
return function(scope, element, attrs) {
element.bind('click', function() {
scope.$apply(function() {
scope[ attrs.obj ][attrs.prop] = "Button 1";
});
});
};
});
DEMO:http://jsfiddle.net/CSbRB/9/
Alternatively using existing format you could split() value of your current ng-update1 attribute and use result array for object and property in notation
element.bind('click', function() {
var target=attrs.ngUpdate1.split('.');
scope.$apply(function() {
scope[ target[0] ][target[1]] = "Button 1";
});
});
DEMO with both approaches: http://jsfiddle.net/CSbRB/10/
One more approach where you create an isolated scope in directive and can pass in the reference to inputdata object and pull property name from attribute(same markup as second version):
app.directive('ngUpdate3', function () {
return {
scope: {
targetObject: '=obj'
},
link: function (scope, element, attrs) {
element.bind('click', function () {
scope.$apply(function () {
scope.targetObject[attrs.prop]='Button 3';
});
});
}
}
});
http://jsfiddle.net/CSbRB/11/

Categories

Resources