How to use binding in directive? - angularjs

In my ng-repeat I have a directive called update which takes the current index of repeat. How can I pass index value to my directive ?
This is how my markup looks like:
<ul>
<li ng-repeat="article in articles">
<span class="btn" update="{{ $index }}">update</span>
<span class="btn" ng-attr-update="{{ $index }}">update</span>
</li>
</ul>
Directive:
angular.module('magazine')
.directive('update', function() {
return {
restrict: 'A', // restricting it only to attributes
link: function(scope, element, attrs) {
console.log(attrs.update);
}
}
})
When I run the above code it logs undefined. Angular docs on directive (http://docs.angularjs.org/guide/directive) says that using ng-attr as a prefix to an attribute does the job. But its not happening. I checked my DOM, when I use ng-attr-<directive>="{{ $index }}" as a prefix to an attribute it does not compile it to only <directive>="value".
Is there something I am missing or doing it wrong ?

Related

different templates and objects in ng-repeat

I need to implement a feed with ng-repeat. The feed items have to be sorted by recency. However, I can't possibly use the same template for each item, because the events and behaviors are different. Would using ng-if to render the feed events like this make sense?
<ul ng-controller="FeedController as FeedCtrl">
<li ng-repeat="feedItem in FeedCtrl.feedFactory.feedItems | orderBy '-date_created'">
<div feed-comment-liked ng-if="feedItem.type == 'comment_liked'" item-info ="feedItem">
<div feed-comment-reply ng-if="feedItem.type == 'comment_reply'" item-info ="feedItem">
<div feed-friend-request ng-if="feedItem.type == 'friend-request'" item-info ="feedItem">
</li>
</ul>
This would of course necessitate a number of directives called feedCommentLiked, feedCommentReply, and feedFriendRequest, each with their own template, and getting their data through an item-info binding.
I think this is ok, but I'm hoping others have been down this road. The most obvious constraint in why I don't include multiple ng-repeat lists is that they need to be ordered by the same "date_created" attribute.
You can use thin directive approach so you would have one directive.
<ul ng-controller="FeedController as FeedCtrl">
<li ng-repeat="feedItem in FeedCtrl.feedFactory.feedItems | orderBy '-date_created'">
<div thin-directive="{{myConfig[feedItem.type]}}" item-info ="feedItem">
</li>
</ul>
$scope.myConfig = {
comment_liked: 'template url here',
comment_reply: 'template url here',
friend-request: 'template url here',
}
-----
.directive('thinDirective', function($compile,$templateRequest) {
return {
restrict: 'A',
scope: false,
compile: function(element, attrs) {
return function (scope, element, attrs) {
$templateRequest(attrs.thinDirective).then(function(html){
var template = angular.element(html);
element.append(template);
$compile(template)(scope);
});
}
}
};
or you can use ng-include instead of thin directive but it will create N subscopes. Your call
<ul ng-controller="FeedController as FeedCtrl">
<li ng-repeat="feedItem in FeedCtrl.feedFactory.feedItems | orderBy '-date_created'">
<div ng-include="myConfig[feedItem.type]" item-info ="feedItem">
</li>
</ul>

How can I define additionally binding HTML in an AngularJS directive?

My main view makes use of a directive called fileUploader. The fileUploader directive uses an ng-repeat to represent a list of objects (file details). I want this file-uploader directive to be reusable, so I wish to place use-specific UI in the view that is using it.
Here is my main view. In this case I want to see the standard file-uploader's template for each item plus two additional pieces of meta data I will find on the object (reference / destinationFolder).
<file-uploader class="col-md-10 file-uploader" file-uploader-upload-url="/x/upload">
<dl>
<dt>Reference</dt>
<!-- Note the binding on the next line, how do I evaluate it to the "item" in the directive's ng-repeat? -->
<dd>{{ item.reference }}</dd>
<dt>Folder</dt>
<dd>{{ item.destinationFolder }}</dd>
</dl>
</file-uploader>
My directive uses ng-transclude to include the <dl> contents above. It seems the {{ item.reference }} is evaluated in the main view and then inserted many times, what I want is for it to transclude it as-is and then evaluate the expression within the context of the directive's ng-repeat. The transclude etc is working correctly, but the binding is not working as I wish.
<ul class="file-upload-list">
<li ng-repeat="item in controller.fileUploader.queue">
<div class="file-upload-item" drag-container drag-data="controller.fileUploader.queue[$index]">
<div class="file-upload-icon">
<img src="/Icons/FileExtension/{{item.file.name | fileExtension}}" alt="Icon" class="file-upload-icon" />
</div>
<div class="file-upload-filename">
<a href="" title="{{ item.file.name }}">
{{ item.file.name | limitTo : -28 }}
</a>
</div>
<!-- Here is where I want the main view's template repeated and data-bound -->
<ng-transclude class="file-upload-meta-data"/>
<div class="progress">
<div class="progress-bar" role="progressbar" ng-style="{ 'width': item.progress + '%' }"></div>
</div>
</div>
</li>
</ul>
Sadly, this is not how ng-transclude transclusion works. What you are trying to do is create a container directive that makes use of its children as a "template" of what to stamp out inside your own directive.
The content that is transcluded is by definition bound to the scope of the place where the directive is instantiated; not to the scope of the directive's template.
So actually you don't really need to use transclusion here as what you really trying to do is simply inject the inner HTML into your own template. You can do this in the compile function like this:
app.directive('test', function(){
return {
restrict: 'E',
compile: function compile(tElement, tAttrs, tTransclude) {
// Extract the children from this instance of the directive
var children = tElement.children();
// Wrap the chidren in our template
var template = angular.element('<div ng-repeat="item in collection"></div>');
template.append(children);
// Append this new template to our compile element
tElement.html('');
tElement.append(template);
return {
pre: function preLink(scope, iElement, iAttrs, crtl, transclude) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
}
};
}
};
});
-- Angular Issue #7874: ng-repeat problem with transclude
The solution was to use an ng-include inside the directive.
First I added an additional attribute on my directive in which the developer can specify the ID of the template, like so:
<file-uploader file-uploader-metadata-template-id="someID" class="col-md-10 file-uploader" file-uploader-upload-url="/x/upload">
Then inside the directive I just needed to use ng-include
<div class="file-upload-meta-data" ng-include="controller.fileUploaderMetadataTemplateId"/>
I can then use the directive like so
<file-uploader class="col-md-10 file-uploader" file-uploader-upload-url="/x/upload" file-uploader-metadata-template-id="file-meta-template"/>
<script type="text/ng-template" id="file-meta-template">
<dl>
<dt>Application</dt>
<dd>{{ item.meta.applicationName }} </dd>
<dt>Reference</dt>
<dd>{{ item.meta.referenceName }} </dd>
<dt>Folder</dt>
<dd>{{ item.meta.targetFolderName }} </dd>
</dl>
</script>
Note that I am binding to a controller, with controllerAs set to "controller" - here is the typescript for those interested....
module MyApp.Directives.FileUploaderDirective {
export class FileUploaderDirectiveController {
public uploadUrl: string;
public metaTemplateId: string;
}
class FileUploaderDirective implements angular.IDirective {
public restrict: string = "E";
public templateUrl: string = "/app/Directives/FileUploaderDirective/FileUploaderDirective.html";
public scope: any = {
uploadUrl: "#fileUploaderUploadUrl",
metaTemplateId : "#fileUploaderMetadataTemplateId"
};
public controller: string = "MyApp.Directives.FileUploaderDirective.FileUploaderDirectiveController";
public controllerAs: string = "controller";
public bindToController: boolean = true;
public transclude: boolean = true;
public replace: boolean = true;
}
export function register(app: angular.IModule) {
app.controller("MyApp.Directives.FileUploaderDirective.FileUploaderDirectiveController", FileUploaderDirectiveController);
app.directive("fileUploader", () => new FileUploaderDirective());
}
}

AngularJS directive for accessing ng-repeat scope of child

Okay, so let me start off saying that I have a directive that creates a JQueryUI tab setup with a backing field that populate the tab name and the tab contents basically.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}">
{{category.Content}}
</div>
</div>
So as you can see, I have a <ul> with an ng-repeat in the <li>.
As I will be applying JQueryUI Tab function to the parent container id="summaryTabPanel", I need to wait until all the dom is rendered.
So I do some research, and find out I need to create a directive that kind of looks like the following:
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
scope.$watch("repeatDone", function () {
if (scope.$last) {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
}
;
});
}
};
});
This watches to see if the last repeated item is found, and then applies the DOM changes via the statement $("#summaryTabPanel").tabs()
This is all fine and good, but it only works if I apply the directive to the last ng-repeated child item of the summaryTabPanel container.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}" repeat-done>
{{category.Content}}
</div>
</div>
If I move it to the previous ng-repeat item things work but not quite right. If I move it to the parent container, things don't work at all.
It seems wrong to me to apply this directive to the last child, and instead have a way to apply it to the root and somehow accomplish the same thing.
<div id="summaryTabPanel" repeat-done>
Is this possible somehow with Angular?
According to this link you should write your directive like this
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
$timeout( function () {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
});
}
});
Please have a look at the jsfiddle I created to illustrate your case.

