At work, I have run into a problem using Angular. I have this kind of Angular component:
#Component({
selector: 'foo',
templateUrl: 'foo.html'
})
export class FooComponent {
#Input() data: string;
content: string;
ngOnInit() {
this.content = this.data;
}
setValue(data) {
this.content = data;
}
}
This is initialized from my main Angular component in a code block such as this:
this.components = [FooComponent, BarComponent, BazComponent, QuuxComponent];
Now this works so far. But if I try to call the setValue() function with this.components[0].setValue("Hello world!"); I get an error "this.components[0].setValue is not a function."
What is the reason for this and how can I fix it?
This seems like a very very weird way to work with components in angular.
You really don't want to break encapsulation by calling methods inside one component from another component.
I personally haven't seen this kind of component referencing anywhere (and have doubts it is a correct approach).
There is no reason to duplicate the data property in the content.
You can pass values in the template. Or use a service if you don't have direct access to the template.
Here is a very basic example on how to modify data from the parent using a template and #Input.
app.component.ts
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
message = "I am a message from the parent";
}
app.component.html
<app-child [content]='message'></app-child>
child.component.ts
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-child",
templateUrl: "./child.component.html",
styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
#Input("content") public content: string;
constructor() {}
ngOnInit() {}
}
child.component.html
<p>{{content}}</p>
Related
When I use #ViewChild I get the error that the component is not defined.
When I use #ViewChildren I get the error that the function from that component is not a function.
I am new to using child components in Angular so I'm not sure why it's doing this when I do have the child component defined in the parent component and when it's clearly a function in the child component.
I don't want to have to define every function from the child in the parent or else what's even the point of using a separate component.
Child Component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-mood',
templateUrl: './mood.component.html',
styleUrls: ['./mood.component.css']
})
export class MoodComponent implements OnInit {
moodColors = ['red', 'orange', 'grey', 'yellow', 'green'];
constructor() { }
ngOnInit(): void {
}
chooseMood() {
alert(this.moodColors);
}
}
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood is undefined")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChild('mood') mood: MoodComponent = new MoodComponent;
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood.chooseMood is not a function")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChildren('mood') mood: MoodComponent = new MoodComponent;
Parent View
<h2 (click)="mood.chooseMood()"></h2>
You don't explicitly initialize view children via new.
Just use:
#ViewChild('mood') mood : MoodComponent;
If that doesn't work post a Stackblitz example which I can edit to resolve the issue.
Also, using ViewChild is more of an exception in Angular, and your use of it points to a probable design issue. More likely you child component should emit via an Output to the parent.
Regarding outputs, you can do something like this - though it is hard to give a precise answer without deeper knowledge of what you are trying to achieve:
export class MoodComponent implements OnInit {
#Input() moodId: string;
#Output() chooseMood = new EventEmitter<string>();
moodClicked(){
this.chooseMood.emit(moodId);
}
}
export class CalendarComponent implements OnInit {
moodChosen(string: moodId){
console.log(moodId);
}
}
// Calendar template:
<app-mood
moodId="happy"
(chooseMood)="moodChosen($event)"
></app-mood>
1 - you have to use this code
#ViewChild('mood') mood : MoodComponent;
when you are using #ViewChildren it will return list of items with the 'mood' name then you have to use this code
mood.first.chooseMood() ;
its better use ViewChildren when there is ngIf in your element
2- no need new keyword for initialize mood variable
it would be fill after ngOnInit life cycle fires
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.
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.
}
I have created a component called Parent and Child. I want to display all the UI of ChildComponent to my ParentComponent.
child.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
testContent = 'child component content...';
constructor() { }
ngOnInit() {
}
}
child.component.html
<p>{{testContent}}</p>
parent.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss'],
directives: [ChildComponent]
})
export class AdminComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
parent.component.html
<div>
Lorem ipsum
<app-child></app-child>
</div>
I want to display the contents of child component inside parent component. However, I am encountering an error in Angular 4
"Object literal may only specify known properties, and 'directives'
does not exist in type 'Component'."
Do you have any idea what is the alternative property to add child component?
Remove directives from the parent component and add the child component to the module declarations array where the parent component lives.
Directives and pipes in #component are deprecated since angular RC6. Just remove it from the component.
#Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss']
})
I'm trying to learn Angular 2, and I'm running into a weird issue while following this documentation: https://angular.io/docs/ts/latest/guide/user-input.html for binding button clicks to a method on my controller.
I started off with their click event binding example. I created a simple app with the angular cli with ng new test-app and modified it so that it contains a single button that when I click, just adds a message to the page.
I have two components, the app component and the message components shown here:
message.component.html:
<p>{{message}}</p>
message.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-message',
templateUrl: './message.component.html',
styleUrls: ['./message.component.css']
})
export class MessageComponent implements OnInit {
message: string;
constructor() {
this.message = 'some initial message';
}
ngOnInit() {
}
}
app.component.html:
<button (click)="addMessage()">Click</button>
<app-message *ngFor="let message of messages"></app-message>
app.component.ts:
import { Component } from '#angular/core';
import { MessageComponent } from './message/message.component';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
messages = [];
addMessage() {
const newMessage = new MessageComponent();
newMessage.message = 'Something else entirely';
this.messages.push(newMessage);
}
}
When I run this with ng serve, the button appears as expected, but when I click the button, only the message some initial message appears despite being given a different value by my app controller. While doing a search I found a different way to do the one-way databinding by replacing the string interpolation with: <p [innerText]="message"></p> but the result is the same.
I'm kinda at a loss here as to why it won't display the updated value of Something else entirely.
MessageComponent component should take message as #Input:
export class MessageComponent implements OnInit {
#Input() message: string;
}
AppComponent should send this input to its child like
<app-message *ngFor="let message of messages"
[message]="message.message"></app-message>