Access the content of <ng-template> - javascript

Let us say that we have a child component that takes a template as an input :
In child.html:
<ng-container *ngTemplateOutlet=“formTemplate”></ng-container>
In child.ts:
#Component({
selector: ‘child’,
templateUrl: ‘./child.html’
})
export class ChildComponent {
#Input() formTemplate: TemplateRef<HTMLElement>;
}
In the parent component.html:
<child [formTemplate] = “tmpl”></child>
<ng-template #tmpl>
//some reactive form will be passed here
</ng-template>
In parent.ts:
#Component({
selector: ‘parent’,
templateUrl: ‘./parent.html’
})
export class ParentComponent {
}
The form is displayed correctly inside the child component.
Is there any way to access the form inside the child component ?
The child needs to access the form to perform some logic when the form is invalid for example.

Related

How to set value to parent component property when a button is clicked in Child component in Angular

I am new to Angular 7 (2+) & trying my hands on #Input & #Output. However, passing data from Parent to Child component via #Input is understood & in place.
However, very basic on the other hand passing data from Child to Parent component via using #Output concept is understood & but the implementation is not getting right.
Here is what I am trying. When a button is clicked in the
Child component, a property in the parent component should be
converted to Upper case & displayed.
ChildComponent.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
})
export class ChildComponent implements OnInit {
#Input('child-name') ChildName: string;
#Output() onHit: EventEmitter<string> = new EventEmitter<string>();
constructor() { }
ngOnInit() {}
handleClick(name): void {
this.onHit.emit(name);
}}
ChildComponent.html
<h1> child works! </h1>
<button (click)="handleClick('eventEmitter')"> Click me! </button>
ParentComponent.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
console.log(e);
}}
ParentComponent.html
<div style="text-align:center">
<app-child [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b (onHit)="catchChildEvent($event)">
<i> {{customerName}} </i>
</b>
No error in console or binding
From the above snippet, when the button in Child Component is clicked the parent Component's property CustomerName should get the value & displayed?
Example: https://stackblitz.com/edit/angular-3vgorr
(onHit)="catchChildEvent($event)" should be passed to <app-child/>
<app-child [child-name]="HelloChild" (onHit)="catchChildEvent($event)"></app-child>
You are emitting event from app-child component so attach the handler for app-child component to make it work.
<div style="text-align:center">
<app-child (onHit)="catchChildEvent($event)" [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b>
<i> {{customerName}} </i>
</b>
And within the ts file update value of cutomerName property.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
this.customerName = e;
}
}
You should move (onHit)="catchChildEvent($event)" to app-child in parent html:
<app-child [child-name]="HelloChild"
(onHit)="catchChildEvent($event)>
</app-child>

Need to pass value from <ng-content [value]="Hello World"></ng-content> to parent

Trying to pass a value from child to parent via ng-content
Parent Component
<block-1>
<block-2 [value]="{{I want this value from child}}"></block-2>
</block-1>
Block-1 Component
<div *ngFor="let value form values">
<ng-content {{I want to pass this value to parent {{value}}></ng-content>
</div>
Use EventEmitter with #Output decorator to pass data from child to parent.
ChildComponent:
export class ChildComponent {
#Output() str = new EventEmitter<string>();
pass(str: string) {
this.str.emit('Pass this string to parent');
}
}
ParentComponent:
#Component({
selector: 'app-parent',
template: `
<h2>Pass data?</h2>
<app-child
(str)="onPassed($event)">
</app-child>
`
})
export class ParentComponent {
onPassed(str: string) {
console.log(str);
}
}
Fire the event (pass()) when you are looping <div *ngFor="let value form values">.

Angular pass input variable to another component on button click