contenteditable in Angular js

I am using contenteditable directive of angular js. and i try to access value of a tag using ng-model.but it gives undefined.
This is my html where i use contenteditable directive
<span ng-switch on="editgroupChatFieldFlag" style="width: 87%;margin-right: 0;float:left;" ng-hide="displatSessionTitleFlag==false">
<h5 style="float:left;margin: 7px 14px 0;margin-right: 0;">Chat Session : </h5>
<a ng-switch-when="true" contenteditable="true" strip-br="true" ng-model="chatMessageName" style="margin-right: 0;width: 59%;margin-left: 4px;" ng-keydown="changeNameFunc($event)">{{secondPresonName}}</a>
<a ng-switch-when="false" style="margin-right: 0;width: 59%;margin-left: 4px;">{{secondPresonName}}</a>
</span>
and this contenteditable directive
app.directive('contenteditable', function() {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
if(!ngModel) return; // do nothing if no ng-model
// Specify how UI should be updated
ngModel.$render = function() {
element.html(ngModel.$viewValue || '');
};
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$apply(read);
});
read(); // initialize
// Write data to the model
function read() {
var html = element.html();
// When we clear the content editable the browser leaves a <br> behind
// If strip-br attribute is provided then we strip this out
if( attrs.stripBr && html == '<br>' ) {
html = '';
}
ngModel.$setViewValue(html);
}
}
};
});
can anyone tell me how i get a tag value using ng-model.
You are dealing with a scope issue (ng-switch creates a child scope). You need to either use . or $parent.
Example with $parent update ng-model to use $parent.chatMessageName:
<a ng-switch-default contenteditable="true"
strip-br="true"
ng-model="$parent.chatMessageName" >Enter</a>
Example with dot/object:
JS
--controller
$scope.myType = {
chatMessageName: ''
};
Html:
<!-- html -->
<a ng-switch-default
contenteditable="true"
strip-br="true"
ng-model="myType.chatMessageName" >Enter</a>
<a>{{"Text "+ myType.chatMessageName}}</a>
Educate yourself: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Side Note: when you are posting your question: include the fiddle in your question (not the comment) and remove unnecessary code, and format so its more readable.

