I'm trying to clone the ng-content items of a component along with any functionality added on the HTML of that content. For example, the markup using the component might look like this:
<custom-component>
<button (click)="doAThing();">A button</button>
</custom-component>
Then I set up my template for custom-component like so:
<ng-template #content>
<ng-conent></ng-content>
</ng-template>
<ng-template *ngTemplateOutlet="content"></ng-template>
<div class="second-area>
<ng-template *ngTemplateOutlet="content"></ng-template>
</div>
My expectation would be that the ng-content would get duplicated into both ngTemplateOutlet areas. What happens is that it pushes to the last outlet and ignores the first. This markup will duplicate normal markup just fine, but ng-content only move to one outlet.
Is this not possible with this technique, am I missing something obvious, or it there another way to clone the contents of ng-content along with any events attached to it?
I found this solution that worked for me. First the HTML, you'll need a directive to wrap the content in so you can reference it. You'll need to use asterisk with directive so it can be duplicated.
<custom-component>
<ng-container *customDirective>
<button (click)="doAThing();">A button</button>
</ng-container>
</custom-component>
The directive doesn't require any extra code. We just need it for a reference.
In your custom-component, you'll need to create a reference to the diretive via #ContentChild like so:
#ContentChild(CustomDirective, { read: TemplateRef }) transcludeTemplate;
Then we use the following for our custom-component HTML avoiding using ng-content tags all together:
<ng-template *ngTemplateOutlet="transcludeTemplate"></ng-template>
<div class="second-area>
<ng-template *ngTemplateOutlet="transcludeTemplate"></ng-template>
</div>
So this isn't really the same as duplicating <ng-content>, but it gives us a similar function. Apparently ng-content not multiplying is intended behavior. So this might be the best way to achieve a similar goal.
Related
I'm trying to create a custom element that handles tab groups (it's using material under the hood). I need this element to take a dynamic amount of tabs, and I want to do this with html rather than creating a bunch of attributes that need to be filled out. I figured an efficient way of doing this would be to use templates, and then have the custom element determine how many templates were injected and create the appropriate markup and place the contents of the templates appropriately.
The markup would look something like this
<my-tabgroup>
<template label="Tab 1">
<p>Tab 1 content</p>
</template>
<template label="Tab 2">
<p>Tab 2 content</p>
</template>
<template label="Tab 3">
<p>Tab 3 content</p>
</template>
</my-tabgroup>
My custom element would look something like this:
<slot #slotContent></slot>
<mat-tab-group>
<mat-tab *ngFor="let group of groups" [label]="group.label">{{group.content}}</mat-tab>
</mat-tab-group>
(this is not correct code, just giving ideas)
The idea is that the custom element would be able to parse the templates that were passed in the slots and generate the appropriate groups object to dynamically render all the tab groups.
My issue is that I can't seem to access the templates that were passed into the <slot>. Is there a way to access templates that are passed into slots?
Answering my own question here. This came down to a lack of deeper understanding with slots and shadow dom. The light dom (the components that are passed to the slots) actually exist within the custom element but not within the shadow root. The browser does the "virtualization" of placing those light dom elements into the appropriate shadow dom slot.
I learned that I can use the assignedNodes or assignedElements function on the slot element to get the nodes/elements that would be "put" inside the slot, thus allowing me to evaluate dynamically what's going to be rendered.
Hope my struggle helps somebody else!
I'm putting together an application in which there are many modals. As I do not want to repeat the code of the modal, I want to assemble a base component that has the minimum structure and then with that structure to be able to assemble the different modals and to carry what I need inside (form, text, images)
An example of what I am looking to do
<app-modal-base>
<app-form></app-form>
<app-modal-base>
I hope you understand what I'm looking for. In case you can not, someone found an alternative solution?
Thanks
In your base modal template, include the <ng-content></ng-content> tag. When you display your modal, you can use it as follows:
<Modal>
<div id="mydiv">
<p> Simple paragraph </p>
<form>...</form>
</div>
</Modal>
The modal will include what you have included between the <Modal></Modal> tags at the place where the <ng-content></ng-content> tags are in the template for the base modal component. It would look like:
template: `
<div id="closeButton"></div>
<ng-content></ng-content>
`
This information is gathered from this source, and I can't seem to find official docs about this. You might have to try it out.
The title might seem weird but what im talking about is basically what this link is doing.
Im look for a way to use the current iterated person in a <tr> outside of the *ngFor scope.
In the link he used ng-repeat-start and ng-repeat-end to include multiple tag withing the ng-repeat. How can i achieve the same behavior withing Angular2 using *ngFor?
I had to use the <template> tag, which made the iterated objected accessible within it.
<ng-template let-person ngFor [ngForOf]="people">
<tr>
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
<div>
<span>{{person.details}}</span>
</div>
</ng-template>
Hope it'll help somebody
The * in *ngFor (and really any angular directive) is shorthand that tells angular to surround the element in an ng-template, which is just the angular implementation of . You can read about how that happens at https://angular.io/guide/structural-directives, but I'll reproduce the relevant example below.
If your code contains
<div *ngIf='object'>{{object.property}}</div>
as part of the render process, angular transposes that to
<ng-template [ngIf]='object'>
<div>{{object.property}}</div>
</ng-template>
I am trying to add a render a template using the push mutation method. I want to push a section component, but instead of the template content I get the raw output of <vsection></vsection'. Can anyone help me render the actual template content and not the raw tags? I included a jsbin below.
http://jsbin.com/wurofatuve/1/edit?html,js,output
You're thinking about this a little oddly. What I think you'd be better off doing is putting a v-for on a <vsection> component.
<vsection v-for="section in sections">
{{ section.content }}
</vsection>
This way, when you push content to sections it'll out put another one. You'll also have to adjust your section component so you can use the content.
<template id="section-template">
<div class="section">
<slot></slot>
</div>
</template>
Here it is working like I think you want: http://jsbin.com/suhadidobe/1/edit?html,js,output
I am converting someone else's code to Handlebars.js and I'm stuck on converting this tag to its {{#handle-bar}}{{/handle-bar}} counterpart.
The previous coder used an {{#ifCond}} to toggle what 'selected'. This is my component.
{{#dropdown-item }}
{{unbound this.itemName}}
{{/dropdown-item}}
Here is the div i want converted to my component
<div class="dropdownItem" {{bind-attr value=formField_DropdownItemID}}{{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} >
{{unbound this.itemName}}
</div>
My first thought was to just pop the div's logic into the the component, like the next example, but this gave me an error.
{{#dropdown-item bind-attr value=formField_DropdownItemID {{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} }}
{{unbound this.itemName}}
{{/dropdown-item}}
Any suggestions?
You can set those properties to compute. The syntax would be:
{{#dropdown-item selected=computedProperty value=formField_DropdownItemID}}
computedProperty can deal with your conditional logic. The whole idea is to pull that out of handlebars anyways. :)