I come from a Python background, but I started trying to learn Angular and I'm really having trouble. Working between components is confusing to me and I can't figure it out. I made a good example that I think if someone helped me with it would go along way towards understanding Angular.
I just have two components: a "header" component and an app component. In the header component, I ask for the user's name and they click a button, and then it should show "Hello {{name}}" in the next component. I cannot get it to work to say the least and it's really frustrating. The Header part seems to work okay, but it's just not communicating with the other component at all. Neither the button part or the "name" part are working so I am clearly misunderstanding something I need to do when it comes to listening from the parent component.
Here is my Header HTML:
Name: <input type="text" id="userInput" value="Joe">
<button (click)=showName()>Show More</button>
Here is my Header TS:
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
bodyDiv = false;
inputName = '';
#Output() buttonClicked = new EventEmitter();
constructor() { }
ngOnInit() {
}
showName() {
console.log('showName clicked.');
this.bodyDiv = true;
this.inputName = document.getElementById('userInput').value;
console.log(this.inputName);
console.log(this.bodyDiv);
this.buttonClicked.emit(this.bodyDiv);
this.buttonClicked.emit(this.inputName);
}
}
Here is the main Component's HTML:
<app-header (buttonClicked)='showNextComponent($event)'></app-header>
<p *ngIf="![hiddenDiv]" [inputName]="name">Hello {{ name }} </p>
Here is the main component's TS:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
hiddenComponent = true;
title = 'show-button';
showNextComponent() {
console.log('Button clicked.');
this.hiddenComponent = false;
console.log(this.hiddenComponent);
}
}
So who can show me what I'm doing wrong and help figure out Angular a little better? :) Thank you!
replace showName function with below code :
showName() {
console.log('showName clicked.');
this.bodyDiv = true;
this.inputName = document.getElementById('userInput').value;
console.log(this.inputName);
console.log(this.bodyDiv);
this.buttonClicked.emit(this.inputName);
}
replace below code in your main component.
name:string
showNextComponent(value:string) {
this.name = value;
}
replace below code in your html :
<app-header (buttonClicked)='showNextComponent($event)'></app-header>
<p *ngIf="name">Hello {{ name }} </p>
Please let me if you have any question and I would suggest try to use ngmodel or something else instead of directly communicating with the DOM.
Here is a slightly modified and working sample: https://stackblitz.com/edit/angular-jhhctr
The event emitter in the header component emits the name (string) which is the $event in showNextComponent($event). You have to capture this in the main component and assign it to a local variable to be able to use it in the main component's template as {{name}}
[inputName]="name" is incorrect. You can pass values like that to angular components not to actual HTML DOM elements.
There are couple of ways to communicate from one component to another in angular - Using #Input()in your child component will expects an input from parent component and #Output() from your child component will emit an event from the child component
So in your case if you want to pass a value from parent to child you need to use input property or decorator on your child property - I will provide you the code but just go through proper guidance from the link provided this will make you to create better angular applications https://angular.io/guide/component-interaction
First you need to swap your components your header component should be your parent and the child component will be your main component - if you want to work in the same way just move your codes vice versa
Header html
Name: <input type="text" id="userInput" name='userInput' [(ngModel)]='inputName' value="Joe">
<button (click)=showName()>Show More</button>
<div [hidden]='bodyDiv'>
<app-header [bindName]='inputName'></app-header>
</div>
Header Component
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
bodyDiv = true;
inputName = '';
constructor() { }
ngOnInit() {
}
showName() {
bodyDiv = false;
}
}
Main Component Html
<p>Hello {{ bindName }} </p>
Main component ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
#Input()
bindName: string;
}
In your header component the inputName property will be binded using two way data binding where i used [(ngModel)]='inputName' so whatever you enter in the input text it will be updated in your inputName property
Now we need to do only one thing just to show your child component with any event - so when the button is clicked the div with [hidden] property will be false and it will be displayed and as we pass the inputName to the child Component it will be updated
And finally the child component will be displayed and the input written in the text will be updated in the child component - when the child component html displays the bindName will be updated and there will be result you expected
That's all I think this should work well - Try this and let me know - Thanks Happy coding !!
Don't forget to look into the link above where you can see many types of component interactions

How can I access an already transcluded ContentChild?

