I'm doing my first steps in AngularJS directives. Just set up this one as an exercise to output a product name:
.directive('productname', function (Prefs) {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: '/partials/productname.html',
link: function (scope, element, attrs) {
scope.brand = Prefs.owner;
}
}
})
productname.html
<span class="productname"><span class="brand">{{brand}}</span> <span class="product" ng-transclude>{{productname}}</span></span>
and so the usage is plainly: <productname>{{product.name}}</productname>
Now, could someone tell me how I should go about making this directive configurable by adding a flag to output the productname linked?
The usage would be: <productname linked>{{product.name}}</productname> and the output/template would be:
<span class="productname"> <span class="brand">{{brand}}</span> <span class="product" ng-transclude>{{productname}}</span></span>
Seems complicated and I cannot quite figure out where the logic should be injected...
First of all, you should use the scope property of the directive declaration. Also, you do not need transclude in this case. Like so:
.directive('productname', function (Prefs) {
return {
restrict: 'E',
scope: {
product: "=",
linked: "="
},
replace: true,
templateUrl: '/partials/productname.html',
link: function (scope, element, attrs) {
// scope.product and scope.linked are automatically set
scope.brand = Prefs.owner;
}
}
})
And the template:
<span class="productname" ng-switch="linked">
<a href="/edit/{{id}}" ng-switch-when="true">
<span class="brand">{{brand}}</span>
<span class="product">{{product.name}}</span>
</a>
<span ng-switch-default>
<span class="brand">{{brand}}</span>
<span class="product">{{product.name}}</span>
</span>
</span>
Call the template like so:
<productname product="product"></productname>
or:
<productname product="product" linked="'true'"></productname>
Update
If you want to use the linked attribute as a flag, you could do that by using the attrs variable:
.directive('productname', function (Prefs) {
return {
restrict: 'E',
scope: {
product: "="
},
replace: true,
templateUrl: '/partials/productname.html',
link: function (scope, element, attrs) {
// scope.product is automatically set
scope.linked = typeof attrs.linked != 'undefined';
scope.brand = Prefs.owner;
}
}
})
Call it like so:
<productname product="product" linked></productname>
Related
I am trying to create a reusable html element / angular directive that will be used inside of ng-repeat so I want to pass it the values it will display in the DOM.
Something worth noting, I don't care for the values to bind. They can be a one-time binding simply displaying their values the first time ng-repeat creates them.
For example here is my directive:
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
templateUrl: '../Pages/Views/Templates/newsListing.html',
scope: {},
link: function (scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});
My HTML template:
<div>
<span class="glyphicon glyphicon-list-alt logo-green"></span>
<label>{{DateValue}}</label>
<label>{{Category}}</label>
<label class="noBorder">{{Content}}</label>
What I want the ending product to be:
<news-Listing Date="{{someValue}}" Category="{{someValue}}" Content="{{someValue}}"></news-Listing>
I have never created a directive before and all the guides I am trying to follow don't explain the scope, and the way binding happens inside of a directive.
Try like this
var app = angular.module('anApp', []);
app.controller('aCtrl', function($scope) {
$scope.data = [{date:"2000-12-12",category:"sport",content:"this is a test"}]
});
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
template: '<div><label>{{date}}</label><p>{{category}}</p><p>{{content}}</p></div>',
scope: {
date:'#',
category:'#',
content:'#'
},
link: function (scope, elem, attrs) {
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="anApp" ng-controller="aCtrl">
<div ng-repeat="d in data">
<news-listing date="{{d.date}}" category="{{d.category}}" content="{{d.content}}" ></news-listing>
</div>
</div>
Here is a working example of what you want: https://jsfiddle.net/jonva/kuk3pbbz/
.directive('newsListing', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<div> < span class = "glyphicon glyphicon-list-alt logo-green" > < /span> < label > {{dateValue}} < /label> < label > {{category}} < /label> < label class = "noBorder" > {{content}} < /label>',
scope: {
dateValue: '#',
content: '#',
category: '#',
},
link: function(scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});
I have a directive that displays an icon with a little cross. When the user clicks on this cross, a callback should be called.
Here's the code of the directive template:
<div class="item" title="{{name}}">
<button type="button" class="close">
<span ng-click="onDelete()">×</span>
</button>
<span class="glyphicon glyphicon-folder-open"></span>
</div>
The Javascript of the directive:
angular.module('hiStack').directive('hsItem', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'item.tpl.html',
scope: {
onDelete: '&',
title: '#'
}
};
});
Finally, the code that uses the directive:
<hs-item on-delete="deleteGroupModal = true" title="TestTitle"></hs-item>
deleteGroupModal = true is never called when I click on the cross. If I pass a function instead like deleteGroup(), it works.
How can I pass an assignment like we usually do with ng-click for example?
Thank you.
As Igor Janković said, it's better to pass a function than to write it directly on the attribute.
That said, it's possible to eval the expression passed on the attribute like this:
angular.module('hiStack').directive('hsItem', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'item.tpl.html',
scope: {
title: '#'
},
link: function(scope, element, attrs) {
scope.onDelete = function() {
// Eval the code on the parent scope because directive's scope is isolated in this case
if (attrs.onDelete) scope.$parent.$eval(attrs.onDelete);
}
}
};
});
This works:
<p class="form-control-static">{{ctrl.StatusId}}</p>
<div content-selects ng-model="Statuses" selection="2"></div>
This doesn't
<p class="form-control-static">{{ctrl.StatusId}}</p>
<div content-selects ng-model="Statuses" selection="ctrl.StatusId"></div>
In case one, {{ctrl.StatusId}} shows 2 and my directive works perfectly.
In case two, {{ctrl.StatusId}} also shows 2 and my directive doesn't work.
function contentSelects(){
return {
restrict: 'AE',
templateUrl: '/app/Directives/contentSelects.tpl.html',
replace:true,
scope: {
ngModel: '=',
selection: '='
},
controller:function($scope){
},
link: function (scope, element, attrs) {
scope.selectedModel = scope.ngModel[scope.selection];
console.log(scope.selectedModel);
scope.isChanged = function () {
//console.log("changed");
}
}
};
If I use curly brackets it breaks selection="{{ctrl.StatusId}}". What should I give this attribute so that it recognises the value that's passed?
Having this HTML :
<div messages>
Some content...
</div>
This directive :
myAppDirectives.directive("messages", function () {
return {
restrict: 'A',
templateUrl: '/directives/messages/messages.html',
link: function (scope, elem, attrs) {
//...
}
};
});
And messages.html :
<p>message1</p>
<p>message2</p>
The content of my html gets replaced by the directive template but i'd like it to just be appended.
Html becomes this :
<div messages>
<p>message1</p>
<p>message2</p>
</div>
But I'd like this :
<div messages>
<p>message1</p>
<p>message2</p>
Some content...
</div>
Is this possible without using ng-include?
This looks like a perfect situation to use ngTransclude.
Try:
myAppDirectives.directive("messages", function () {
return {
restrict: 'A',
transclude: true,
templateUrl: '/directives/messages/messages.html',
link: function (scope, elem, attrs) {
//...
}
};
});
messages.html:
<p>message1</p>
<p>message2</p>
<ng-transclude></ng-transclude>
If you really want this behaviour, use transclusion:
myAppDirectives.directive("messages", function () {
return {
restrict: 'A',
transclude: true,
templateUrl: '/directives/messages/messages.html',
link: function (scope, elem, attrs) {
//...
}
};
});
And the template becomes:
<p>message1</p>
<p>message2</p>
<span ng-transclude></span>
The original content of the <div messages> element will be wrapped in the <span ng-transclude>, but this should not harm.
I want to know how to invoke a function residing in controller on the ng-click event of template element. I have to use this directive in many pages.Hence I need to handle the click event in respective controller pages.The below code invokes the click function (moreitemdetails) residing within the directive.I tried setting the scope as moreitemdetails: '=' . It is also not working.
I have been using the directive
app.directive('groceryList', function){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};
});
The call for directive is
<grocery-list array="items"></grocery-list>
This is the template file
<div ng-click="listItemClick($event)">
<div>
<div class="item">
<span class="item-details">
{{array[0].Item}}
</span>
<span class="down-arrow"></span>
</div>
<div class="next-items">
<ul>
<li class="item" ng-repeat="list in array">
<div class="item-details" ng-click="moreitemdetails(list.Name,list.Type)">{{list.Item}}</div>
</li>
</ul>
</div>
Is there a way to get around?
I also would like to know the use of $location within another directive. Quoting the previous example (everythin is same except the directive definition and action in moreitemdetails() )
app.ui.directive('groceryList', ['$location', function(location){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
$location.path('/home/');
}
}
};
}]);
Thanks in advance
So by declaring
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
you are creating an isolated scope for your directive. One solution might be to not declare this scope in your directive. This would mean that your ng-click="moreitemdetails(list.Name,list.Type) would trigger the function on your controllers scope.
Alternatively you could use an emit and listener. To do this, in your directive you could call:
scope.moreitemdetails=function(name,type){
var deets = {
name: name,
type: type
};
scope.emit('moreitemdetails',deets)
}
Then in your various controllers you implement:
scope.$on('moreitemdetails',function(event,details){
// do some code with your name and type
}
I'm not sure exactly what you would like to know about $location, if you could be a bit more specific I might be able to help a more.
Hope this helps in some way!
EDIT:
The directive without any scope declared would look like this:
return {
restrict: 'E',
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};