Transclude then replace with angularjs - javascript

I would like to create a directive that replaces :
<my-overlay class="someOverlay">
<h4>Coucouc</h4>
</my-map-overlay>
With :
<div class="someOverlay default-overlay">
<h4>Coucouc</h4>
</div>
The replace method is yet deprecated.
How to write a directive that manipules the DOM creating a div, adds the default-overlay class to the previous defined one, transcludes and replaces the <my-map> directive ?
Is it possible to divide the process as following : DOM manipulation in compile and transcluding in link?

This is the commit for 'replace' to be removed:
https://github.com/angular/angular.js/commit/eec6394a342fb92fba5270eee11c83f1d895e9fb
If you read some of the last comments it seems that replace may not be deprecated after all. However this could be a way to achieve what uou want:
.directive('myOverlay', function(){
return {
restrict: 'E',
template: '<div ng-transclude></div>',
transclude: true,
link: function (scope, element) {
element.children()[0].setAttribute('class', element.attr('class') + ' default-overlay');
element.replaceWith(element.children()[0]);
}
}
});
http://jsfiddle.net/b6ww0rx8/10/

Related

How to deal with css and custom directive in angulajs

Consider a custom directive in angularjs:
.directive('myDir', function() {
return {
restrict: 'E',
template: "..."
}
})
As far as I see the tag <my-dir></myDir> has no default style associated (or, at least, it is not a block tag). Now I want to style it and place in the right point of my page. I have 2 alternatives:
1) Use this template:
.directive('myDir', function() {
return {
restrict: 'E',
template: "<div class="layout">...</div>",
}
})
And css:
.layout {
/* bla bla bla */
}
But this introduce a new unnecessary level in the DOM three, since if I wrote something like <my-dir class="layout"></my-dir> with the proper css attached it would have worked anyway BUT I'll have to remember to add the same css class every time I use <my-dir> inside my code (and this is not DRY).
2) This led me to add style inside post-link function:
.directive('myDir', function() {
return {
restrict: 'E',
template: "...",
link: function(scope, element, attrs) {
element.addClass('layout');
}
}
})
Which strategy is better? Are there pros or cons I can't see?
UPDATE:
Using replace: true in directive definition is not an option, since it has been deprecated and when using bootstrap things like <my-dir class="visible-xs"></my-dir> may be useful.
You could solve this problem by having replace: true option in your directive, which will basically replace your directive DOM with the template which you have given from your directive.
Directive
.directive('myDir', function() {
return {
restrict: 'E',
template: "<div class="layout">...</div>",
replace: true //added replace true property
}
})
Resultant HTML
<div class="layout">..Content..</div>
Like above I shown, you could get rid off the unnecessary HTML tag by simply using replace option of directive.
Demo Plunkr
Note
As per Angular Docs replace option has been deprecated in Angular2,
but in your case you can avoid that.
But if you really wanted to go avoid the use of replace option of directive then I'd suggest you to use your directive as an attribute instead of going for an element but then you need to take <div class="layout"> outside and then your could put your directive over that like <div class="layout" my-dir>. But if you again think over it in Angular2 perspective, you are changing your component structure to old angular way using attribute/class

Angular: How to force recompile directive?

HTML:
<div ng-repeat="obj in arr">
<custom-directive status-stored-in="obj"></custom-directive>
</div>
Problem:
I have page-turn built in for the large amount of objs. Which means that the value of arr, representing the current page of objs, will change. However, the obj in the status-stored-in="obj" part is not refreshed with the change.
Right now my solution is to add a ng-if in customDirective, flickering its value back and forth to have it force recompiled. Is there any other equivalent, neater way to deal with this?
Edit:
The start of the custom directive:
module.directive 'checkbox', (checkboxHooks) ->
restrict: 'E'
scope:
hook: '='
hookedTo: '='
statusStoredIn: '='
templateUrl: 'templates/checkbox.html'
link: (scope, element, attr) ->
The gist is that it needs to get hold of an object, for storing the checked status. The whole of it can be found here: [coffee/js].
Inside your directives link function you need to watch status-stored-in for changes and then recompile it e.g.:
link: function(scope, element) {
scope.$watch('statusStoredIn', function() {
element.html(statusStoredIn);
$compile(element.contents())(scope);
});
}

How to hide an element after $compile?

