I want to override an image src within a directive. I've tried the following, none of which work.
app.directive('imgTransform', function () {
return {
retrict: 'A',
link: function (scope, elem, attrs) {
elem.attr('ng-src', 'foo');
attrs.ngSrc = 'foo';
elem.attr('src', 'foo');
attrs.src = 'foo';
}
};
});
Does the link function execute before the DOM binding takes place?
I also tried creating an isolate scope and binding to the ngSrc attribute:
scope: {
src: '#ngSrc'
},
Then setting scope.ngSrc in the link function. This did not work either.
Am I missing something?
http://jsfiddle.net/benfosterdev/y7JE9/
You need to modify it before you get to your linking function.
I'd suggest in the controller function, as opposed to digging to deep into the $compiler.
restrict: "A",
controller: function ($scope, $element, $attrs) {
$attrs.$set('ngSrc', 'someOtherValue');
},
link: function (scope, el, attrs) {...}
Here's a fiddle: http://jsfiddle.net/y7JE9/5/
Also, use $observe when looking up interpolated values in your link function. Good luck : )
Related
I'm trying to generate a smart-table directive from within a custom directive I've defined:
<div ng-controller="myContrtoller">
<containing-directive></containing-directive>
</div>
The directive definition:
angular.module('app')
.directive('containingDirective', function() {
return {
restrict: 'E',
replace: true,
template: '<table st-table="collection" st-pipe="scopeFun"></table>',
link: function(scope, elem, attrs) {
scope.scopeFun = function () {
// solve the misteries of life
}
}
}
});
As you can see my directive tries to replace the element by the template generated by the st-table directive, using the st-pipe directive depending on the first, briefly:
ng.module('smart-table')
.controller('stTableController' function () {
// body...
})
.directive('stTable', function () {
return {
restrict: 'A',
controller: 'stTableController',
link: function (scope, element, attr, ctrl) {
// body
}
};
})
.directive('stPipe', function (config, $timeout) {
return {
require: 'stTable',
scope: {
stPipe: '='
},
link: {
pre: function (scope, element, attrs, ctrl) {
var pipePromise = null;
if (ng.isFunction(scope.stPipe)) { // THIS IS ALWAYS UNDEFINED
// DO THINGS
}
},
post: function (scope, element, attrs, ctrl) {
ctrl.pipe();
}
}
};
});
Problem:
The st-pipe directive checks the scope var stPipe if it is defined or not by: if (ng.isFunction(scope.stPipe)). This turns out to be ALWAYS undefined. By inspecting I found two things:
From the stPipe directive, the value supposed to be scope.stPipe that is my scopeFun defined within my containingDirective is undefined on the scope object BUT defined within the scope.$parent object.
If I define my $scope.scopeFun within the myContrtoller I don't have any problem, everything works.
Solution:
I did find a solutions but I don't know what really is going on:
Set replace: false in the containingDirective
Define the scope.scopeFun in the pre-link function of containingDirective
Questions:
Why is the scopeFun available in the stPipe directive scope object if defined in the controller and why it is available in the scope.$parent if defined in the containingDirective?
What is really going on with my solution, and is it possible to find a cleaner solution?
From the docs: "The replacement process migrates all of the attributes / classes from the old element to the new one" so what was happening was this:
<containing-directive whatever-attribute=whatever></containing-directive>
was being replaced with
<table st-table="collection" st-pipe="scopeFun" whatever-attribute=whatever></table>
and somehow st-table did not enjoy the extra attributes (even with no attributes at all..).
By wrapping the containingDirective directive template within another div fixed the problem (I can now use replace:true):
<div><table st-table="collection" st-pipe="scopeFun"></table></div>
If someone has a more structured answer would be really appreciated
I'm in need of doing transclusion together with manual compilation in link function and I'm wondering if there is a better way than what I'm pasting below as "Dynamic". It seems to be working but I'm not sure what are the consequences of removing ng-transclude attribute and I'm removing it due to Angular complaining to Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div ng-transclude="">.
Bottom-line is that I need to provide different templates for different instances of this directive with fallback to generic ones.
I can't use compile part. Here I have just simple example but I have also other cases with big link function that I would rather don't want to convert as this should be just "golden bullet" type of adjustment.
This little guy can be found here https://github.com/institut-de-genomique/Ultimate-DataTable/blob/master/src/directives/udt-form.js in original form.
Original
angular.module('ultimateDataTableServices')
.directive('udtForm', function () {
return {
restrict: 'A',
replace:true,
transclude:true,
templateUrl:'udt-form.html',
link: function(scope, element, attr) {
}
};
});
Dynamic
angular.module('ultimateDataTableServices')
.directive('udtForm', ['$compile', '$templateCache', function ($compile, $templateCache) {
return {
restrict: 'A',
transclude:true,
link: function(scope, element, attr, ctrls, transclude) {
var tplName = 'udt-form.html',
tplPath = scope.udtTable.configMaster.templates[tplName] || scope.udtTable.configMaster.templatesMaster[tplName] || tplName,
elem = angular.element($templateCache.get(tplPath));
elem.find('[ng-transclude]').removeAttr('ng-transclude').append(transclude());
element.replaceWith($compile($templateCache.get(tplPath))(scope));
}
};
}]);
TIA!
I'm trying make a dropdownlist directive, and I want it to have an attribute like onItemSelected="myCallbackFunction(selectedItem)", how can I do it? Can someone provide an example?
You can use the & binding (Isolate Scope Function Expression Binding)
.directive("sample", [function() {
return {
restrict: "A",
scope: {
myFunc: "&"
},
link: function(scope, elem, attrs) {
scope.myFunc(); //whenever you wanna call it
}
}
}])
And the HTML
<div sample my-func="someScopeFunction()"></div>
Id recommend reading: https://gist.github.com/CMCDragonkai/6282750 for directive binding explanations.
To complement tymeJV's answer, if you are not using isolate scope you can make use of $eval or $parse.
app.directive('sample', function($parse){
return {
restrict:'A',
link:function(scope, elem, attrs) {
$parse(attrs.evt)(scope);
scope.$eval(attrs.eval);
}
};
});
Plunk: http://plnkr.co/edit/P1hggqBXMBKBMqJtr6Zg?p=preview
Is there a way to pass configuration object into custom directive which defined as a attribute-directive?
I've got an object in Controller that I want to send to directive:
$scope.configObject = {
count: 5,
mode: 'fast',
getData: myService.getData // function of external service that avaliable in controller
}
In my View I declare directive:
<div class='list-class' my-list='configObject'></div>
Directive looks like:
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = angular.fromJson(attrs.myList);
}
}
i've tried to get config object using angular.getJson - but it doesn't work for functions (it's possible to get only count and mode). Is .getJson() the incorrect way to get config?
Also (I guess it's not even possible) - is there a way to get config object avoiding accessing to
attrs.myList
directly? I mean if I change initializing of directive from
.directive('myList', function() { ... }) to
.directive('myCustomList', function() { ... })
shall I change accessing to
attrs.myCustomList
because view would look like
<div class='list-class' my-custom-list='configObject'></div>
you can pass it using isolate scope if you want
return {
restrict: 'A',
scope: { config : '=myList' }
link: function(scope, elem, attrs) {
//access using scope.config
}
}
or as already answered you can parse it from attrs
$parse(attrs["myList"])(scope);
and yes if you change the directive to myCustomList, you will have to change the code
scope: { config : '=myCustomList' }
or
$parse(attrs["myCustomList"])(scope);
You can use $parse service to fetch the config object.
(function(){
var directiveName = 'myList';
angular.module('YourModule').directive(directiveName,['$parse',function($parse){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = $parse(attrs[directiveName])(scope);
}
};
}]);
})();
You can $eval the attribute
link: function(scole, element, attrs) {
var config = scope.$eval(attrs['myList']);
}
For some reason Angular isn't catching my data-src
When I do this:
<div foo data-src="{{ my_url }}"></div>
I get back nothing. However, it works when I manually type it: i.e
<div foo data-src="blah"></div>
What am I doing wrong here?
More of my code:
var foo = angular.module('foo', []);
foo.directive('foo', function ($document, $rootScope, $http, $resource) {
return {
restrict: 'A',
scope: true,
link: function (scope, elem, attrs) {
console.log(attrs.src)
}
});
[Edit]
I forgot to use camelCase for the attribute name when calling attrs.$observe(). Also, it looks like you do need to use ng-data-source). Correction made below...
You want to use attrs.$observe() in the link function for your directive. Angular will watch the attribute and execute a callback so the directive can be notified when the value of the interpolated expression changes.
Here's a link to a fiddle created by Mark Rajcok that demonstrates it. And a link to the documentation as well.
Finally, here is some sample code using your directive:
var foo = angular.module('foo', []);
foo.directive('foo', function ($document, $rootScope, $http, $resource) {
return {
restrict: 'A',
scope: true,
link: function (scope, elem, attrs) {
attrs.$observe('ngDataSrc', function(newValue) {
console.log('interpolated value', newValue);
}
}
});