How to use `replace` of directive definition? - angularjs

In this document: http://docs.angularjs.org/guide/directive , it says that there is a replace configuration for directives:
template - replace the current element with the contents of the HTML. The replacement process migrates all of the attributes / classes from the old element to the new one. See the Creating Components section below for more information.
javascript code
app.directive('myd1', function(){
return {
template: '<span>directive template1</span>',
replace: true
}
});
app.directive('myd2', function(){
return {
template: '<span>directive template2</span>',
replace: false
}
});
html code
<div myd1>
original content should be replaced
</div>
<div myd2>
original content should NOT be replaced
</div>
But the final page is looking like:
directive template1
directive template2
It seems the replace doesn't work. Do I miss anything?
Live demo: http://plnkr.co/edit/rGIgmjO81X2UxJohL4HM?p=preview

You are getting confused with transclude: true, which would append the inner content.
replace: true means that the content of the directive template will replace the element that the directive is declared on, in this case the <div myd1> tag.
http://plnkr.co/edit/k9qSx15fhSZRMwgAIMP4?p=preview
For example without replace:true
<div myd1><span class="replaced" myd1="">directive template1</span></div>
and with replace:true
<span class="replaced" myd1="">directive template1</span>
As you can see in the latter example, the div tag is indeed replaced.

As the documentation states, 'replace' determines whether the current element is replaced by the directive. The other option is whether it is just added to as a child basically. If you look at the source of your plnkr, notice that for the second directive where replace is false that the div tag is still there. For the first directive it is not.
First result:
<span myd1="">directive template1</span>
Second result:
<div myd2=""><span>directive template2</span></div>

Replace [True | False (default)]
Effect
1. Replace the directive element.
Dependency:
1. When replace: true, the template or templateUrl must be required.

Also i got this error if i had the comment in tn top level of template among with the actual root element.
<!-- Just a commented out stuff -->
<div>test of {{value}}</div>

Related

AngularJS optional ng-transclude

I have written a custom directive called 'news' in AngularJS 1.5.
It's layout is as follows:
<div class="row">
<div class="largeText shadow1" ng-transclude="heading"></div>
<div class="mediumText shadow2" ng-transclude="content"></div>
</div>
The JavaScript file of this directive is as follows:
return {
restrict: 'E',
transclude: {
'heading': 'heading',
'content': 'content'
},
scope: {
//Some parameters here
},
templateUrl: '/directives/news.html'
};
As you see, my news directive has two children, called heading and content fields. It can be used as follows:
<news>
<heading>
//Any content goes here
</heading>
<content>
//Any content goes here
</content>
</news>
So far, the directive works fine. I mean, as long as heading and content sections are filled with some content, the directive shows them as expected. However, I am trying to make these transclusion slots not mandatory. Whenever I use the directive as:
<news>
<heading></heading>
</news>
AngularJS throws an error saying that I have not filled the content slot. Is it ever possible to make these slots optional?
I can't really find where it is in the actual documentation, but based on an example I saw, I believe that you can use a ? before the value to make the slot optional.
Example:
transclude: {
'heading': 'heading',
'content': '?content'
}
This comes from the example in angular docs at https://docs.angularjs.org/api/ng/directive/ngTransclude#multi-slot-transclusion. It is in the app.js.
You can also add a default for the cases where the slot is optional, by doing something like this:
<div class="largeText shadow1" ng-transclude="heading">Default stuff for the slot goes here</div>
Edit: actually I found it in the documentation. It says in this section https://docs.angularjs.org/api/ng/service/$compile#transclusion:
If the element selector is prefixed with a ? then that slot is optional.
For example, the transclude object { slotA: '?myCustomElement' } maps <my-custom-element> elements to the slotA slot, which can be accessed via the $transclude function or via the ngTransclude directive.

How to get the attribute value in template html with angularjs

I wrote a directive but I don't know how to get the attribute of the directive in template html, those attributes are used in ngIf to determine which div elements will be displayed, pls take a look at below code snippet.
directive use:
<geo-country-selector type="WGC"></geo-country-selector>
template html:
<div ng-if="type.indexOf('W')>0">
...
</div>
<div ng-if="type.indexOf('G')>0">
...
</div>
I need to get the attribute type's value in the template html. I did lots of research on that, but no luck. Any ideas? Thanks
The directive can be like Anik said :
link : function(scope,element,attr){
...
},
scope: {
type: '#'
}
In your directive add a link function then add attrs value to scope then you can check condition in your template like that
try like this
link : function(scope,element,attr){
scope.type=attr.type
}
There is another way by creating isolate scope like this
scope: {
type: '#'
}

Binding stops working in input within ng-if