There is a directive:
.directive('location', function () {
return {
restrict : 'A',
scope : {},
replace : true,
templateUrl: 'common/components/location/location.html',
link : function (scope, element, attr) {……}
}
});
So it is used in another directive:
var scope = $rootScope.$new(true);
var directive = $compile('<div location></div>')(scope);
$document.find('body').append(directive);
directive.hide(); - not working
How to hide directive generated html after inserting it to body? Directive has "replace" set to true
http://plnkr.co/edit/e7fNua?p=preview
The line you've marked as wrong is actually working meaning that after directive.hide() if you print the element on console you'll see:
console.log(directive[0]);
-> <div location="" class="ng-scope" style="display: none;"></div>
This one is a bit tricky but easy if you carefully follow what you told angular to perform. Let's go through it step by step
$compile('<div location></div>')(scope); - here you're telling angular to compile given template and link it with given scope. Since you have specified templateUrl that must be fetched angular has not yet completed processing this element.
$document.find('body').append(directive); directive.hide(); - append the element to body and immediately hide it. If you print the directive[0] to console you'll see: <div location="" class="ng-scope" style="display: none;"></div>
Note that there is still no <section><p>Text</p></section> inside DOM
Angular completes fetching location.html template.
Since you have replace: true inside directive declaration the directive[0] is removed from DOM and replaced by compiled <section><p>Text</p></section>.
As a result you still see Text on page.
You can change this behaviour in various ways:
change directive declaration to replace: false
use inline template: '<section><p>Text</p></section>'
wrap template that you pass to $compile in another element: $compile('<div class="wrapped"><div location></div></div>')

How can I use a directive wrapper for ng-grid with the grid options determined by a directive attribute?

I am attempting to create a re-usable directive wrapper for ng-grid where I can apply the location of the ng-grid options through the use of an attribute.
Here is the skeleton of the code which gets very close to what I want:
angular.module('myApp').directive('grid', function() {
return {
restrict: 'E',
template: '<div class="gridStyle" ng-grid="someObject.gridOptions"></div>',
link: function(scope, element, attributes) {
// no code here necessary, to use the hard-coded ng-grid options
}
}
}]);
What I would like to do is supply the variable for ng-grid by using an attribute, similar to this:
<grid dataLocation="someObject.gridOptions"></grid>
I've tried using the compile and the link options with multiple methods, including reading the attributes, and then using the element.html() method to update the html to set the ng-grid attribute to "someObject.gridOptions", as well as using {{someScopeVariable}}, and setting scope.someScopeVariable to "someObject.gridOptions" in the linking function. I have verified using the chrome's html inspector that the div's attribute looks correct, but I have not been able to get the item to show up on my page.
I suspect I'm running into issues since the ng-grid is already a compiled directive. Is there any way I can pull this off? I've tried a large number of compile and linking methods with no success yet.
To access the original template's attributes, the template option of directive should be defined as a function as follows:
angular.module('myApp').directive('grid', function() {
return {
restrict: 'E',
template: function(element, attrs){
return '<div ng-grid="' + attrs.dataLocation + '"></div>';
}
};
});
Your HTML stays the same:
<grid dataLocation="someObject.gridOptions"></grid>

How to select an element with a specific id from a directive's linking function's 'element' parameter, using jQuery rather than angular's jqLite?

Here's the template of the directive:
(if I understand it right, this is what becomes available from the 'element' parameter of the linking function, no?)
template: '<div class="panel">' +
'<div id="lh" title=""></div>' +
'<div id="rh" title=""></div>' +
'</div>',
I've read that jQuery takes precedence over jqLight, when it is loaded prior to angular.
Bearing that in mind, here's my attempt to select the div marked with 'lr' id from the linking function and to set its 'title' attribute:
link: function(scope, element, attrs) {
element.$('#link').attr("title", attrs.title);
}
This doesn't work, as if there were no jQuery present.
How to do it the 'Angular' way, while taking advantage of jQuery?
Thank you for your time.
Jared
In jQuery you can optionally set the root from where it start looking.
$(selector[, root])
In your case it would be
$("#lr", element)
Also it is worth noting that there should only be one element with a particular ID on the page at any given time, so $("#lr") should also work.
Basically you need to get the HTML of the template and then convert it using jqLite (or jQuery) object.
link: function (scope, element, attrs) {
var html = element[0].innerHTML;
var e = angular.element(html).find("#lr").attr('title', 'ABC');
element.replaceWith(e);
}

Categories

Resources