I'm working with Angular directives that looks like this:
<parent>
<children></children>
<children></children>
<children></children>
</parent>
Parent directive has a
template: return "<div><ul></ul><div ng-transclude></div></div>"
And children directives will go inside that ng-transclude div.
My final HTML structure is
<newParent>
<div ng-transclude>
<child></child>
<child></child>
<child></child>
</div>
</newParent>
I wonder if it's possible to remove that ng-transclude div so that the new children are direct children of the new parent. (I have more children, a random number >1).
I have to do so to match an existing template so I cannot change its structure.
I actually have no Fiddles, if you need more information just ask. Thank you!
You can append the content yourself, without using ng-transclude, using the transclude function:
app.directive("parent", function($compile) {
return {
restrict: "EA",
transclude: true,
link: function(scope, element, attrs, ctrls, $transclude) {
$transclude(function(clone, scope) {
element.append(clone);
});
}
};
});
Here's more information about Transclusion.
Without a template, this will add the transcluded elements as only children of the directive. With the template, you'll need to properly place (e.g. insert it into a <div> after <ul>) the content yourself.
Related
I have a large chunk of HTML in an ng-repeat that for certain elements has a container element and for others it does not. I'm currently achieving this with two ng-ifs:
<strike ng-if="elem.flag">
… <!-- several lines of directives handling other branching cases -->
</strike>
<div ng-if="!elem.flag">
… <!-- those same several lines copied-and-pasted -->
</div>
While this works, it means I have to remember copy-and-paste any edits, which is not only inelegant but also prone to bugs. Ideally, I could DRY this up with something like the following (inspired by ng-class syntax):
<ng-element="{'strike':flag, 'div':(!flag)}">
… <!-- lots of code just once! -->
</ng-element>
Is there any way to achieve a similarly non-repetitive solution for this case?
You can make such directive yourself.
You can use ng-include to include the same content into both elements.
Assuming the effect you desire is to have the text within your tag be striked through based on the condition of the elem.flag:
You could simply use the ng-class as follows
angular.module('ngClassExample', [])
.controller('elemController', Controller1);
function Controller1() {
vm = this;
vm.flag = true;
vm.clickItem = clickItem
function clickItem() {
// Toggle the flag
vm.flag = !vm.flag;
};
}
.strikethrough{
text-decoration: line-through
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='ngClassExample' ng-controller="elemController as elem">
<div ng-class="{strikethrough: elem.flag}" ng-click="elem.clickItem()">
element content should be sticked through: {{elem.flag}}
</div>
</div>
You can do it with a directive
module.directive('myFlag', function() {
var tmpl1 = '<strike>...</strike>';
var tmpl2 = '<div>...</div>';
return {
scope: {
myFlag: '='
},
link: function(scope, element) {
element.html(''); // empty element
if (scope.myFlag) {
element.append(tmpl1);
} else {
element.append(tmpl2);
}
}
};
});
And you just use it like:
<div ng-repeat="item in list" my-flag="item.flag"></div>
You could create a directive which will transclude the content based on condition. For tranclusion you could use ng-transclude drirective, in directive template. Also you need to set transclude: true.
HTML
<my-directive ng-attr-element="{{elem.flag ? 'strike': 'div'}}">
<div> Common content</div>
</my-directive>
Directive
app.directive('myDirective', function($parse, $interpolate) {
return {
transclude: true,
replace: false, //will replace the directive element with directive template
template: function(element, attrs) {
//this seems hacky statement
var result = $interpolate(attrs.element)(element.parent().scope);
var html = '<'+ result + ' ng-transclude></'+result+'>';
return html;
}
}
})
Demo Plunkr
You can also use ng-transclude :
Create your directive :
<container-directive strike="flag">
<!-- your html here-->
</container-directive>
Then in your directive do something like :
<strike ng-if="strike">
<ng-transclude></ng-transclude>
</strike>
<div ng-if="!strike">
<ng-transclude></ng-transclude>
</div>
I am new to react and I have a react component structure like:
<MainComponent>
<Button />
<Content />
</MainComponent>
Now when I click on the Button, I need to replace the existing div (say div1) of the Content component with another div (div2). Can you please tell me how to do it. Thank you.
Note: Till now, on click event I have been changing the data of the single div using state and prop. Now I got to replace the whole div with another one.
Like this.
render() {
var returnIt;
if (useDivOne) returnIt = (<div id='one'></div>);
else returnIt = (<div id='two'></div>);
return (returnItj);
}
If this is your structure:
<MainComponent>
<Button />
<Content />
</MainComponent>
and Content renders something like:
<div>
this is div 1
</div>
I would think you would need to pass a prop to Content that would tell you which div to render, then in Content's Render you manipulate the properties of Boolean logic to present a different component:
class Content extends Component {
render() {
return(
{
!this.props.RenderDiv2Bool &&
<div>
This is Div1 and it will be rendered
because RednerDiv2Bool is false.
</div>
}
{
this.props.renderDiv2Bool &&
<div>
This is Div2 and it will be rendered
because RednerDiv2Bool is true.
</div>
}
)
};
}
Not necessarily better but just another way to do it.
Assume the current URL is: http://server.com/?key=value#/foo
In a normal anchor tag link, the following will just affect the anchor hash:
LINK
And the URL becomes: http://server.com/?key=value#/bar
However, I am adding links in a template in a web component that was imported from another .html file. Therefore, for the anchor hash to be relative to the loaded page instead of the component's html, I need to specify the link as follows:
LINK
However, a link like this causes the query search string to be lost: http://server.com/#/bar
Is there a clean solution here? Workaround, of course, is to create a new element inherited from that manually updates the window.document.location.
So, my current workaround is to just create a new anchor tag inherited from <a> that accepts an attribute hash instead of href (using Polymer 0.9):
<dom-module id="a-hash"></dom-module>
<script>
Polymer({
is: 'a-hash',
extends: 'a',
hostAttributes: { href: "" },
properties: { hash: String },
listeners: { tap: '_ontap', click: '_onclick' },
_onclick: function(e) { e.preventDefault(); },
_ontap: function(e) {
e.preventDefault();
document.location.hash = this.hash;
}
});
</script>
Usage:
Link: <a is=a-hash hash="/client/side/route">Click me</a>
I found a much cleaner solution to adding relative links in a new web component. Just add a:
<base href="../../" />
to the top of the component's .html file (assuming you keep your custom elements in an elements/element-name subdirectory) and then you can just add normal anchors such as:
<a href="#/bar>LINK</a>
And it will be created relative to the original app's URL instead of the component's html without losing the query string or reloading.
Just remember that ALL links in the component will now be relative to the root of the app instead of the component, so other references may need to be updated accordingly.
in every angular template we have to define a root html node, then inside it we can define the Html of our directive.
is there a way in angular to ignore that root node?
example :
my directive template :
<div class="space consuming div, and absolute positioning breaker">
<div class="content positioned relative to the directives parent 1"></div>
<div class="content positioned relative to the directives parent 2"></div>
<div class="content positioned relative to the directives parent 3"></div>
</div>
can we just set our template to be
<div class="content positioned relative to the directives parent 1"></div>
<div class="content positioned relative to the directives parent 2"></div>
<div class="content positioned relative to the directives parent 3"></div>
thanks!
You only need one root element if you are using replace: true in your template.
This is the case if you have defined custom element and are using then in your HTML in the following way:
<tabs>
<pane>1</pane>
<pane>2</pane>
</tabs>
In this case, replacing tabs with a template which has two roots will cause some confusion.
However, if you do not need replace: true, then you can set the directive on the element you want and assign a multi-root template on it. That template will be rendered inside the element which has the directive.
JS
var app = angular.module('plunker', []);
app.directive('myDirectiveOne', function() {
return {
template: '<p>Hello</p><p>World!</p>'
};
})
app.directive('myDirectiveTwo', function() {
return {
template: '<p>Hello</p><p>World!</p>',
replace: true
};
})
Template
<!-- works -->
<div my-directive-one></div>
<!-- has problem -->
<div my-directive-two></div>
E.g. http://plnkr.co/edit/jgEWsaxzfD4FkHcocJys?p=preview
I have a directive that compiles another directive and attaches it to the body with the same scope passed. This will not be the same location as the "parent" directive.
When the parent directive gets destroyed is there some way to have the child directive and scope destroy as well? I ask because after inspecting the DOM the child directive is still there.
Currently I hook into the parents $destroy event but was curious if it could be handled automatically.
jsFiddle: http://jsfiddle.net/FPx4G/1/
The child stays there as you toggle the parent, but i'd like to to be destroyed. What would be the best method to do that?
html:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<button data-ng-click="toggleParent()">Toggle Parent</button>
<div data-ng-switch data-on="displayDirective">
<div data-ng-switch-when="true">
<div class="parentDirective">Parent Directive</div>
</div>
</div>
</div>
</div>
javascript:
angular.module('app', [])
.directive("parentDirective", function($compile){
return {
restrict: 'C',
link: function(scope, element){
var secondDirective = angular.element(document.createElement("div"));
secondDirective.addClass("childDirective");
document.body.appendChild(secondDirective[0]);
$compile(secondDirective)(scope);
}
}
})
.directive("childDirective", function(){
return {
restrict: 'C',
template: '<div>Child Directive</div>',
link: function(scope, element){
scope.$on("destroy", function(){
alert(1);
});
}
}
});
function ParentCtrl($scope){
$scope.displayDirective = true;
$scope.toggleParent = function(){
$scope.displayDirective = !$scope.displayDirective;
}
}
Normally, I'd just have the sub element within the original directive's template so that it's positioned correctly. The issue really comes down to dealing with z-index. The parent element is in a container that can be scrolled, so the child (in one case a custom dropdown) would be hidden/cut off if it was larger then the container. To combat this I instead create the actual child in the document body and position it relative to the parent. It would also listen in on scroll events to reposition itself. I have that all working and is just fine. It's what happens when I need to delete the parent... the child is still there.
directive("childDirective", function(){
return {
restrict: 'C',
template: '<div >Child Directive</div>',
link: function(scope, element){
scope.$on("$destroy",function() {
element.remove();
});
}
}
});
updated fiddle : http://jsfiddle.net/C8hs6/