For some reason binding doesn't work on an input within ng-if block in a directive
so this, doesn't work:
app.directive 'foo', ->
restrict: 'E'
scope:
type:'='
template: "<input ng-if=\"type === 'string'\" ng-model='filterText'>
<div> {{filterText}} </div>"
<foo type="'string'" />
it works fine outside of directive or without ng-if. Wrapping input inside of a div with ng-if not helping. Is it a bug?
jsbin link
It is caused by the ng-if introducing a new scope combined with the fact that you ng-model "has not dot in it".
This works:
template: "<div ng-init='holder={}'> <input ng-if=\"type === 'string'\" ng-model='holder.filterText'></div>
<div> {{holder.filterText}}</div>"
See the directive info at https://docs.angularjs.org/api/ng/directive/ngIf and notice the text "This directive creates new scope."
For the "dot-in-model", see for example
Does my ng-model really need to have a dot to avoid child $scope problems?
or
https://egghead.io/lessons/angularjs-the-dot
Basically when reading the value it will be read correctly traversing scope prototypes, but when modifying the value it will be written to the very own scope.
Since ng-if creates a new scope, you just need to do this
ng-model='$parent.filterText'
Also, please check this answer.
The <div> tag isn't closed in your example, nor is the ng-if applied to that node.
Try this:
template: "<input ng-model='filterText'>
<div ng-if=\"type === 'string'\"> {{filterText}}"</div>"

How to use the 'replace' feature for custom AngularJS directives?

Why does replace=true or replace=false not have any impact in the code below?
Why isn't the "some existing content" being displayed when replace=false?
Or putting it more humbly, can you kindly explain what is the replace=true/false feature in directives and how to use it?
Example
JS/Angular:
<script>
angular.module('scopes', [])
.controller('Ctrl', function($scope) {
$scope.title = "hello";
})
.directive('myDir', function() {
return {
restrict: 'E',
replace: true,
template: '<div>{{title}}</div>'
};
});
</script>
HTML:
<div ng-controller="Ctrl">
<my-dir><h3>some existing content</h3></my-dir>
</div>
See it in Plunker here:
http://plnkr.co/edit/4ywZGwfsKHLAoGL38vvW?p=preview
When you have replace: true you get the following piece of DOM:
<div ng-controller="Ctrl" class="ng-scope">
<div class="ng-binding">hello</div>
</div>
whereas, with replace: false you get this:
<div ng-controller="Ctrl" class="ng-scope">
<my-dir>
<div class="ng-binding">hello</div>
</my-dir>
</div>
So the replace property in directives refer to whether the element to which the directive is being applied (<my-dir> in that case) should remain (replace: false) and the directive's template should be appended as its child,
OR
the element to which the directive is being applied should be replaced (replace: true) by the directive's template.
In both cases the element's (to which the directive is being applied) children will be lost. If you wanted to perserve the element's original content/children you would have to translude it. The following directive would do it:
.directive('myDir', function() {
return {
restrict: 'E',
replace: false,
transclude: true,
template: '<div>{{title}}<div ng-transclude></div></div>'
};
});
In that case if in the directive's template you have an element (or elements) with attribute ng-transclude, its content will be replaced by the element's (to which the directive is being applied) original content.
See example of translusion http://plnkr.co/edit/2DJQydBjgwj9vExLn3Ik?p=preview
See this to read more about translusion.
replace:true is Deprecated
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636
Update
Note: replace: true is deprecated and not recommended to use, mainly due to the issues listed here. It has been completely removed in the new Angular.
Issues with replace: true
Attribute values are not merged
Directives are not deduplicated before compilation
transclude: element in the replace template root can have unexpected effects
For more information, see
AngularJS $compile Service API Reference - Issues with replace:true

AngularJS : directives nested in a ng-repeat

I have an angularjs web app which has a view with the following basic structure :
<ul>
<li ng-repeat="entry in entries" ng-switch on="entry.type" logic-options >
<div ng-switch-when=1 text-entry></div>
<div ng-switch-when=2 other-entry></div>
</li>
</ul>
Here text-entry and other-entry are directives which have there own templates and logic-options is another directive which has a template and controller.
The logic-options directive provides some functions to be used by the child scope and has replace: false so that it should append its template to the end of the li. The text-entry and other-entry directives simply have some templates to be inserted which are also used on other views.
When I run this the logic-options directive will render and seems to function but the inner directives (text-entry and other-entry) will not.
In the console I get the error :
Error: Argument '?' is required qa#...
What causes this error and how do I correct it?
A fiddle demonstrating this problem : http://jsfiddle.net/cubicleWar/c3mTT/1/
I believe you should be transcluding the child elems, also you may want to avoid
putting 'ng-switch' directive in a different layer, so that it would not conflict with
other directives.
app.directive('logicOption', function() {
return {
replace: false,
transclude: true,
template: "<div ng-transclude>{{entry.type}} : This is the logic stuff</div>"
};
});
http://jsfiddle.net/YusCU/

Resources