Angular html nesting - html

Let's say I have in some upper level class some angular template code that looks like this
<outer-component>
<a></a>
</outer-component>
Where <a> can be any module that extends a certain interface defined elsewhere, is there a way for <outer-component> be able to take <a> or whatever is placed inside the tags and communicate with it specifically be able to listen to functions or bind to variables in a way that is as succinct as the snippet above?

If you want to share data between a parent and a child (hierarchical relationship) you can use EventEmitter to allow the parent to get data from the child.
In the child component:
import { Component, Input, Output, EventEmitter } from 'angular/core';
#Component({
selector: 'app-child',
template: `
<h3>Child</h3>
Say {{message}}
<button (click)="sendMessage()"></button>
´,
styleUrls: ['pathToStyles.css']
})
export class ChildComponent {
message: string = "Hello world";
#Output() messageEvent = new EventEmitter<string>();
constructor() {}
sendMessage() {
this.messageEvent.emit(this.message);
}
}
In the parent component:
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['pathToStyles.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}

Related

I need to have a 'container' template that has to show MyComponent upon certain condition 'externalCondition' MyComponent uses Form and formValidation

container.html
<div ngIf="externalCondition"> <!--Initially this is false. Later became true --!>
<my-component #MyComponentElem > </my-component>
<button [disabled]= "!myComponentElemRef.myDetailsForm.valid" (click)="myComponentElemRef.AFunctionInsideComponent()"> </button>
</div>
container.ts
#Component({
selector: 'my-component',
templateUrl: './comm-roleplay-end2end.html',
styleUrls: ['./comm-roleplay-end2end.scss']
})
export class Container {
#ViewChild('MyComponentElem', { static: true }) private myComponentElemRef: MyComponent;
}
mycomponent.ts
#Component({
selector: 'my-component',
templateUrl: './my-component.html',
styleUrls: ['./my-component.scss']
})
export class MyComponent {
public externalCondition:Boolean = false;
public myDetailsForm: FormGroup;
constructor()
{
//my form builder
}
//
public AFunctionInsideComponent()
{
}
}
Based on myDetailsForm.valid, I need to turn on something in my container.
Issue is that myComponentElemRef is 'undefined' because the element is not created initially. It is visible only after 'externalCondition' becomes true. Upon button click, I need the FormGroup of MyContent to be visible.
But it gets stuck in script error due to
TypeError: reading undefined (myComponentElemRef)
Please suggest the best mechanism to handle the situation
try using [hidden] instead of ngIf, that way the component is created but not visible

Angular: How to send string values to another component using EventEmitter?

I have a parent component and a child component, I want to send 2 values from the child component via EventEmitter to the parent component as I want to use the printMessages() function there.
I have a click event on the child component which emits 2 string values, but I'm unsure how to connect the emit event to the parent component and for the printMessages() function to run. How can the parent component read these values into the "printMessages()" function and for that function to run after the values have reached it? As you can see, I currently have no HTML in the parent component, perhaps I need to do something there?
Parent Component:
import { Component } from '#angular/core';
#Component({
selector: 'parent-component',
template: ``,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
// Function in which I want to send values from the child component.
printMessages(string1: string, string2: string) {
console.log(string1, string2);
}
Child Component:
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'child-component',
template: `
<button (click)="sendStrings()">Send Strings</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Output() message = new EventEmitter();
constructor() { }
sendStrings(){
this.message.emit({string1:"Hello", string2:"World!"});
}
}
You need to react on the child components event within the template and call the method.
import { Component } from '#angular/core';
#Component({
selector: 'parent-component',
template: `<child-component (onMessage)="printMessages(e)"></child-component>`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
printMessages(event) {
// strings are included in event object
}
}

Angular: sanitizer.bypassSecurityTrustHtml does not render attribute (click)