I have a angular component app-b that is used within a component app-a that is used in the app-component. The app-component has some content in app-a, app-a transcludes this with ng-content into app-b, app-b shows it with another ng-content - but how can I access this content within the component (and not it's template)?
I would think that ContentChild is the correct approach but appears to be wrong.
Example:
https://stackblitz.com/edit/angular-ddldwi
EDIT: Updated example
You cannot query by tag name with #ContentChild decorator. You can query either by template variable, component or directive selector.
app-a.component.html
<app-b>
<ng-content></ng-content>
<p #myContent>This is a content child for app-b.</p>
</app-b>
app-b.component.ts
import { Component, AfterContentInit, ContentChild } from '#angular/core';
#Component({
selector: 'app-b',
templateUrl: './b.component.html',
styleUrls: ['./b.component.css']
})
export class BComponent implements AfterContentInit {
#ContentChild('myContent') contentchild;
ngAfterContentInit() {
console.log(this.contentchild);
}
}
Live demo
I recommend sharing the data between components. For example, move your data (E.G. dummyName) into a service. Then add the service to each component (where you need the shared data).
Service:
import { Injectable } from '#angular/core';
#Injectable()
export class DataShareService {
public dummyName: String = 'one';
constructor() { }
}
Add the new service to app.module.ts:
providers: [DataShareService],
Child Component:
import { DataShareService } from './data-share.service';
import { Component } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html'
})
export class ChildComponent {
constructor(public ds: DataShareService) { }
toggle() {
this.ds.dummyName = this.ds.dummyName === 'one' ? 'two' : 'one';
}
}
Child Component template (html):
<p> {{ds.dummyName}}</p>
<button (click)="toggle()">Click Me to Toggle Value</button>
Parent Component:
import { Component, OnInit } from '#angular/core';
import { DataShareService } from './data-share.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
constructor(public ds: DataShareService) {}
displayNumber() {
return this.ds.dummyName === 'one' ? 1 : 2;
}
}
Parent Component template (html):
<p> this is the parent component:</p>
<div> value as string: {{ ds.dummyName }} </div>
<div> value as number: <span [textContent]="displayNumber()"></span></div>
<hr>
<p> this is the child component:</p>
<app-child></app-child>
Note! The child component toggle() function demonstrates how you can change the data. The parent component displayNumber() function demonstrates how to use the data, independent of it's display (I.E. as just pure data).
This appears to be impossible due to a bug in Angular:
https://github.com/angular/angular/issues/20810
Further reference:
https://www.reddit.com/r/Angular2/comments/8fb3ku/need_help_how_can_i_access_an_already_transcluded/

*ngFor on elements of ng-content Angular

I was wondering if is it possible to create a iteration of certain elements of . I need a different css class to every element of the ng-content so i need to make a loop of every element of ng-content. Is it possible?
Right now i pass a parameter to the child element to enumerate him, but i would like to do it without the number. This is my code:
<sys-tab [tabs]="['Principal', 'Complementar']">
<sys-tab-content [num]="1">
<sys-panel header="Dados Gerais">
<sys-input-text header="Nome" tam="1"></sys-input-text>
<sys-input-mask header="CNPJ"></sys-input-mask>
<sys-input-mask header="CNES"></sys-input-mask>
<sys-input-mask header="Telefone"></sys-input-mask>
<sys-input-text header="Email"></sys-input-text>
</sys-panel>
</sys-tab-content>
<sys-tab-content [num]="2">
<sys-input-text header="Email"></sys-input-text>
</sys-tab-content>
</sys-tab>
As you can see, to the child i passs the number so i can recognize who is him, but i want to create a loop to the ng-coontent so i can add a different class to every "sys-tab-content"
Simple List Example with dynamic ngTemplate
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
items = [ { name: 'abc' }, { name: 'cdf' }, { name: 'fgh' } ];
}
app.component.html
<nu-list [itemTpl]="itemTpl" [items]="items"></nu-list>
<ng-template let-item #itemTpl>
<h1>{{item.name}}</h1>
</ng-template>
list.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'nu-list',
templateUrl: './list.component.html'
})
export class ListComponent {
#Input() itemTpl;
#Input() items;
}
list.component.html
<ng-container *ngFor="let item of items">
<ng-container *ngTemplateOutlet="itemTpl; context: {$implicit: item}"></ng-container>
</ng-container>
Example Link: https://stackblitz.com/edit/angular-list-ngtemplateoutlet
There's two ways of adding a "ngFor" or iterate trough the childs.
One is by transclusion: and the other one is by checking the ViewChildren of the parent component.

Categories

Resources