I'm trying to pass some data from directive into a function addTrackFromPicker in my controller.
$scope.addTrackFromPicker = function (message) {
console.log("addTrackFromPicker", message);
};
Here what I have in my directive
dir.directive('youtubeList', function($http, $timeout, YT_event){
return {
restrict: 'E',
scope: {
search: '=',
dial: '&'
},
templateUrl: 'youtube-list.html',
...
Here I want to call controllers function from my template and pass it item.id.$t as argument:
<div class="media list-group-item" ng-repeat="item in entries">
<a type="button" ng-click="dial(item.id.$t)">
<img ng-src="{{item.media$group.media$thumbnail[0].url}}">
</a>
But I don't know how to pass it into my tag
<youtube-list search="search" dial="addTrackFromPicker(???)"></youtube-list>
I also tried $parent.addTrackFromPicker but it didnt work
In order to pass in your data from your directive, you will need to do it like this:
<youtube-list search="search" dial="addTrackFromPicker(data)"></youtube-list>
Then, in your template:
<div class="media list-group-item" ng-repeat="item in entries">
<a type="button" ng-click="dial({data: item.id.$t})">
<img ng-src="{{item.media$group.media$thumbnail[0].url}}">
</a>
</div>
You can use an "argument name" other than data if something else makes more sense for your situation. See Angular's documentation on scope for detailed info on how this works.
Isolated Scope: pass some values from the parent scope to the directives
There’re 3 types of prefixes AngularJS provides
"#" ( Text binding / one-way binding )
"=" ( Direct model binding / two-way binding )
"&" ( Behaviour binding / Method binding )
All these prefixes receives data from the attributes of the directive element.
class="directive"
name="{{name}}"
color="color"
When the directive encounters a prefix in the scope property, it will look for an attribute ( with same property name ) on directive’s html element
scope : {
name: "#"
}
I've followed this linkhttp://jsfiddle.net/shidhincr/pJLT8/10/light/
Related
I have a directive foo whose template includes a list via ng-repeat:
<div>
<h5>I want to insert transcluded template into body of the li:</h5>
<ul>
<li ng-repeat='item in items'>
<!-- need item template here -->
</li>
</ul>
</div>
I want the template for each item to (optionally) to be specifiable at the point of usage of the directive:
<foo items='people'>
<h5>{{item.name}}</h5>
</foo>
<foo items='people'>
{{item.name}} is {{item.age}} years old.
</foo>
So I need the innerHTML of the directive (e.g. <h5>{{item.name}}</h5>) to be copied to the marked location in the directive template.
<ng-transclude> does this, but it gives the transcluded items the scope of the directive rather than the item. I also need to be able to optionally pull the item template from somewhere else. So I need to do the transclusion manually.
I have access to the transcluded content during link:, but at that point the list item in question is gone!
<div>
<h5>I need to insert transcluded template into body of the li:</h5>
<ul>
<!-- ngRepeat: item in items -->
</ul>
</div>
I think I need to do it during compile, but the transclusion function passed to the compile function is deprecated.
Found a way to do it with a second directive, but that seems unnecessary...
You can achieve that using $interpolate service and changing a bit your approach please see demo here http://plnkr.co/edit/bSb7fEWiMTdNVJYyiXD8?p=preview
set your dynamic template as attribute in directive.
<foo template="'<h1>{{item.name}}</h1>'" items="people"></foo>
and change your directive to :
app.directive('foo', function($interpolate) {
return {
scope: {
items: '=',
template:'='
},
restrict: 'E',
transclude: true,
templateUrl: 'foo-template.html',
link: function(scope, element, attributes, controller, transclude) {
//interpolate your template like below
scope.getValue= function(item) {
var exp = $interpolate(scope.template);
var result =exp({item:item})
return result;
}
}
}
});
and in your template use ng-bind-html
<li ng-repeat='item in items'>
<div ng-bind-html="getValue(item)"></div>
</li>
don't forget to add ngSanitize module to your app
I am working on an AngularJS app. I am trying to write a reusable component to use throughout my app. For the sake of demonstration, we'll just use a text box. I've created a fiddle of my attempt here. Basically, I'm trying to use a control like this:
<div ng-app="myApp">
<div ng-controller="myController">
<my-control textValue="{{controlValue}}"></my-control>
<br />
You entered: {{controlValue}}
</div>
</div>
Unfortunately, I cannot figure out how to bind a property between the scope of the controller and the scope of the directive. I'm not sure what I'm doing wrong in my fiddle. Can someone please tell me what I'm doing wrong?
Thank you!
You have created directive with isolated scope and you are trying to print value from isolate scope. It isn't right, you can write your directive like this, without isolated scope:
return {
restrict: 'EAC',
template: '<input type="text" ng-model="controlValue"></input>'
};
if you want to setup directive with isolated scope, you should isolate your scope then use $watch for do changes
You were not so far, but you need to be carefull when using {{}}
Remove braces and don't use camelNotation for text-value attribute :
<div ng-app="myApp">
<div ng-controller="myController">
<my-control text-value="controlValue"></my-control>
<br />
You entered: {{controlValue}}
</div>
</div>
Use ng-model attribute :
angular.module('ui.directives', []).directive('myControl',
function() {
return {
restrict: 'EAC',
scope: {
textValue: '='
},
template: '<input type="text" ng-model="textValue"></input>'
};
}
);
I have two directives:
window.app.directive('placeholder', function ($compile, $route, $rootScope) {
return {
restrict: 'AC',
link: function (scope, element, attr) {
// Store the placeholder element for later use
$rootScope["placeholder_" + attr.placeholder] = element[0];
// Clear the placeholder when navigating
$rootScope.$on('$routeChangeSuccess', function (e, a, b) {
element.html('');
});
}
};
});
window.app.directive('section', function ($compile, $route, $rootScope) {
return {
restrict: 'AC',
link: function (scope, element, attr) {
// Locate the placeholder element
var targetElement = $rootScope["placeholder_" + attr.section];
// Compile the template and bind it to the current scope, and inject it into the placeholder
$(targetElement).html($compile(element.html())(scope));
element.html('');
}
};
});
I use them to basically swap out one section with html in another.
If I have the following html:
<div placeholder="footer"></div>
<div section="footer">
<ul ng-model="items">
<li ng-repeat="item in items"> {{item.Description}}</li>
</ul>
</div>
The ng-repeat doesn't seem to be working. If I simply output {{items}} below the , it displays fine. Also, I know binding is working because I can change items and it will update.
Lastly, if I move the ul outside the section it works fine.
So, my question is why does this not work (compile ng-repeat inside directive).
Am I missing something?
EDIT:
What is confusing me, is I can do this:
<div section="footer">
<!-- This Works -->
{{items}}
<!-- This also works -->
<input type="text" ng-model="items[0].Description" />
<!-- This doesn't work -->
<ul ng-model="items">
<li ng-repeat="item in items"> {{item.Description}}</li>
</ul>
</div>
This isn't going to work. It can't evaluate something from another scope without having an exact copy of it in its scope. If you want two directives to communicate use require and setup a way for them to do that if they aren't in a parent child relationship.
A couple of things you should think about. Essentially what you are doing is called transclusion. Section directive would use ng-transclude to capture the client's defined code. Use transclusion and maybe you can evaluate the template into html in the scope of section then using directive communication allow it to pass the HTML block (already evaluated) to the other directive. The only problem is making sure this happens when things change through binding. You're probably going to need some $watches on variables in section in order for placeholder to be notified when things change.
You will probably need a 3rd directive so allow section and placeholder to communicate through. In this example say I have a 3rd directive called broadcaster. Then section and placeholder will require broadcaster (ie require: '^broadcaster'), and it will define some interface for each of the directives to send HTML from section -> placeholder. Something like this:
<div broadcaster>
<div placeholder="footer"></div>
<div section="footer">
<ul>...transcluded content</ul>
</div>
</div>
My html:
<div ng-app="myApp">
<div ng-controller="testCtrl">
<div in-tags text="{{ tags }}"></div>
<div data-ng-show="tags.length" in-tags text="{{ tags }}"></div>
<p data-ng-show="tags.length">another text</p>
</div>
</div>
And js:
.controller('testCtrl', function($scope){
$scope.tags = 'one two three';
})
.directive( 'inTags',function() {
return {
scope: {
text: '#'
},
template: '<span ng-repeat="item in text | splitByWords"> {{ item }} </span>'
};
})
.filter( 'splitByWords', function() {
return function( text ) {
return text.split( /\s+/ );
};
});
How it works: http://jsfiddle.net/3HT2F/12/
Question is: Why tags.length interpreted like false with directive?
extra question: How can i hide div?
For your primary question, the scope attribute on your directive (inTags) sets a new isolated scope with only one member (the connected text attribute). It's one of the stumbling blocks of Angular with nested scopes and isolated scopes. When you set a literal object for the scope and specify a mapping (such as this case with at the dom attribute binding using '#'), it creates an isolated scope that doesn't inherit any other values from its parent. So tags is no longer a member of the local scope on that element.
See the scope rules for directives
Second question, why wouldn't ngShow or ngHide work? If you're on a new enough Angular (1.2+), you can also use ngIf to complete remove elements vs just hiding them.
Edit: Here's your fiddle updated
I'm giving a first try at AngularJS custom directives.
I'm having trouble using (or understanding ...) the isolated scope in the link function of the directive.
Here is the code of this part of my app :
view.html
...
<raw-data id="request-data" title="XML of the request" data="request">See the request</raw-data>
...
request is a variable published in the scope of the viewCtrl that contains the xml-string of a request.
rawData.js
directives.directive('rawData', function() {
return {
restrict : 'E',
templateUrl : 'partials/directives/raw-data.html',
replace : true,
transclude : true,
scope : {
id : '#',
title : '#',
data : '='
},
link : function($scope, $elem, $attr) {
console.log($scope.data); //the data is correclty printed
console.log($scope.id); //undefined
}
};
});
raw-data.html
<div>
<!-- Button to trigger modal -->
<a href="#{{id}}Modal" role="button" class="btn" data-toggle="modal" ng-transclude></a>
<!-- Modal -->
<div id="{{id}}Modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="{{id}}Modal" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">{{ title }}</h3>
</div>
<div class="modal-body">
<textarea class="input-block-level" rows="10">{{ data }}</textarea>
</div>
<div class="modal-footer">
<!-- <button class="btn" ng-click="toggleTagText('')">{{'cacher'}} l'image</button> -->
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">Fermer</button>
</div>
</div>
</div>
I don't understand why the ID is correclty shown when the modal pops, but when I try to console.log() it, its value is undefined.
Maybe i'm wrong with the isolated scope value (= and #).
Thank you for reading. :)
Isolate scope properties bound with # are not immediately available in the linking function. You need to use $observe:
$attr.$observe('id', function(value) {
console.log(value);
});
Your template works properly because Angular automatically updates isolate scope property id for you. And when it does update, your template automatically updates too.
If you are just passing strings, you can simply evaluate the values once instead of using # binding:
link: function($scope, $elem, $attr) {
var id = $attr.id;
var title = $attr.title
console.log(id, title);
}
However, in your case, since you want to use the properties in templates, you should use #.
If you weren't using templates, then # is useful when attribute values contain {{}}s – e.g., title="{{myTitle}}". Then the need to use $observe becomes more apparent: your linking function may want to do something each time the value of myTitle changes.
This is intentional and has to do with compilation order and the difference between '#' and '='.
Some excerpts from this Google Groups discussion with input from Misko:
# and = do very different things. One copies the attribute value
(which may be interpolated) and the other treats the attribute value
as an expression.
#attrs are not $interpolated until later, so they are not available at
link time. If you want to do something with them in the link function
you either need to $interpolate yourself manually
well, none of the answers above actually mentioned one key aspect, even with '=', it doesn't seem to me you can access the scope inside link function directly like the following,
scope: {
data: '=',
},
link: function(scope, elem, attrs) {
console.debug(scope.data); // undefined
but you can access the scope in the internal function,
link: function(scope, elem, attrs) {
scope.saveComment = function() {
console.debug(scope.data);
so it seems to me there might be a time lag in when scope.data can be available.
You can visit the JSFiddle created by me here: http://jsfiddle.net/7t984sf9/5/:
link: function($scope, $elem, $attr) {
console.log($scope.onewayObject);
$attr.$observe('onewayObject', function(value) {
console.log(value);
});
}
Or some more detailed explanation here: What is the difference between & vs # and = in angularJS