So I'm struggling with passing the scope to my custom directives.
Here's my html
<li ng-repeat="post in posts | filter:search | orderBy:sortField:reverse" class="archives-listing" ng-class="{'last-border':$last}">
<archive-notes></archive-notes>
</li>
Here's my directives
app.directive('archiveNotes', function(){
return {
restrict: 'E',
transclude: true,
scope: {
notes: '#',
paths: '#'
},
controller: function($scope) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/notes.html'
}
})
app.directive('archiveFolders', function(){
return {
require: '^archiveNotes',
restrict: 'E',
transclude: true,
scope: {
path: '#'
},
link: function(scope, element, attrs) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/folders.html'
}
});
here are my templates.
notes.html
<div ng-class="{'found' : find(post.paths[$index], search)}" class="arch-notes">
<div ng-bind-html="is_NotesEmpty(post.notes)">{{post.notes}}</div>
<archive-folders></archive-folders>
</div>
folders.html
<div ng-repeat="path in post.paths | filter:search track by $index" ng-transclude>
<span class="arch-paths">{{path}}</span>
</div>
I left several things blank i.e controller and link because at this point i was just trying to figure how to make everything show up first before i start manipulating the DOM
I followed the example in the angularjs documentation, and it got me this far. I guess cant seem to figure out how to access the scope?
Any help is appreciated.
Based on your templates, it seems that your archiveNotes directive definition should actually look like this:
app.directive('archiveNotes', function(){
return {
...
scope: {
post: '='
},
...
}
})
To get the post variable passed in from ng-repeat's scope, you also need to set the post attribute on the directive's element:
<archive-notes post="post"></archive-notes>
Similarly, you need to set it on the child directive:
app.directive('archiveFolders', function(){
return {
...
scope: {
post: '='
},
...
}
});
... and change your notes.html template:
<archive-folders post="post"></archive-folders>
Isolate scope is sort of like a firewall, where you can set exceptions, in this case on specific scope variables. All we're doing here is setting those exceptions in the directive definitions, then passing them through using the attribute on the elements.
These videos by John Lindquist really shed some light on isolate scope for me:
Understanding Isolate Scope
Isolate Scope '#'
Isolate Scope '='
Isolate Scope '&'
Related
I have 3 directive with isolate scope and share scope and I want pass a function beteween outermost a innermost directive. The outer and middle has isolate scopes and the middle with inner share the scope. Any suggest ?
Pass the functions of my controller as shown below .
<outer on-edit="helloWorld" ng-model="model" ng-repeat="items in items.objects" ></outer>
In my controller:
$scope.helloWorld = function(){
alert('Hello world');
}
My directive:
angular.module('myApp')
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
scope: {
item: "=ngModel",
onEdit: '&'
},
template: '<div><middle on-edit='onEdit'></middle></div>',
controller : function($scope){
$scope.edit = function(){
$scope.onEdit()();
}
}
};
})
.directive('middle', function () {
return {
restrict: 'E',
replace: true,
scope: {
item : '=ngModel',
onEdit : '&'
},
templateUrl: '<div><inner on-edit='onEdit'></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
template: '<div><a ng-click='edit()'>Edit</a></div>'
};
})
And this not work, any ideas?
Thanks
This looks a bad design though, but in the middle directive's template you are using inner directive as follows:
<div><inner on-edit='onEdit'></inner></div>
If you look at it, inner directive has no scope, so the attribute on-edit doesn't make sense there.
If you want to use any method that is present in middle directive can be directly used in inner directive because of shared scope. Think of inner directive as a part of html written in some other html file which will be replaced at run time.
So anything you pass to middle directive is implicitly passed to inner.
Hi i have created a directive and it does not pass back the correct message.
The directive is used to pass tool-tips back to the html page
this is what the html looks like
<info-text info-msg="Adding another applicant might help you to get approved."></info-text>
below is the directive
(function(){
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '&infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoText}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
}());
the message i get rendered on the page is as follows (it does send the glyphicon):
{{infoText}}
Any ideas,
thanks. Kieran.
You should not use & for this sort of binding, basically it is used for expression binding. I think one way binding (#) is efficient for what you are doing.
Also you should change directive template {{infoText}} to {{infoMessage}}
Markup
<info-text
info-msg="{{'Adding another applicant might help you to get approved.'}}"></info-text>
Directive
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '#infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoMessage}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
And making more cleaner and readable html you could place that string into some scope variable and pass that scope variable in info-msg attribute
I created a directive which has a directive inside it, and when I try to give it parameters I get this message: This error message
Here is my directive
app.directive('percentageSquare', function(){
return {
restrict: 'E',
scope: {
color: '#',
chartConfig: '#'
},
templateUrl: '/templates/dashboard/charts/PercentageChart.html'
};
});
Which loads this template:
<div class="drop-shadow">
<highchart id="{{chartConfig}}" config="{{chartConfig}}" style="background-color: {{color}};" ng-show="true"></highchart>
</div>
Here is how I am calling it:
<percentage-square color="yellow" chart-config="chartBounceRate"></percentage-square>
I am not sure what I can do to fix it, because it look just fine to me...
The way highcharts works, it want to have config to be an object from scope. so you should use two way binding here
Directive
scope: {
color: '#',
chartConfig: '=', //<--two way binding here
selector: '#'
},
Directive Use
<percentage-square color="'yellow'" chart-config="chartBounceRate" selector="{{selector}}"></percentage-square>
Template
<div class="drop-shadow">
<highchart id="{{selector}}" config="chartConfig"
ng-style="{background-color: color}" ng-show="true">
</highchart>
</div>
EDIT
See this famous stackoverflow question and answer to understand #, & and = in angularjs directive definition
I'm creating a custom directive similar to a list-box. This is my directive definition:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '#',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
Here's how I want to use it in my view:
<search-list items-source="productsSource"
item-template="{{item.Name}} Selling for: {{item.Price}}"
selected-item="selectedProduct" />
Both productsSource and selectedProduct come from the view's scope (they work fine). I want item-template to be taken straight up as text (there is no item object in the scope used by my view).
Inside SearchCtrl I obtain the items to show in my search-list, and then I want to apply that item-template to each item (through the use of the $compile service).
The problem is that inside SearchCtrl $scope.itemTemplate is equal to Selling for: (the {{}} syntax was resolved, not passed as text)
tl;dr
My search.html template looks like this:
<li ng-repeat="item in itemsDataSource" ng-class-odd="'oddRow'" ng-class-even="'evenRow'">
<div class="searchResultsItem" ng-click="onItemSelected(item)">
<span compile="internalItemTemplate"></span>
</div>
</li>
Since itemTemplate is one-way binding, the SearchCtrl will reassign it to internalItemTemplate.
if (typeof $scope.itemTemplate === 'undefined') {
$scope.internalItemTemplate = '{{item}}';
} else {
$scope.internalItemTemplate = $scope.itemTemplate;
}
The compile directive on the span tag was borrowed from: Angular Docs for $compile
It looks like this:
angular.module('Utilities')
.directive('compile', ['$compile',
function ($compile) {
// directive factory creates a link function
return function (scope, element, attrs) {
scope.$watch(
function (scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function (value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}
]);
If I hard-code $scope.internalItemTemplate in SearchCtrl to be '{{item.Name}} Selling for: {{item.Price}}', than it works.
How do I get my directive to allow the passing of {{}} without trying to resolve it?
Changing the scope type from # to = and then surrounding it in quotes worked.
The directive would look like this:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '=',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
and the view would look like this:
<search-list items-source="productsSource"
item-template="'{{item.Name}} Selling for: {{item.Price}}'"
selected-item="selectedProduct" />
I'm trying to access a method in a directive from translcuded content. My HTML looks like:
<typeahead class="typeahead" model="customers" filtered-model="customersfiltered" ng-model="selectedcustomer">
<ul>
<li ng-click="select(customer)" ng-repeat="customer in customersfiltered | filter:filter | limitTo:10">
{{customer.firstname}} {{customer.lastname}}
</li>
</ul>
</typeahead>
And my AngularJS directive:
directive('typeahead', function ($filter) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
model: '=',
filteredModel: '='
},
template: '<div class="typeahead"><form><input type="text" autocomplete="off" class="col-lg-12" ng-model="filter"></form><div ng-transclude></div></div>', //
controller: function($scope){
$scope.filterArray = function(filterString){
$scope.filteredModel= $filter('filter')($scope.model, filterString);
}
$scope.clear = function(){
$scope.filteredModel = [];
}
$scope.$watch('filter', function(){
if($scope.filter) {
$scope.filterArray($scope.filter);
} else {
$scope.clear();
}
});
},
link: function ($scope, $element, $attributes) {
$scope.select = function(customer){
console.log("dwadad");
}
}
}
})
The problem here is that I cannot access the select() function inside the link function() from the ng-click event of the transcluded content (list-element).
Have somebody an idea how to solve this?
Here is a Plunker of the current code: Plunker
I think you can't do that. From the Angular docs:
(...) The advantage of transclusion is that the linking function receives a
transclusion function which is pre-bound to the correct scope. In a
typical setup the widget creates an isolate scope, but the
transclusion is not a child, but a sibling of the isolate scope. This
makes it possible for the widget to have private state, and the
transclusion to be bound to the parent (pre-isolate) scope.
In other words, the scope of the transcluded DOM is a sibling, not a child, of the directive's scope. So you can't access it from there, and I think that's correct. Otherwise you wouldn't be able to bind the transcluded content to the right scope.
You can use $$nextSibling:
link: function($scope) {
$scope.$$nextSibling.select = function(customer) {
alert('used isolated scope using $$nextSibling');
}
},