I have an element which its whole content could be transcluded. the transclusion is optional, so I can let its inner parts stay but an inner element be transcluded. Let's see it in action:
<h4 class="modal-title" ng-transclude="title">
Are you sure you want to remove this <span ng-transclude="innerType">good</span>
</h4>
In directive definition I have:
myApp.directive('confirmDeleteModal',function(){
return {
restrict:'E',
transclude: {
title:'?modalTitle',
innerType:'?modalType',
},
templateUrl:'templates/confirm_delete_modal.html',
scope: {
obj:'=info',
},
}
});
The HTML code would be so:
<confirm-delete-modal info="goodToBeDeleted">
<modal-type>good</modal-type>
</confirm-delete-modal>
When I run my code, I get the following error: [ngTransclude:orphan].
What should I do?
I am using AngularJS v1.5.8
You use another directive ng-transclude in a fall back content of modal-title. But the ng-transclude become the "parent" of the span and doesn't provide transclusion function. I suggest to you to modify your directive and to use the method isSlotFilled to know if title is filled or not :
directive('confirmDeleteModal',function(){
return {
restrict:'E',
transclude: {
title:'?modalTitle',
innerType:'?modalType',
},
link: function(scope, elem, attrs, ctrl, trfn) {
scope.titleFilled = trfn.isSlotFilled('title');
},
template:'<h4 class="modal-title" ng-transclude="title" ng-if="titleFilled"></h4>' +
'<h4 class="modal-title" ng-if="!titleFilled">Are you sure you want to remove this <span ng-transclude="innerType">good</span></h4>',
scope:{
obj:'=info',
}
}
})
(https://plnkr.co/edit/k0RXLWbOvHdNc9WFpslz?p=preview)
Related
I want to create a directive like:
<div ng-refreshing-when="x">Some Content In Here</div>
so when x=true, it will do some css and fade a spinner div over top, then when x=false, the spinner will fade out. I want to append a div (which is hidden by css) to the parent div, NOT replace the content. My current code is here, but the ngRefreshingWhen isn't triggering the ng-if:
http://plnkr.co/edit/griUYR6RY46x5DlqVHWU?p=preview
I set:
$scope.refreshing = false;
so the "refreshing" text shouldn't be there...
How can I accomplish this?
You should be able to use transclude and put the HTML in the template:...
angular.module('ngRefreshingWhen',[])
.directive('ngRefreshingWhen', function ($compile) {
return {
restrict: 'A',
transclude: true,
template: '<div class="refresher" ng-if="ngRefreshingWhen"><p>Refreshing</p></div><div ng-if="!ngRefreshingWhen" ng-transclude></div>',
scope: {
ngRefreshingWhen: '='
}
};
});
If the HTML is more complicated, put it in a separate file and use templateUrl.
http://plnkr.co/edit/28Eq0HARKF0KXpYJwBTS?p=preview
<div> <!-- Parent Div -->
<div ng-refreshing-when="refreshing"></div>
<p>Some Stuff I Want to Keep</p>
</div>
&
angular.module('ngRefreshingWhen', [])
.directive('ngRefreshingWhen', function($compile) {
return {
restrict: 'A',
scope: {
ngRefreshingWhen: '='
},
template: '<div class="refresher" ng-if="ngRefreshingWhen"><i class="fa fa-spin fa-refresh"></i></div>',
link: function(scope, element, attrs) {
// add has-refresher class
element.parent().addClass('has-refresher');
}
};
});
http://plnkr.co/edit/xRgqqOfz9IuskL1kn057?p=preview
I did it differently, I added directive inside, then added the needed class to it's parent, same result, just added an element instead of trying to add an attribute to the parent.
I am using directive and template. My template is as follows
<div>
<a data-my-dir="item" data-attr1="true"
data-attr2="{{itemParentId}}"
data-attr3="{{item.id}}">
</a>
</div>
Here due to curly braces watches are added and it is affecting my performance. I don't want watches as I am not going to modify attr2 or attr3.
I want to directly resolved value here only.
We can use bindonce directive where we don't want watches where we can use bo-text="xyz" instead, but here I want to pass values as attr to my custom directive.
Inside my directive's link function I am binding click event with element as follows
link: function (scope, element, attrs) {
element.bind('click', function (event) {
var myAttr1 = attrs.attr1;
var myAttr2 = attrs.attr2;
}
}
So due to those watches in template on attr1 and attr2 I am getting these values resolved in click event.
What are the alternatives?
One time Binding
Seems like a good use case for one time binding (if you are using angular 1.3+)
<div>
<a data-my-dir="item"
data-attr1="true"
data-attr2="{{::itemParentId}}"
data-attr3="{{::item.id}}">
</a>
</div>
the directive would look like
angular.module('app', [])
.directive("myDir", function() {
return {
restrict: "A",
scope: {
"attr1": "#",
"attr2": "#",
"attr3": "#",
},
template: '<span> {{attr1}} {{attr2}} {{attr3}}</span>'
};
})
Demo
http://plnkr.co/edit/GJCZmb9CTknZZbcRHN7s?p=preview
You could use data-attr2="itemParentId" directly but for that you need to use = as currently you are using # option of directive.
app.directive('myDir', function(){
return {
scope: {
dataAttr1: '=', //or '=dataAttr1'
dataAttr2: '=' //or '=dataAttr2'
},
link: function(scope, element, attrs){
console.log(scope.dataAttr1);
console.log(scope.dataAttr2);
}
}
})
I'm trying to assign a random string to ngModel. But I can't even seem to assign a regular string to it. In the code below, I'm trying to change the ngModel to "new", but in chrome, it's still showing me that the ngModel is "placeholder". What am I doing wrong?
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
template: '<div ng-model="placeholder"></div>',
controller: function ($scope, $element) {
$scope.placeholder = "new";
}
}});
The plain div tag was just an example. What I actually have is a content editable div that I've bound to a textarea with a contenteditable directive. I made a button to allow me to add as many of these directives as I want, but when I add this directive, I'd like a new ng-model for each one, because that's what I'm using to save the content of each content editable div to a file.
This is the full example in case it might help someone else. I put the addPage directive to a button, which, when clicked, appends a new content editable div (which I'm calling a page). There is one more directive that I didn't include (contenteditable) because I got it from the bottom of the docs over here
app.directive("addPage", function($compile){
return function(scope, element, attrs){
element.bind("click", function(){ angular.element(document.getElementById('container')).append($compile('<page></page>')(scope));
});
};
});
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
// template: '<div class="page" contenteditable strip-br="true" ng-model="chapter"></div>',
template: function (elem, attr) {
var test="chapter.two"; //in reality, I will generate a random string to keep these divs unique
return '<div class="page" contenteditable strip-br="true" ng-model=' + test +'></div>'
}
}});
In my controller, I have this object that is used to store all the ng-model content
$scope.chapter = {};
I think you are approaching this a bit backwards. Starting with the model, you'd need an array to keep the values from all of the inputs / contenteditables.
.controller("MainCtrl", function($scope){
$scope.data = [{v: ""}]; // first element
$scope.addContentEditable = function(){
$scope.data.push({v: ""});
};
})
Then, you can bind to each element of that array easily, and add elements at will:
<button ng-click="addContentEditable()">Add</button>
<div ng-repeat="item in data" ng-model="item.v" contenteditable></div>
I'm not sure exactly how the page directive needs to be used here, but one way or another, the approach is the same as above.
If you just want to pass a string in you could use attributes and template(fn)
HTML
<div page="new">
Directive
app.directive("page", function () {
return {
restrict: 'E',
replace: true,
template: function (elem, attr) {
return '<div ng-model="' + attr.page + '"></div>';
}
}
});
If I have something like this
<div ng-class="{'breakfast': meal == 'eggs'}"></div>
Now I need to create a directive that would add the same thing, so doing this:
restrict: 'C'
scope:
meal:"&"
link:(scope, element, attr)->
element.attr('ng-class', "{'breakfast': meal == 'eggs'}")
adds the attribute to the DOM, but then nothing happens, when meal == 'eggs' it doesn't add breakfast class to the element. What am I doing wrong?
ps. I can of course do it with element.addClass and element.removeClass but then the animation would only work when I add the class, but wouldn't for the removal (if I have css styles like .breakfast-add, .breakfast-remove etc.
Instead of modifying the element's attributes at link or compile time, you can use ng-class in the directive's template and use a scope variable with your classes. It works for me in the following example, and it seems to be cleaner:
HTML:
<div ng-app="app">
<div class="meal: eggs;">xxx</div>
</div>
JS:
angular.module('app', [])
.directive('meal', function () {
return {
template: '<div ng-class="classes" ng-transclude></div>',
transclude: true,
restrict: 'C',
scope: {
meal: '#'
},
link: function (scope) {
scope.classes = {
breakfast: (scope.meal == 'eggs')
};
}
};
});
i have html markup that i need to use at many places so rather to copy paste it again and again but with different Headings
i thought to create a directive it looks like this
myModule.directive('row', function ($compile) {
return {
restrict: 'E',
template: '<div class="row">'+
'<div class="col-md-10 margin-top-10px font-18px">{{heading}}</div>'+
' <div class="col-md-2">'+
'<div class="margin-top-10px"><span ng-click="close()" class="close helvetica color-black">×</span></div>'+
'</div>'+
'</div>',
replace: true,
link: function (scope, element, attrs, ctrl) {
var test = attrs.heading;
}
};
});
and how i m trying to use it
<row heading="its my heading "></row>
i cant make it work .i dont want to create isolated scope or child scope i just wanted to pass value from attribute and when template gets replaced i want its interpolated value .
can some one help me how can id o this
Try with:
link: function (scope, element, attrs, ctrl) {
scope.heading = attrs.heading;
}
It might also be a good idea to set
scope: true
You have to actually attach the value from the attr object to the scope before it can be used in the template.
add
scope: {
heading: '='
}
to your directive's definition to create 2 way data-binding with that attribute. you would do this if you are passing in a scope variable.
or use #
scope: {
heading: '#'
}
if you are just passing in a string.
minimalist plunkr to demonstrate: http://plnkr.co/edit/U5OzlMiXaIsHEdb5gmRT