Angular 5 Child component exposes properties but not methods - javascript

Any idea why I can get hold of all child component properties but not any of its methods?
I need to call a method on the child component (without using a service or an eventEmmiter) to validate its state and return a property.
Parent
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class MyParent {
#ViewChild('childEl') childEl: PhoneInputComponent;
getChildValue() {
const value = this.childEl.getMyValue();
console.log(value);
console.log(this.childEl);
}
}
Child
#Component({
selector: 'phoneinput',
templateUrl: 'phoneinput.html'
})
export class PhoneInputComponent {
private onKey = new Subject<string>();
#Input() phone: string;
private errorMessage: string;
getMyValue() {
return 'hello from child';
}
}
console.log of value:
undefined
console.log of this.childEl:
PhoneInputComponent{onKey{}:Subject{}, phone:null, errorMessage:null}
no way to make getValue() available

Modify your code
#Component({
selector: 'page-login',
templateUrl: 'login.html'
})
export class MyParent {
#ViewChild(PhoneInputComponent) childEl: PhoneInputComponent;
getChildValue(){
const value = this.childEl.getMyValue();
console.log(value);
console.log(this.childEl);
}
Example:https://stackblitz.com/edit/angular-v4qqtt

Related

Does this approach have a chance, when I use instance of parent component as property of child component?

I tried to use instance of parent component in child component via constructor. In other words, I create instance of parent component class as private property and use its properties, methods etc.
Besides that, I can affect to values of parent component properties directly without using Input, Output decorators, event listeners etc.
Parent
#Component({
selector: 'parent-component',
templateUrl: './parent-component.component.html',
styleUrls: ['./parent-component.component.scss']
})
export class ParentComponent implements OnInit {
someParentProperty: number = 10;
constructor() {}
ngOnInit() {}
someParentMethod = (num) => num**2;
}
Child
import { ParentComponent } from '../parent-component';
#Component({
selector: 'child-component',
templateUrl: './child-component.component.html',
styleUrls: ['./child-component.component.scss']
})
export class ChildComponent implements OnInit {
someChildProperty: number;
constructor(pc: ParentComponent) {}
ngOnInit() {
this.someChildProperty = this.pc.someParentMethod(this.pc.someParentProperty);
}
}
That's rather comfortable, but I'm not sure, that it's a best practice and right approach.
Could someone explain minuses of this one?
Why don't you use a service?
So that you can access your required method from both child and parent components.
E.g: A common service:
#Injectable({
providedIn: 'root',
})
export class CommonService {
someParentMethod(num) {
return num**2;
}
}
At ParentComponent:
export class ParentComponent implements OnInit {
constructor(private commonService: CommonService) {}
ngOnInit() {
console.log(commonService.someParentMethod(2));
}
}
You can do the same at ChildComponent you can do the same.
It's the best way for sharing.
You can find detail about services here.

Why I don't receive my event when I pass event from my child component using eventEmitter?

I have tried to the event using eventEmitter but in somehow, the event is not being pass.
Here is the child component where I try to pass the object with eventEmitter
import { Component, OnInit,Input,Output,EventEmitter } from '#angular/core';
#Component({
selector: 'app-favorite',
templateUrl: './favorite.component.html',
styleUrls: ['./favorite.component.css']
})
export class FavoriteComponent implements OnInit {
#Input('isFavorite') isFavorite?: boolean
#Output() change = new EventEmitter();
constructor() { }
ngOnInit(): void {
}
onClick(){
this.isFavorite = !this.isFavorite
console.log(this.isFavorite)
this.change.emit({newValue: this.isFavorite});
}
}
Here is my template of my Child component:
<p>favorite works!</p>
<button (click)=onClick()>click</button>
Here is the parent component where I want to pass my event to:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angular-ly';
post = {
title: "Title",
isFavorite:false
}
onFavoriteChange(isFavorite:any){
console.log("Favourite change",isFavorite);
console.log(isFavorite)
}
}
Here is the template of my parent component:
<app-favorite [isFavorite]="post.isFavorite" (change)="onFavoriteChange($event)"></app-favorite>
I try to the print the event that I pass from my child component into the console and here is what I got:
true
app.component.ts:15 Favourite change undefined
app.component.ts:16 undefined
false
app.component.ts:15 Favourite change undefined
app.component.ts:16 undefined
true
app.component.ts:15 Favourite change undefined
app.component.ts:16 undefined
false
app.component.ts:15 Favourite change undefined
app.component.ts:16 undefined

Communicating between child and parent components with ngAfterViewInit

