How to move DOM to rootElement in directive (angularjs) - angularjs

I have a directive which has its template but part of it should always be appended to $rootElement. So wherever user put this directive in app's html part of it must reside as the last child of $rootElement.
This is a special case for transclusion I guess but I wonder if someone has already been done anything like this.
Example - let's say I have my-modal-dialog directive and user wants to use two or more dialogs inside each other. So I'd have this html:
<my-modal-dialog id="dlg1">
<div>...</div>
<my-modal-dialog id="dlg1-1">
...
</my-modal-dialog>
</my-modal-dialog>
But the resulting html should be like this:
<div ng-app>
...
<div id="dlg1" class="modal">
...
</div>
<div id="dlg2" class="modal">
...
</div>
</div>
Any idea of how this could be done?

You could probably use compile method for this. Here is an example in fiddler - http://jsfiddle.net/EyDPh/
myApp.directive("myModelDialog", function () {
return {
replace: true,
restrict: 'E',
scope: {
'value': '='
},
compile:function(element, attrs){
var newElement = angular.element('<div id=' + attrs.id + '> Your content is - ' + element.html() + '</div>');
element.replaceWith(newElement);
}
};
});

Related

How to dynamically generate a component withing a component/directive?

I want to create a wrapper for different angularjs components. That wrapper have some have html code and some common functions.
<div class="wrapper">
<h1>{{header}}</h1>
<!--place for a component that has been passed through bindings-->
</div>
So ideally, I want to pass a name of a child component to this one and have it render it. How can I achieve this?
Not sure you can achieve that by passing names of components, but you could definitely use the AngularJS transclude directive
For example:
JS
var app = angular.module('AppModule', []);
app.directive('wrapperDir', function () {
return {
restrict: 'E',
template: '<div class="wrapper">' +
'<h1>Example transclude</h1>' +
'<div ng-transclude></div>' +
'</div>',
transclude: true,
scope: {}
};
});
HTML
<div ng-app='AppModule'>
<wrapper-dir>
<h2>Content to transclude</h2>
<div>Other stuff, including other directives</div>
</wrapper-dir>
</div>
JSFiddle example here

Angular Directive Pass back scope variable

I am trying to make a directive that is basically a text box with a max length counter on it. My directive is below. Basically a text box that will tell the user that they only have x number of characters left.
angular.module('InputApp', []);
angular.module('InputApp').directive('textAreaCounter', function () {
return {
templateUrl: '/Content/Directives/Inputs/TextAreaCounter.html',
restrict: 'AE',
multiElement: true,
scope: {
text: '='
},
link: function (scope, elem, attrs) {
if (scope.text == undefined || scope.text == '') {
scope.CharactersLeft = attrs.totalCharacters;
} else {
scope.CharactersLeft = attrs.totalCharacters - scope.text.length;
}
scope.TextValueChanged = function () {
scope.CharactersLeft = attrs.totalCharacters - scope.text.length;
}
}
}
});
The template html is:
<div class="row">
<div class="col-md-12">
<textarea ng-model="text" ng-change="TextValueChanged()" autogrow rows="5"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12 top-left smallText">
You have {{CharactersLeft}}.
</div>
</div>
And I use the directive like this.
<text-Area-Counter text="WholeDeletionText" total-Characters="250"></text-Area-Counter>
The issue that I am having is that the value 'WholeDeletionText' is not being updated by the directive.
My expectation is that the scope.WholeDeletionText in the parent would update with the text that was written in the textarea in the directive. At least my understanding of the isolated scope having the '=' sign is that they share it with the parent. What am I doing wrong or is there a better way to do it?
The problem occurs because you have not WholeDeletionText variable in the application scope.
To fix it you need:
1) create controller and initialize WholeDeletionText variable
.controller("MyController", function ($scope) {
$scope.WholeDeletionText = '123';
});
2) add ng-controller="MyController" directive in your main html file.
<body ng-app="InputApp" ng-controller="MyController">
<div>
<text-Area-Counter text="WholeDeletionText" total-Characters="250"></text-Area-Counter>
</div>
</body>
Full code see in Plunk.
Exactly, adding to the #Roman's point, when you use '=' in declaring the directive's scope variable it should be declared the the parent controller as well, otherwise you can use '#'.

Directive displaying Error: [$parse:lexerr] after moving template to templateURL