angular js directive - How to evaluate angular tags of directive's template html

I have a directive whom's behavior I want to control using attributes. It's a navbar that should should have an active item depending on the value of an attribute
<navbar active="programs"></navbar>
The directive's template
<div class="navbar span12">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="#">Loopz</a>
<ul class="nav">
<li ng-class="{active: isActive('programs')}">Programs</li>
<li ng-class="{active: isActive('shop')}">Shop</li>
<li ng-class="{active: isActive('profile')}">Profile</li>
</ul>
</div>
</div>
The active class should be put on the element that has the matching active attribute value. The template should be evaluated and the directive's internal scope should have a function that matches the active attribute's value with the value passed to the directive's scope method isActive(value)
The directive
directivesModule.directive('navbar', function(){
return {
restrict: "E",
templateUrl: "partials/navbar.html",
replace: true,
controller: function($scope, $element, $attrs){
$scope.isActive = function(value){
return $attrs.active === value;
}
}
}
});
The isActive function is being called with the correct value but the $attrs object doesn't contain the active attribute's value.
I just dropped all your code into a jsFiddle (it'd be helpful if you did it next time) and everything seems to work: http://jsfiddle.net/rtCP3/110/
Here's the output of the li that should be active:
<li ng-class="{active: isActive('programs')}" class="active">
Programs
</li>

Resources