angular trasnclude always adds as child, sibling required - angularjs

I would like to do the transclude equivalent of element.insertAfter. What I am getting is basically element.appendChild. I would like the new element to share the same parent as the directive's element.
I would like
<div>
<input with-button></input>
</div>
to become
<div>
<input></input>
<button></button>
</div>
but I get instead
<div>
<input>
<button></button>
</input>
</div>
My directive's template looks like
<ng-transclude></ng-transclude>
<button></button>
and the directive looks like
angular
.module('appy')
.directive('withButton', withButton);
function withButton() {
return {
restrict: 'A',
transclude: true,
templateUrl: 'path/to/template'
};
}
According to the docs this should work. What am I missing?

First thing, everything you declare as directive template will be put inside the element on which the directive is used. So in your case your template:
<ng-transclude></ng-transclude>
<button></button>
Will go inside the:
<input with-button></input>
So, no wonder the button finishes inside your input.
Second thing, setting { transclude: true } means "get everything from inside the element on which this directive is applied, and put it inside the element in the directive template on which ng-transclude is used", in this case nothing since there is nothing inside
<input with-button></input>
(it has no children).

Related

ngTransclude directive doesn't work, can't realize where I'm wrong

Trying to use ngTransclude first time to make custom directive, to achieve the floating label functionality as shown here: Floating label pattern — Angular JS, but it doesn't work.
Here is my directive code:
.directive('floatingLabel', function () {
return {
restrict: 'A',
scope: {
label: '#',
value: '='
},
transclude: true,
templateUrl: 'floating-label-template.html'
}}
)
Directive's template:
<div class="field">
<label ng-show="value" class="show-hide">{{label}}</label>
<div ng-transclude></div>
</div>
I'm trying to use it in the following way:
<input floating-label label="Floating" value="floatingDirective" type="text" class="form-control" ng-model="floatingDirective"/>
Plunker with my code: https://plnkr.co/edit/MC8G4H3B9zEleaBZ7ijJ?p=preview
P.S. I'm using AngularJS 1.4.9
Fixed plunker.
Read this : What is ng-transclude
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
Why your code not working: you have nothing to be 'transclude'.
A general markup of use directive with ng-transclude may like this:
<directive-with-ng-transclude>
<node-to-transclude></node-to-transclude>
</directive-with-ng-transclude>
After compiled, <node-to-transclude></node-to-transclude> will be appended to the template of your directive where you put ng-transclude.
Notice : ng-model inside ng-transclude , that's why I use a dotted ng-model floatingDirective.

AngularJS Directive What means Replace and Transclude?

Can somebody explains what means:
replace: true / false
and
transclude: true / false
in an angularjs directive?
I did read the documentation but could understand it 100%.
Replace
Whenever you say replace: true, you are telling your directive to replace your directive tag with the template of directive.
Assume you have used template: '<div>This is directive template</div>' in your directive that look like <div data-my-directive></div> When you run your application all the directives div in DOM will be replaced by <div>This is directive template</div>
However in the same scenario if you declare replace: false Your DOM will have something like this
<div data-my-directive>
<div>This is directive template</div>
</div>
TRANSCLUDE
You use ng-transclude, you want to append data in the directive template from controller of your view.
Lets say you have template in your directive which looks like
template: '<div class="main"><div data-ng-transclude></div></div>'
So when you set transclude: true you are explicitly telling your directive to allow all the transcluded values to be part of directive itself when DOM loads.
Lets say you have used the directive in your view like this:
<div data-my-directive>
Transcluded data
</div>
You will see th the text Transcluded data in your web page, however if you have set transclude: false You wont see the text Transcluded data
To keep it simple:
Replace true will replace your directive's template with the tag from where you called your directive:
<my-directive></my-directive>
if replace is true will become:
<div>bla bla</div>
if replace is false will become:
<my-directive><div>bla bla</div></my-directive>
Transclude will "copy" whatever is inside your directive, and append it somewhere on your template:
<my-directive>This is some content</my-directive>
if transclude is true:
<my-directive>
<div>bla bla</div>
<span>This is some content</span> <!--for this to work your template needs to use the ng-transclude directive -->
</my-directive>
if transclude is false:
<my-directive>
<div>bla bla</div>
</my-directive>

Exposing AngularJS directive property value to parent controller

I am working on an AngularJS app. I am trying to write a reusable component to use throughout my app. For the sake of demonstration, we'll just use a text box. I've created a fiddle of my attempt here. Basically, I'm trying to use a control like this:
<div ng-app="myApp">
<div ng-controller="myController">
<my-control textValue="{{controlValue}}"></my-control>
<br />
You entered: {{controlValue}}
</div>
</div>
Unfortunately, I cannot figure out how to bind a property between the scope of the controller and the scope of the directive. I'm not sure what I'm doing wrong in my fiddle. Can someone please tell me what I'm doing wrong?
Thank you!
You have created directive with isolated scope and you are trying to print value from isolate scope. It isn't right, you can write your directive like this, without isolated scope:
return {
restrict: 'EAC',
template: '<input type="text" ng-model="controlValue"></input>'
};
if you want to setup directive with isolated scope, you should isolate your scope then use $watch for do changes
You were not so far, but you need to be carefull when using {{}}
Remove braces and don't use camelNotation for text-value attribute :
<div ng-app="myApp">
<div ng-controller="myController">
<my-control text-value="controlValue"></my-control>
<br />
You entered: {{controlValue}}
</div>
</div>
Use ng-model attribute :
angular.module('ui.directives', []).directive('myControl',
function() {
return {
restrict: 'EAC',
scope: {
textValue: '='
},
template: '<input type="text" ng-model="textValue"></input>'
};
}
);