I have a directive working without any issues when the HTML markup is written in the template section of the directive.
I've just moved the HTML markup in a .html file and on load of the page, i am seeing:
Error: [$parse:lexerr] http://errors.angularjs.org/1.3.14/$parse/lexerr?p0=Unexpected%20next%20character%20&p1=s%2016-16%20%5B%5C%5D&p2=option.name%20%3D%3D%3D%20%5C'choices%5C'
Original directive:
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
data: "="
},
template: '<p>' +
'<div ng-repeat="select in data.output">' +
'<div ng-if= "select.name === \'choices\'">' +
'<p ng-repeat="choice in select.value"><label><input type="radio" ng-model="data.input[0].value" ng-value="$index" >{{choice}}</label></p>' +
'</div>' +
'</div>' +
'</p>'
}
}
);
New:
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
data: "="
},
templateUrl: 'home/mydirective.html'
}
}
);
I page load i can see the http request for mydirective.html and the markup is correct, however the lexerr then appears in the console.
Your html should not contain concatenation that will mess while angular $compile that template. It should be plain html.
mydirective.html
<p>
<div ng-repeat="select in data.output">
<div ng-if="select.name === 'choices'">
<p ng-repeat="choice in select.value">
<label>
<input type="radio" ng-model="data.input[0].value" ng-value="$index">{{choice}}</label>
</p>
</div>
</div>
</p>
In my case this was related to using single quote with a backslash (\') in the .html-file. It was working well when the html was put directly inside the directive using template.
But it threw an error when it was put in a separate .html-file and using templateUrl. So changing ng-class="{\'something\'}" to ng-class="{'something'}" fixed it for me. Hope this will save someone else couple of hours.

AngularJs transclude not working in Directive template or templateURL

I have written a custom directive like so, notice I have commented out the template URL that contains the same HTML structure and the template property:
.directive('sillyDirective', function ( ) {
'use strict';
return {
restrict: 'A',
replace: false,
transclude: true,
template: '<h2>Welcome to my site</h2>',
//templateUrl: '/views/hello.html' ,
link: function (scope, element, attrs) {
element.bind('click', function (){
alert('you click me! I am clicked');
});
};
});
In my HTML view I have the following...
<div data-silly-directive>
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
</div>
The problem is the content of the directive, e.g.:
<div><img src="logo.jpg></div>
<div><h1>My First Website</h1></div>
is being overwritten with the template content even thought I have set transclude to true and replace to false? What am I doing wrong here?
you need to specify ng-transclude in the template of your directive, this will let angular know where to insert the content of the markup.
app.directive("foo", function() {
return {
transclude: true,
template: "<div>the template</div><div ng-transclude></div>"
};
})
html:
<div foo>
Some Content Here
</div>
result:
<div foo>
<div>the template</div>
<div ng-transclude>Some Content Here</div>
</div>
here's a plnkr
source: https://www.accelebrate.com/blog/angularjs-transclusion-part-1/
Your template must contain an element with an ng-transclude attribute. That's where the body will be "pasted" by angular.
See
https://docs.angularjs.org/api/ng/directive/ngTransclude

How to write an angularjs directive that makes use of both scope and attributes and refer it thru compiled partial?

I want to write a directive which takes advantage of custom attributes, as follows:
<plant-stages
title="Exploration<br/>du cycle de<br/>développement<br/>de la plante"
></plant-stages>
The controller is currently as follows:
app.directive('plantStages', function () {
return {
restrict: 'AE',
templateUrl: 'corn.figure.plant.stages.html',
link: function (scope, element, attrs) {
scope.title = attrs.title;
}
};
});
The partial is as follows:
<figure class="cornStages">
<div>
<p>{{title}}</p>
</div>
<div ng-repeat="stage in stages">
<div class="stage{{stage.stage}}"></div>
<div>
BBCH : {{stage.bbch}}<br/>
{{stage.displayName}}
</div>
</div>
</figure>
The partial makes use of some scope model variables.
And {{title}} should support plain HTML injection out of the view which embeds it, hence should be compiled. I tried to support this but without success.
What modification should I make to have the HTML compiled?
A bonus question: when I pass the attribute in, I create a dummy title variable in the scope that persists where it should only be local. How would one make changes to handle this?
If you want to wrap HTML in your custom directive take a look at the transclude option (see docs):
module.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude></div>'
};
});
This enables you to place HTML within the directive tag which can be used in the template:
<div ng-controller="Controller">
<my-directive>
<h1>Test</h1>
</my-directive>
</div>
In case you really want to pass HTML via an attribute use ng-bind-html. This requires the ngSanitize module:
module.directive('myDirective', function () {
return {
restrict: 'E',
template: '<div ng-bind-html="title"></div>',
scope: {
title:'#'
}
};
});
I added this to your fiddle.

Resources