So I try to communicate between components with ngAfterViewInit.
And I want to use the property
participant: ParticipantInfoDTO;
also using in other component. So I try it like this
#Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.scss'],
template: 'Example: {{participant}}<app-echeq-selector></app-echeq-selector>'
})
export class DetailComponent implements OnInit, AfterViewInit {
#ViewChild(EcheqSelectorComponent) echeqReference: ParticipantInfoDTO;
participant: ParticipantInfoDTO;
constructor(private dialog: MatDialog, route: ActivatedRoute) {
this.participant = route.snapshot.data['participant'];
}
ngOnInit() {
}
ngAfterViewInit(): void {
this.participant = this.echeqReference;
}
}
And in child component(EcheqSelectorComponent) I want it using like this:
<p> selected id:{{participant}} </p>
But I get an error on this line:
#Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.scss'],
template: 'Example: {{participant}}<app-echeq-selector></app-echeq-selector>'
})
saying:
Component 'DetailComponent' must not have both template and
templateUrlng(0)
Thank you
Remove template property in your component decorator.
In detail.component.html, add all the elements you want so that your html file looks like this:
Example: {{participant}}<app-echeq-selector></app-echeq-selector>
If you want to pass a property to app-echeq-selector component then you can use property binding like this:
<app-echeq-selector [participant]="participant"></app-echeq-selector>
In echeq component.ts:
export class echeq... {
#Input() participant;
// you can use this participant property as you want now.
}

how to update the object in child component when object property in changes in the parent component

I have 2 components
Parent component is
#Component({
selector: 'parent',
template: `
<child [obj]="obj"> </child>
`,
styleUrls: [''],
})
export class parentComponent implements OnInit{
obj = {
id:1;
name:'abc'
}
}
and child component is
#Component({
selector: 'child',
templateUrl: '',
styleUrls: [''],
})
export class ChildComponetimplements OnInit{
#Input() obj : any;
}
If I change any of the property in the obj in the parent, it is not getting updated in the child component.
Maybe because the obj reference is not changed.
Please suggest me the solution for this.
You have to use ngOnChanges like below
Parent Component
export class AppComponent {
obj = {
id:1,
name:'abc'
}
name = 'Angular';
changeObject() {
this.obj.id++;
}
}
Parent Template
<button (click)="changeObject()">Change Object</button>
<hello name="{{ name }}" [obj]="obj"></hello>
Child Component
import { Component, Input, OnInit, OnChanges } from '#angular/core';
#Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1><p>{{ obj | json }}</p>`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent implements OnInit, OnChanges {
#Input() name: string;
#Input() obj;
ngOnInit() {
}
ngOnChanges(changes) {
this.obj = changes.currentValue ? changes.currentValue.obj : this.obj;
}
}
Working example is here in Stackblitz
Stackblitz Demo
obj = {
id: 1,
name: 'Hello'
}
changeObject() {
this.obj.name = 'Hello change';
}
Use ngOnChanges to listen to changes of input properties. You will pass the object from parent to child and whenever there is any change in the object that you have passed as input to the child will give SimpleChanges object in ngOnChanges hook of child component.
interface OnChanges {
ngOnChanges(changes: SimpleChanges): void
}
Example
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
// TODO(issue/24571): remove '!'.
#Input()
prop !: number;
ngOnChanges(changes: SimpleChanges) {
// changes.prop contains the old and the new value...
}
}
For more on ngOnChanges

How can I pass a variable from #Input to a service in an Angular2 component>

So, What I am trying to do seems like it would be trivial. And it probably is. But I can't figure it out. My question is:How can I pass a variable from #Input to a service in an Angular2 component? (Code has been simplified)
My component is as follows:
import { Component, Input } from '#angular/core';
import { CMSService } from '../cms.service';
#Component({
selector: 'cmstext',
templateUrl: './cmstext.component.html',
styleUrls: ['./cmstext.component.css']
})
export class CMSTextComponent {
constructor(private cms: CMSService) { }
#Input() id : string;
content = this.cms.getContent(this.id); // this.id is NULL so content is NULL
}
And then my service:
import { Injectable } from '#angular/core';
#Injectable()
export class CMSService {
constructor() { }
getContent(textId:string) : string {
this.text = textId; // textId is NULL so this.text returns NULL
return this.text;
}
}
My component template:
<p>id: {{id}}</p>
<p>Content: {{content}}</p>
When <cmstext id="4"></cmstext> is added to another component template the output is:
id: 4
content:
I'm just diving into Angular2 any help or suggestions would be greatly appreciated!
Just make it a setter and put the code there:
#Input()
set id(value : string) {
this.content = this.cms.getContent(value);
}
As pointed out by #Kris Hollenbeck,ngOnInit() was the answer. My final code looked like this. The component now passed the variable to the service.
import { Component, Input, OnInit } from '#angular/core';
import { CMSService } from '../cms.service';
#Component({
selector: 'cmstext',
templateUrl: './cmstext.component.html',
styleUrls: ['./cmstext.component.css']
})
export class CMSTextComponent implements OnInit {
public content : string;
#Input() id : string;
constructor(private cms: CMSService) { }
ngOnInit() {
this.content = this.cms.getContent(this.id);
}
}
This assigned the data from the service to the variable "content" and the id passed from the element attribute to the variable "id". Both variables were then accessible to the template!

Categories

Resources