How can I replace a number of DIVs with a single element in AngularJS?

I have some DIVs that appear on the bottom of each of my pages:
<div id="message" ng-show="test.stateService.displayMessage">
<div>
<div>
<div>
{{ test.stateService.message }}
</div>
</div>
</div>
</div>
Is there a way that I can use Angular to simplify this code so that I do not need to add the same code block to every page?
Yes, you could use a directive like so:
app.directive('codeblock', function() {
return {
scope: true, // use a child scope that inherits from parent
restrict: 'A',
replace: 'false',
template: '<div>\
<div>\
<div>\
{{ test.stateService.message }}\
</div>\
</div>\
</div>'
};
});
You'd call it like this:
<div codeblock id="message" ng-show="test.stateService.displayMessage">
Check https://docs.angularjs.org/api/ng/directive/ngInclude
You can save this div as a file, say footer.htm and add the below code to the bottom of all pages.
<div ng-include="footer.htm"></div>
But this code also needs to be given at the bottom of all pages. So I am not sure how it can save your time.
I don't think you can get rid of adding at least some code on each of the pages. Using an html template with ng-include would be one of the approaches.

When to use transclude 'true' and transclude 'element' in Angular?

When should I use transclude: 'true' and when transclude: 'element' ?
I cant find anything about transclude: 'element' in the angular docs, they are pretty confusing.
I would be happy if someone could explain this in simple language.
What is the benefit of each option? What is the real difference between them?
This is what I have found :
transclude: true
Inside a compile function, you can manipulate the DOM with the help of transclude linking function or you can insert the transcluded DOM into the template using ngTransclude directive on any HTML tag.
and
transclude: ‘element’
This transcludes the entire element and a transclude linking function is introduced in the compile function. You can not have access to scope here because the scope is not yet created. Compile function creates a link function for the directive which has access to scope and transcludeFn lets you touch the cloned element (which was transcluded) for DOM manipulation or make use of data bound to scope in it. For your information, this is used in ng-repeat and ng-switch.
From AngularJS Documentation on Directives:
transclude - compile the content of the element and make it available to the directive. Typically used with ngTransclude. 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.
true - transclude the content of the directive.
'element' - transclude the whole element including any directives defined at lower priority.
transclude: true
So let's say you have a directive called my-transclude-true declared with transclude: true that looks like this:
<div>
<my-transclude-true>
<span>{{ something }}</span>
{{ otherThing }}
</my-transclude-true>
</div>
After compiling and before linking this becomes:
<div>
<my-transclude-true>
<!-- transcluded -->
</my-transclude-true>
</div>
The content (children) of my-transclude-true which is <span>{{ something }}</span> {{..., is transcluded and available to the directive.
transclude: 'element'
If you have a directive called my-transclude-element declared with transclude: 'element' that looks like this:
<div>
<my-transclude-element>
<span>{{ something }}</span>
{{ otherThing }}
</my-transclude-element>
</div>
After compiling and before linking this becomes:
<div>
<!-- transcluded -->
</div>
Here, the whole element including its children are transcluded and made available to the directive.
What happens after linking?
That's up to your directive to do what it needs to do with the transclude function. ngRepeat uses transclude: 'element' so that it can repeat the whole element and its children when the scope changes. However, if you only need to replace the tag and want to retain it's contents, you can use transclude: true with the ngTransclude directive which does this for you.
When set to true, the
directive will delete the original content, but make it available for reinsertion within
your template through a directive called ng-transclude.
appModule.directive('directiveName', function() {
return {
template: '<div>Hello there <span ng-transclude></span></div>',
transclude: true
};
});
<div directive-name>world</div>
browser render: “Hello there world.”
The best way of think about transclusion is a Picture Frame.A picture frame has its own design and a space for adding the picture.We can decide what picture will go inside of it.
When it comes to angular we have some kind of controller with its scope and inside of that we will place a directive that supports transclusion. This directive will have it’s own display and functionality . In non-transluded directive, content inside the directive is decided by the directive itself but with transclusion,just like a picture frame,we can decide what will be inside the directive.
angular.module("app").directive('myFrame', function () {
return {
restrict: 'E',
templateUrl:"frame.html",
controller:function($scope){
$scope.hidden=false;
$scope.close=function(){
$scope.hidden=true;
}
},
transclude:true
}
});
Content inside the directive
<div class="well" style="width:350px;" ng-hide="hidden">
<div style="float:right;margin-top:-15px">
<i class="glyphicon glyphicon-remove" ng-click="close()" style="cursor:pointer"></i>
</div>
<div ng-transclude>
/*frame content goes here*/
</div>
</div>
Call Directive
<body ng-controller="appController">
<my-frame>
<span>My Frame content</span>
</my-frame>
</body>
Example

Resources