I try to render a button and it works fine, but when I click the button it doesn't execute alertWindow function, help!:
app.component.ts:
import {
Component,
ElementRef,
OnInit,
ViewEncapsulation } from '#angular/core';
import { DomSanitizer, SafeHtml } from "#angular/platform-browser";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.ShadowDom,
})
export class AppComponent implements OnInit {
public content: SafeHtml;
constructor(private sanitizer: DomSanitizer) {}
async ngOnInit() { this.renderButton(); }
alertWindow() { alert("don't work"); }
renderButton() {
this.content =
this.sanitizer.bypassSecurityTrustHtml(`
<button (click)='connectWallet()' class="button">
Connect your wallet
</button>`);
}
app.component.ts;
<div [innerHTML]="content"></div>
Solution
Based on what I understand you wanted to display HTML dynamically at runtime? then solution is to use
ComponentFactoryResolver
and ViewContainerRef
It will be better if you can provide more details, what you are trying to achieve, so that people can guide you
Why it didn't work?
It doesn't work because it is outside of angular, when you use innerHTML then whatever you passed to it is pure vanilla HTML and JavaScript
Try this example
(window as any).alertWindow = function () {
alert("don't works");
};
#Component({...})
export class AppComponent {
...
renderButton() {
this.content = this.sanitizer.bypassSecurityTrustHtml(`
<button onclick='alertWindow()' class="button">Connect your wallet</button>
`);
}
}
It works right?
As you can see I have moved alrertWindow function outside of component's class and added to window variable and also changed (click) to onclick

#ViewChild("childRef") childElementRef -- Cannot read properties of undefined

I am trying to understand how to use ´#ViewChild´ in an Angular app, so I have created two components: parent (base) and child with the following code:
import { Component, ViewChild, AfterViewInit } from '#angular/core';
#Component({
selector: 'app-base',
templateUrl: './base.component.html',
styleUrls: ['./base.component.css']
})
export class BaseComponent implements AfterViewInit {
#ViewChild("childRef") childElementRef: any;
childComponentTitle = 'fake child component title';
constructor(public router: Router) { }
ngAfterViewInit(): void {
this.childComponentTitle = this.childElementRef.childComponentTitle;
}
changeChildComponentTitle() {
this.childElementRef.changeComponentState('actual child component title');
}
}
<h1>parent (base) component</h1>
<app-child #childRef></app-child>
<h2>{{ childComponentTitle }}</h2>
<button (click)="changeChildComponentTitle()">Show Real Child Component Title</button>
import { Component } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent {
childComponentTitle: string = "";
changeComponentState(newState: string) {
this.childComponentTitle = newState;
}
}
<h1>child component</h1>
<h2>{{ childComponentTitle }}</h2>
If I understand it correctly, once I click the button (which is managed by the ´BaseComponent´) it should invoke the ´changeComponentState´ method (which is managed by the ´ChildComponent´) and that should display ´actual child component title´ on the page.
This does happen, but upon the app loading, I immediately get this error:
ERROR TypeError: Cannot read properties of undefined (reading 'childComponentTitle') at PageoneComponent.ngAfterViewInit
Could someone please help me figure out why this error is thrown?
You should add an if before trying accessing to the child :
ngAfterViewInit(): void {
if (this.childElementRef) {
this.childComponentTitle = this.childElementRef.childComponentTitle;
}
}

Component Interaction #Input

I would like a component to send input to another component. Below is the code .ts and .html. of the two components.
Now the problem is that the html page of the parent component also shows the html part of the child component ... I want the component to pass only one string to the child component
Parent.ts
import ...
#Component({
selector: 'app-parent',
templateUrl: './parent.html',
styleUrls: ['./parent.css']
})
export class ParentComponent implements OnInit {
sostegno : string;
constructor() { }
ngOnInit() { }
avvia1() {
this.sostegno = "xxx";
this.router.navigate(['./xxx'], { relativeTo: this.route });
}
avvia2()
this.sostegno = "yyy";
this.router.navigate(['./yyy'], { relativeTo: this.route });
}
}
Parent.html
<div>
...
</div>
<app-child [sostegno]="sostegno"></app-child>
Child.ts
import ...
#Component({
selector: 'app-child',
templateUrl: './child.html',
styleUrls: ['./child.css']
})
export class ChildComponent implements OnInit {
#Input() sostegno : string;
constructor() { }
ngOnInit() {
console.log(this.sostegno);
}
}
There are some changes which you need to make because looking at the code which your currently have it seems incomplete.
You are using this.router without injecting the Router class in your constructor.
You are using this.route without injecting the ActivatedRoute class in your constructor.
To test that your parent > child interaction is working you can remove your param and instead place a test for the html
<app-child [sostegno]="'Test'"></app-child>
This should work for your ngOnInit function which is inside of your child component. If this works all you need to do now is either initialize sostegno in your parent component else your console log inside your child component will not reflect the changes when you call avvia1 or avvia2 inside of your parent class.
Hope this helps!