I have the following component below:
#Component({
selector: 'myselector',
providers: [ ],
directives: [ ChildComponent],
pipes: [ ],
template: '<myselector>This is {{testEmitter}}</myselector>'
})
export class ParentComponent{
#Input() testEmitter;
constructor(){
}
}
//My Child class goes as such:
#Component({
selector: 'childselector',
templateUrl: '<childselector><input type="text" (focus)="beginTest()"/></childselector>',
pipes: [],
directives: []
})
export class ChildComponent{
#Output() testEmitter: EventEmitter = new EventEmitter();
startTest: boolean = false;
constructor() {
}
beginTest(){
this.startTest = !this.startTest;
this.testEmitter.emit(this.startTest);
}
}
I am just trying to figure out how to display the value of the this.startTest variable from the ChildComponent to the ParentComponent. Right now, the {{testEmitter}} doesn't show anything in my ParentComponent html. I feel like I'm close. Your help is appreciated!
This code doesn't seem to make much sense.
#Component({
selector: 'myselector',
providers: [ ],
directives: [ ChildComponent],
pipes: [ ],
template: '<myselector>This is {{testEmitter}}</myselector>'
})
The template uses the selector <myselector> of the component it is the template of. While there are scenarios where this make sense (recursive structures like trees) this doesn't seem to be the intention here.
Also directives and pipes on #Component() are gone since a while and should be added to #NgModule() instead.
Obviously you are using some beta or RC version instead of final of Angular2 where this is still supported. I'd suggest to update to the newest Angular2 version.
This might be what you want instead:
#Component({
selector: 'parentselector',
directives: [ChildComponent],
template: '<childselector (testEmitter)="testEmitter=$event">This is {{testEmitter}}</childselector>'
})
export class ParentComponent{
#Input() testEmitter;
}
#Component({
selector: 'childselector',
templateUrl: '<input type="text" (focus)="beginTest()"/>',
})
export class ChildComponent{
#Output() testEmitter: EventEmitter = new EventEmitter();
startTest: boolean = false;
beginTest(){
this.startTest = !this.startTest;
this.testEmitter.emit(this.startTest);
}
}
Related
in this tutorial
https://www.sitepoint.com/practical-guide-angular-directives/
i am learning how to create a customised directive. i followed the steps as shown in the code posted below, but despite added the exact code as explained in the aforemenrtioned website, when i run the command
ng serve --open
i get something as shown in the image posted below.
please let me know why myCustomIf is not working. i say that myCustomIf is not working because what i got on the localhost:4200 is something as shown in the image posted
please let me know how to make the myCustomIf works as explained in the tutorial in the above posted link
app.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'ngDirective1';
name = 'Angular';
condition = false;
}
app.myCustomeIfDirective.ts:
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[myCustomIf]'
})
export class MyCustomeIfDirective{
constructor(private templateRef: TemplateRef<any>,private viewContainer: ViewContainerRef){ }
#Input()
setMyCustomIf(condition : boolean) {
if(condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
app.module:
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[myCustomIf]'
})
export class MyCustomeIfDirective{
constructor(private templateRef: TemplateRef<any>,private viewContainer: ViewContainerRef){ }
#Input()
setMyCustomIf(condition : boolean) {
if(condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
app.component.html:
<h1 my-error>Hello {{name}}</h1>
<h2 *myCustomIf="condition">Hello {{name}}</h2>
<button (click)="condition = !condition">Click</button>
image:
If you open console it should show smth like:
NG0303: Can't bind to 'myCustomIf' since it isn't a known property of
'h2'
An Angular structural directive, that is written in a short syntax(with *) and that takes one input or more inputs, must have an #Input with the same name as directive's attribute selector(other inputs follow another rule described here What is the exact grammar for Angulars structural directives), e.g.:
#Directive({
selector: '[anyAttr]'
})
export class MyCustomeIfDirective{
#Input()
anyAttr: any;
or
#Directive({
selector: '[anotherAttr]'
})
export class MyCustomeIfDirective{
#Input()
set anotherAttr(val: any) {}
Why is it so?
That's because *ngIf is just a shortcut for expanded version:
<ng-template [ngIf]="...">...
or
*anyAttr => <ng-template [anyAttr]="...">...
Now, let's look at your code:
#Directive({
selector: '[myCustomIf]'
})
export class MyCustomeIfDirective{
#Input()
setMyCustomIf(condition : boolean) {
Several things to notice:
setMyCustomIf is just a method in your case
if you convert it to a setter set MyCustomIf then MyCustomIf doesnt match myCustomIf because js is case-sensitive.
The solution is:
#Input()
set myCustomIf(condition : boolean) {
Ng-run Example
in your directive (app.myCustomeIfDirective.ts), you need to match the name of your input to the name of the directive (because the condition is passed with that attribute):
#Input("myCustomIf")
set myCustomIf(condition : boolean) {
if(condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
(note you can also change the name of the function to match the directive name)
stackblitz demo
I've implemented a child component to render a table based on a list provided via #Input(). The data is loaded via http, however the UI (child component) is not updated unless I wave my mouse over the screen. I've seen people post about implementing ngOnChanges() in my child, but I thought Angular was supposed to do this by default? Am I missing something? Why would the UI not update with this?
Child code looks something like this:
child.component.ts
#Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
})
export class ChildComponent implements {
#Input() data: any[] = [];
constructor() {}
}
child.component.html
<table>
<tr *ngFor="let item of data"><td>{{ item }}</td></tr>
</table>
Parent code that uses the component looks something like this:
parent.component.ts
#Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
})
export class ParentComponent implements OnInit {
data: string[] = [];
constructor(private endpointService: EndpointService) {}
ngOnInit() {
// response is a string array like: ['hello', 'world']
this.endpointService.loadData().subscribe((response) => {
this.data = response;
});
}
}
parent.component.html
<child [data]="data"></child>
============================= EDIT ==================================
I verified that it only fails to load when updating inside of the subscribe callback (if I set a static array, it loads just fine).
So it looks like I'm able to resolve this by running changeDetectorRef.detectChanges() in the parent component, but this feels hackish like I shouldn't have to do this. Is this a good way to resolve this? Or does this indicate something wrong with my implementation?
parent.component.ts
#Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
})
export class ParentComponent implements OnInit {
data: string[] = [];
constructor(private endpointService: EndpointService,
private changeDetectorRef: ChangeDetectorRef) {}
ngOnInit() {
// response is a string array like: ['hello', 'world']
this.endpointService.loadData().subscribe((response) => {
this.data = response;
this.changeDetectorRef.detectChanges();
});
}
}
You can also try to force change detection by forcing the value reference update via, for example, the spread operator:
this.endpointService.loadData().subscribe((response) => {
this.data = [...response];
});
hummm well .. when the component is rendered as first time it will show with the empty array becouse the api call stills happening and needs the onchanges method in child component in order to listen the complete api call and the list will re render
Seems that you have some other errors in template expressions which force the whole template to fail. Here's a stackblitz I've created and everything works: https://stackblitz.com/edit/angular-ivy-w2ptbb?file=src%2Fapp%2Fhello.component.ts
Do you have maybe some errors in console?
I replaced the service with a static string array and it worked well. I think there is problem with the observable subscription.
child.component.ts
import { Component, OnInit,Input } from '#angular/core';
#Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
#Input() data: any[] = [];
constructor() { }
ngOnInit() {
}
}
parent.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
})
export class ParentComponent implements OnInit {
data: string[] = [];
constructor() {}
ngOnInit() {
this.data = ['hello', 'world','aaa','ddd'];
}
}
I have look at several topics related to ChangeDetectionStrategy and I am confused with the different usages of it. I simply want to update data in the child component whenever the data is changed on parent component. For this aim, I see that changeDetection: ChangeDetectionStrategy.OnPush is added to the Component field of the parent, but I am not sure if I should use ChangeDetectorRef parameter or some default methods e.g. ngDoCheck() method. So, how can I perform this between parent and child components? And should I explicitly add the parameter that come from parent, or it is automatically update the #Input variables?
import { Component,
Input,
ChangeDetectionStrategy,
ChangeDetectorRef } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
#Input() data: string[];
constructor(private cd: ChangeDetectorRef) {}
refresh() {
this.cd.detectChanges();
}
}
The difference for the ChangeDetectionStrategy.OnPush, your component will only will detect the changes when something like below happens :
The Input reference changes
An event originated from the component or one of its children
Use the async pipe in the view
Run change detection explicitly
So if you don't change input's reference you have to detect changes manually.
Here is the stackblitz that shows the difference between OnPush and Default
With ChangeDetectionStrategy.OnPush your child component will perform update only if data in #Input is really updates, so you need your data to be immutable, those after updating data Angular will perform ChangeDetector and update components view.
You can check this example: https://angular-cd-immutable-example.stackblitz.io
One of the way is to create a new reference to an array by using slice method.
A full stackblitz example can be seen here.
app.component.html
<button
(click)="people.push({firstName: '1', lastName: '1'}); people = people.slice()">
+</button>
<div>
<p> {{ people | json }} </p>
</div>
<br/>
<person [persons]="people"></person>
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
people: any[] = [
{
firstName: "Alex1",
lastName: "Brown1",
age: 55,
},
{
firstName: "Foo2",
lastName: "Bar2",
age: 44,
},
{
firstName: "Fido3",
lastName: "Johnson3",
age: 14,
}
]
}
and person.component.html
<div *ngFor="let person of persons">
Hello I am {{person.firstName}} and I am {{ person.age}}
</div>
and person.component.ts
import { Component, OnInit, Input, ChangeDetectionStrategy,
ChangeDetectorRef } from '#angular/core';
class test {}
#Component({
selector: 'person',
templateUrl: './person.component.html',
styleUrls: ['./person.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonComponent implements OnInit {
#Input() persons: any;
}
I use Angular Material Portal to move element in another place.
I use cdkPortal and cdkPortalOutlet.
I don't understand why angular throw expression changed in this super simple example
import { Component, ViewChild } from '#angular/core';
import { CdkPortal } from '#angular/cdk/portal';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
#ViewChild(CdkPortal, { static: false }) portal: CdkPortal
}
Check code here:
https://stackblitz.com/edit/angular-f6sb21
open console to see error:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'portal: undefined'. Current value: 'portal: [object Object]'
You are referencing the same object to the cdkPortalOutlet. You actually need another ng-template with its ViewChild
<ng-template #templatePortalContent>Hello, this is a template portal</ng-template>
and in the ts file:
// This is the reference for the ng-template that we added
#ViewChild("templatePortalContent", { static: false }) templatePortalContent: TemplateRef<any>;
// This is the variable that will hold the new template
templatePortal;
constructor(private _viewContainerRef: ViewContainerRef, private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.templatePortal = new TemplatePortal(
this.templatePortalContent,
this._viewContainerRef
);
this.cdr.detectChanges();
}
This is for the TemplatePortal.
If you need a ComponentPortal, it means if you have already a component, then you will need to create the portal and assign it in the cdkPortalOutlet instead of templatePortal variable.
this.componentPortal = new ComponentPortal(ComponentPortalExample);
and
<ng-template [cdkPortalOutlet]="componentPortal"></ng-template>
You can check here for more info:
Here is the demo.
Trying to do child to parent communication with #Output event emitter but is no working
here is the child component
import { Component, OnInit, Output, Input, EventEmitter } from '#angular/core';
#Component({
selector: 'app-emiter',
templateUrl: './emiter.component.html',
styleUrls: ['./emiter.component.css']
})
export class EmiterComponent implements OnInit {
#Output() emitor: EventEmitter<any>
constructor() { this.emitor = new EventEmitter()}
touchHere(){this.emitor.emit('Should Work');
console.log('<><><><>',this.emitor) // this comes empty
}
ngOnInit() {
}
}
this is the html template
<p>
<button (click)=" touchHere()" class="btn btn-success btn-block">touch</button>
</p>
The console.log inside the touchHere it shows nothing
even if I put this inside the parent component it show nothing as well
parent component
import { Component , OnInit} from '#angular/core';
// service I use for other stuff//
import { SenderService } from './sender.service';
// I dont know if I have to import this but did it just in case
import { EmiterComponent } from './emiter/emiter.component'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
user: any;
touchThis(message: string) {
console.log('Not working: ${message}');
}
constructor(private mySessionService: SenderService) { }
}
and here is the html template
<div>
<app-emiter>(touchHere)='touchThis($event)'</app-emiter>
</div>
Parent component template:
<app-emitor (emitor)='touchThis($event)'></app-emiter>
In parent template #Output should be 'called', not the child method.
Also, see: https://angular.io/guide/component-interaction#parent-listens-for-child-event
Here’s an example of how we write a component that has outputs:
#Component({
selector: 'single-component',
template: `<button (click)="liked()">Like it?</button>`
})
class SingleComponent {
#Output() putRingOnIt: EventEmitter<string>;
constructor() {
this.putRingOnIt = new EventEmitter();
}
liked(): void {
this.putRingOnIt.emit("oh oh oh");
}
}
Notice that we did all three steps: 1. specified outputs, 2. created an EventEmitter that we attached
to the output property putRingOnIt and 3. Emitted an event when liked is called.
If we wanted to use this output in a parent component we could do something like this:
#Component({
selector: 'club',
template: `
<div>
<single-component
(putRingOnIt)="ringWasPlaced($event)"
></single-component>
</div>`
})
class ClubComponent {
ringWasPlaced(message: string) { console.log(`Put your hands up: ${message}`);
} }
// logged -> "Put your hands up: oh oh oh"
Again, notice that:
putRingOnIt comes from the outputs of SingleComponent
ringWasPlaced is a function on the ClubComponent
$event contains the thing that wasemitted, in this case a string
<app-emiter (emitor)="touchThis($event)" ></app-emiter>
By using #Output() you should apply the event you need to emit in the directive of the emitter component.Adding the name of the variable to the the directive and but the emitted over function inside the quotation passing the $event.
touchHere() is the method from which you are binding some value to emit with your EventEmitter. And your EventEmitter is 'emitor'.
So your code will work if you simply do the below:
<app-emiter (emitor)='touchThis($event)'></app-emiter>