I have an angular directive that is a custom attribute that possibly can contain a value like so:
<div my-directive="myVal"></div>
How do I read myVal (as a string) from inside my directive's link function?
The passed value is on the attributes object passed as the third argument to the link function. It's under a property matching the name of the directive.
app.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, elem, attr) {
//read the passed value
alert(attr.myDirective);
}
}
});
Related
I have one concern when creating a custom directive in angular.
When I'm using a link function, I'm not sure what is the real difference when accessing attributes with attrs or scope.
Take this piece of code for example:
myApp.directive('someDirective', function() {
return {
restrict: 'E',
replace: true,
scope: {
title: '=title'
},
template: '<img/>',
link: function(scope, element, attrs) {
if (scope.title) {
// do something here
}
if (attrs.title){
// do something here
}
},
}
From my observations accessing 'title' attribute from attrs and by scope has a similar effect. What is the real difference?
The difference is that attribute is of a String type by definition. Always. In your case attrs.title will be literally string equal to whatever you pass into attribute in HTML.
However, scope.title is parsed and evaluated result of the attribute attr.title.
Ex. If you use something like this in HTML
<some-directive title="name"></some-directive>
where $scope.name = "Thomas Mann" defined in the scope, then attr.title will be string "name", while scope.title will be "Thomas Mann".
I'm setting up a directive which can't have an isolated scope because there are going to be other directives (which are 3rd party) which are already using it. I need to get the reference of a third party object and pass it to the controller as a variable. How can I do this?
angular.module('myModule')
.directive('objectSync', ['$thirdParty', function ($thirdParty) {
return {
compile: function() {
return {
pre: function(scope, element, attrs) {
var thirdPartyObject = $thirdPartyObject.getCurrentScopeObject();
scope[attrs.objectSync] = thirdPartyObject;
}
};
}
};
}]);
And this is the html:
<third-party-directive
sync-object-to-controller="objectToSync">
When I do this, objectToSync never makes it to the controller's scope. But if I set up a container object in the scope: $scope.containerObject = {}, in the directive instead of adding it right to the scope I add it there: scope.containerObject[attrs.objectSync] = thirdPartyObject;, then it makes it through, but I can only call it/attach listeners to it using watch: $scope.$watch(function(){ return $scope.containerObject; } ...
So how can I pull an object from a third party from within the scope of the third party directive and pass it into the root of the controller's scope with a name specified in my own directive before the controller runs?
You need to receive the parameter passed to third-party-directive.
angular.module('myModule')
.directive('objectSync', ['$thirdParty', function ($thirdParty) {
return {
scope : {syncObjectToController : '='} //this receives the parameter
//the hyphen separated name gets converted to camel casing
compile: function() {
return {
pre: function(scope, element, attrs) {
//now you can directly use the third party object here directly
var thirdPartyObject = scope.syncObjectToController
}
};
}
};
}]);
Read more about it in angular's documentation of directives.
I've got an Angular directive. Inside the link function, I do this:
link: function(scope, element, attrs) {
...
element.data('startY', value);
...
}
What I'd like to do is perfix 'startY' with the name of the directive, without hard-coding the name. I'd like to dynamically get the name.
Is there any way to do this? Does Angular provide a way to reflect it? Something like:
link: function(scope, element, attrs) {
...
element.data(this.$name + '-startY', value);
...
}
If not, what are the recommended best practices for choosing data() keys to avoid collisions?
As indicated in the AngularJS source code, a directive's name is assigned in the context of the object literal where your directive options reside. The link function however cannot access the object literal's context, this, because it will be transferred to a compile function where it will be returned and invoked after the compilation process has taken place.
To get the name within your link function you can follow any of these suggestions:
[ 1 ] Create a variable that may hold reference to the object literal(directive options).
.directive('myDirective', function() {
var dir = {
link: function(scope, elem, attr) {
console.log(dir.name);
}
};
return dir;
});
[ 2 ] You can also get the directive's name by using the compile function since it is invoked in the context of the directive option.
.directive('myDirective', function() {
return {
compile: function(tElem, tAttr) {
var dirName = this.name;
return function(scope, elem, attr) { /* link function */ }
}
};
});
As far as I can tell you've answered your own question. You may prefix the name by string concatenation as you've done but it will probably be easier to add it as a separate data store.
element.data('directiveName', this.$name).data('startY', value);
I'm not sure what you mean by avoid collision as this will only apply to the element that was passed into the link function.
I've created a custom directive in AngularJS. The directive uses isolated scope, and it somehow prevents the binding for standard ngModel on the same element.
I want to create a confirm password field (text for readability in the example).
<input type="text" name="one" ng-model="fields.field_one">
<input type="text" validate-match="fields.field_one" name="two" ng-model="field_two">
My directive invalidates the field, when there is no match.
app.directive('validateMatch', function() {
return {
require: 'ngModel',
scope: { matchValue: '=validateMatch' },
link: function(scope, elm, attr, ctrl) {
scope.$watch('matchValue', function(value) {
ctrl.$setValidity('match',
ctrl.$viewValue === value
|| !ctrl.$viewValue && !value);
});
function validate(value) {
ctrl.$setValidity('match', value === scope.matchValue);
return value;
}
ctrl.$parsers.push(validate);
ctrl.$formatters.push(validate);
}
}
});
The thing is, why can't I change the value of that field by changing the model? First field works just fine.
Look at the plunker for details and commented code.
As mentioned in a comment, isolate scopes and ng-model don't mix well. Further, we shouldn't be using an isolate scope here, since we're trying to create a directive/component that needs to interact with another directive (ng-model in this case).
Since the validateMatch directive does not create any new properties, the directive does not need to create any new scope. $parse can be used to get the value of the property that attribute validate-match refers to:
app.directive('validateMatch', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attr, ctrl) {
var model = $parse(attr.validateMatch);
// watch for linked field change (field_one)
scope.$watch(model, function(value) {
console.log('linked change:', value, ctrl.$viewValue);
// set valid if equal or both falsy (empty/undefined/null)
ctrl.$setValidity('match',
ctrl.$viewValue === value
|| !ctrl.$viewValue && !value);
});
// validate on parse/format (field_two)
function validate(value) {
var otherFieldValue = model(scope);
console.log('validate:', value, otherFieldValue);
// set valid if equal
ctrl.$setValidity('match', value === otherFieldValue);
return value;
}
ctrl.$parsers.push(validate);
ctrl.$formatters.push(validate);
}
};
});
plunker
Following Mark's suggestion, I managed to produce a work-around.
When isolated scope exists on the element, ngModel refers to it. The trick is to look the parent scope from within. You can either change the ngModel by hand (prepending it with $parent.), or automatize this process inside the directive by proper compile function.
This is how i did this:
compile: function(element, attrs, transclude) {
// reference parent scope, because isolated
// scopes are not looking up by default
attrs.$set('ngModel', '$parent.'+attrs.ngModel, false);
return function(scope, elm, attr, ctrl) {
// link function body there
}
}
For the full example, look at this plunk.
From what I understand of AngularJS Directives you can use the transclude parameter to access the parent scope of the controller.
I'm having issues getting an object to pass to my directive. I believe I've done things correctly, but after failed attempt after failed attempt I must seek help. What did I miss here that's stopping me from passing an array to my directive?
HTML:
<div class="body">
{{orderList.length}} //shows up as 18
</div>
<queue-summary orders="orderList"></queue-summary>
Javascript:
directive('queueSummary', function () {
return {
scope: {
orders: '='
},
replace: true,
restrict: 'E',
templateUrl: '/partials/admin/bits/queue-summary.htm',
link: function (scope, element, attrs) {
console.log(scope, element, attrs); //$attrs.orders show it as the String "orderList" instead of the array
}
}
}).
It's worth noting that you can access the bound value of an attribute you don't have isolate scope on with $eval:
scope.$eval(attrs.orders)
attrs will just show you the string value of an attribute. In order to access the passed object, use the isolate binding you created:
console.log(scope.orders);