Does multislot transclusion works with Angular 2 components? - javascript

let's say I've got the following basic component with two slots:
// my-component.component.ts
#Component({
selector: 'my-component',
template: `
<div class="my-component">
<div>
Title:
<ng-content select="my-component-title"></ng-content>
</div>
<div>
Content:
<ng-content select="my-component-content"></ng-content>
</div>
</div>`
})
And into the second slot ('my-component-content') I want to insert regular Angular 2 component...
<div class="app">
<my-component>
<div class="my-component">
<div>
Title:
<my-component-title>
This is the Component title!
</my-component-title>
</div>
<div>
Content:
<my-component-content>
<some-regular-angular2-component></some-regular-angular2-component>
</my-component-content>
</div>
</div>
</my-component>
</div>
Where 'some-regular-angular2-component' is a selector of some Angular 2 component...
#Component({
selector:'some-regular-angular2-component'
})'
Problem is that 'some-regular-angular2-component' is never transcluded into ng-content second slot...Only regular HTML works for me...Of cource I tried to set 'some-regular-angular2-component' into [directives] of parent component, but Angular 2 component is not recognized inside of the ng-content...Or does this work for you?

You need to add <ng-content></ng-content> to the view (template) of <my-component-content> If <my-component-content> doesn't support transclusion, its children won't be shown. The <ng-content> tags in your code apply only to elements that match directly, but not their children.

Just to answer my question, yes, angular 2 transclusion supports inserting of Angular 2 components. I had stupid typo in the code.

Yes it does. It is now called content projection. See this example of a modal dialog with multiple slots.

Related

How do I slice *ngFor in a component based on the components it is injected into?

The following code is a component that i need to inject in other components:
<div class="row">
<div class="col-12 [...]" *ngFor="let course of courses">
<div class="card">
[...]
</div>
</div>
</div>
The problem is that in one of the components it is injected in, I need to apply a | slice:0:4 in the ngFor to display less data. For this reason, I modified the child component and made it so the data from the parent components is sent to the child component. So I removed the .row and .col-12 divs and put it into the parent components, this way I can use the slice only in the parent component I need to.
<div class="row">
<div class="col-12 [...]" *ngFor="let course of courses | slice:0:4">
<app-child-component [course]="course"></app-child-component>
</div>
</div>
Child component now is like this:
<div class="card">
[...]
</div>
The problem now is that I need to apply CSS classes to the child component's .row that must be visible everywhere but this way I have to modify every single parent component's .row, so more useless code. I also assume this way I'm not using Angular's components concept the way it is meant to.
Is there a way to use | slice and kind of "send it" from a single parent component to the child component like i'm doing now with [corso]="corso" in <app-component [corso]="corso"></app-component>?
Hoping I explained this well. Thank you.
//Child component ts
#Input() sliceStart = 0; //default to `0` if not passed
#Input() sliceEnd = 4; //default to `4` if not passed
courses = ['HTML', 'CSS', 'Javascript', 'Angular'];
<!-- Child component html -->
<div *ngFor="let course of courses | slice: sliceStart:sliceEnd">
{{course}}
</div>
<!-- Parent component html -->
<app-child-component [sliceEnd]="2"></app-child-component>
Stacblitz example

Render Angular 4 component with text coming in from an API

I want to render incoming text from an API as subsequent HTML and component template.
Most of the solutions I found here use #ViewChild to inject the components but that doesn't work for me since I need to iterate the same behavior for all items in the *ngFor loop.
This is how the code would look like:
The template of the component rendering the incoming messages:
<div *ngFor="let item of messages">
<compile-component [template]="item.html"></compile-component>
</div>
Incoming message structure (item.html):
<my-component></my-component><div>Some html</div>
Component to compile:
#Component({
selector: 'my-component',
template: '<div>It works</div>',
styleUrls: ['./my-component.component.css']
})
export class MyComponent{ }
Output would look like this:
<div>It works</div><div>Some html</div>
I am looking for the solution for compile-component here.
Any help is much appreciated. Thanks in advance.
You should be able to do that via ComponentFactoryResolver. See angular docs about this here:
https://angular.io/guide/dynamic-component-loader

How to not render transclusion slot when it's not provided in AngularJS?

I have an AngularJS component which uses transclusion with 2 transclusion slots. One of them (messageHeading) is optional and sometimes will not be provided at all. Code below:
message.js
import template from "./messsage.html";
angular.component('message', {
transclude: {
heading: '?messageHeading',
content: 'messageContent'
},
template
});
message.html
<aside class="message">
<h2 class="message__heading"
ng-transclude="heading"></h2>
<p class="message__paragraph"
ng-transclude="content"></p>
</aside>
Question
How can I not render <h2> element at all, when messageHeading is not provided? In the current default behaviour it's rendered, only with empty contents, but I want it to never appear in the DOM.
Using ngIf on <h2> seems like a natural way to do it, but I don't know how to write a condition which would be true only when the transclusion argument was provided.
I worked it out myself. There is $transclude service, which provides method isSlotFilled(nameOfTheSlot) to check if the slot has been filled. You can use that as a value of the ngIf condition and so render <h2> only when the slot has been filled.
message.js
import template from "./messsage.html";
class MessageController {
constructor($transclude) {
this.hasHeading = $transclude.isSlotFilled('heading');
}
}
angular.component('message', {
transclude: {
heading: '?messageHeading',
content: 'messageContent'
},
controller: MessageController,
template
});
message.html
<aside class="message">
<h2 class="message__heading"
ng-if="$ctrl.hasHeading"
ng-transclude="heading"></h2>
<p class="message__paragraph"
ng-transclude="content"></p>
</aside>

Injecting Angular2 Components as a class or attribute rather than a tag

In Angular1 you could directly insert the HTML into the index.html or index.php like this:
<div ng-controller="pricingController">
{{price}} - Total Cost
</div>
In Angular2 you have to use a component which forces you to use a TemplateURL in the component.
I want to do something more similar to Angular 1's format.
Something like this directly in the HTML:
<div ngComponent="pricing-component">
{{price}} - Total Price
</div>
Rather than this:
<pricing-component></pricing-component>
If you want to use an attribute you can use the attribute selector, and a directive. A directive is a component without a template. Or better said, a component is a directive with a template:
<div pricing>
{{price}} - Total Price
</div>
And your Pricing will have this selector in the directive annotation:
#Directive({
selector : '[pricing]'
})
export class PricingDirective {}

How to make angular2 not wrap items?

All arranged, everything was fine until it became necessary to create a component which is TR.And when inserting that component onto the page Angular2 does the following:
<my-component>
<tr>...</tr>
</my-component>
It all falls down and bleeds. How can I make it not do that? Where to look? Thanks in advance.
In fact there is no more support for the "replace: true" feature of Angular1.
You could leverage an attribute in the selector to attach the component:
#Component({
selector: '[my-component]'
(...)
})
and use it this way:
<div my-component>
<tr>...</tr>
</div>

Categories

Resources