I get the following error when running ng test:
Can't bind to 'foobar' since it isn't a known property of 'app-my-item'.
1. If 'app-my-item' is an Angular component and it has 'foobar' input, then verify that it is part of this module.
2. If 'app-my-item' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component to suppress this message.
Component:
import { Component, Input, OnInit, Renderer, EventEmitter, Output, OnChanges, SimpleChanges } from '#angular/core';
#Component({
selector: 'app-my-item',
templateUrl: './app-item.component.html',
styleUrls: ['./app-item.component.scss'],
})
export class MyItemComponent implements OnInit, OnChanges {
#Input() video;
#Input() foobar = <string> '';
buttonClass = 'green';
// Code....
ngOnChanges({ foobar: { currentValue } }: SimpleChanges): void {
if (currentValue) {
this.buttonClass = (
currentValue === this.video.key ? 'blue' : 'green'
);
}
}
// Code....
}
View:
<app-my-item
//Code ....
[foobar]="foobar"
//Code ....
>
</app-my-item>
I don't understand why I'm getting the error because foobar is in fact defined in the component as an input. How to fix this error?
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 am trying to pass data from the parent to the child. I get the correct output when I pass the string (data1) from parent to child but when I try to display object (data) the console says it's unidentified and nothing is displayed on the screen.
Parent Component:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data={
name:'Charles',
age:24,
email:'charles#gmail.com'
};
data1 = "Charles";
}
<app-contact [sendName] ="data"></app-contact>
Child Component
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent implements OnInit {
#Input() sendName;
constructor() { }
ngOnInit(): void {
console.log(this.sendName);
}
}
<h1 class="text-center">Contact Form</h1>
<h3 class="pb-5">from the parent {{sendName.name}}</h3>
<h3 class="pb-5">from the parent {{sendName.age}}</h3>
please someone help me.
Thank you
If your data (in parent component) variable has static value then your child component will be
#Input() sendName;
ngOnInit(): void {
console.log(this.sendName);
}
If your data (in parent component) variable will assign from dynamic value then your child component will be
(Assume you have an API to call and update sendName variable on API response, but our child component is initialised without waiting the API response then we need to trigger #Input() to send updated data in child component )
#Input() set sendName(value: any) {
if(value){
console.log(value);
this.name = value
// call any function from here
}
}
name:any; // use this variable at anywhere
constructor(){
}
you might be rendering the child, before setting the value in data in parent
in Child component import Onchanges and get from ngOnchanges()
import { Component, OnInit, Input ,Onchanges} from '#angular/core';
export class ContactComponent implements OnInit,Onchanges {
#Input() sendName; constructor() { }
ngOnChanges() {
console.log(this.sendName); }
I have a child TestComponent component as follows:
import { Component, OnInit, Input } from '#angular/core';
import { ApiService } from '../../../api.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
constructor(private apiService: ApiService) { }
testDisplayMessage = 'No data to show';
ngOnInit() {
}
getMessage(param: string) {
this.callingTest = true;
this.apiService.getTest( param ).subscribe( data => {
this.setTestDisplayMessage( data );
this.callingTest = false;
}, err => {
console.log( JSON.stringify( err ) );
this.setTestDisplayMessage( 'Failed to get data' );
this.callingTest = false;
} );
}
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
}
}
contents of test.component.html
<p style="padding: 10px;">{{ testDisplayMessage }}</p>
Use in parent componet :
Trigger JS Code in parent component on button click,
import { TestComponent } from './test/test.component';
....
.....
#Component({
providers: [ TestComponent ],
templateUrl: 'parent.component.html'
})
export class ParentComponent implements OnInit {
...
constructor(private testComponent: TestComponent) { }
...
// Button on parent template triggers this method
getMessage() {
this.testComponent.getMessage('Hello');
}
...
}
Html tag added in parent component,
<app-test></app-test>
When I debugged above code trigger point, call to setTestDisplayMessage() happens the field testDisplayMessage in TestComponent gets changed but UI shows the old message 'No data to show', why is the message on change does not reflect on UI template? Or this is not the way it is supposed to get used? Shall I use #Input
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
Use #ViewChild()
In html file:
<app-test #childComp></app-test>
In parent component.ts file
import { Component, OnInit, ViewChild } from '#angular/core';
....
.....
#Component( {
templateUrl: 'parent.component.html'
} )
export class ParentComponent implements OnInit {
#viewChild('childComp') childComp: any;
constructor() { }
...
// Button on parent template triggers this method
getMessage() {
this.childComp.getMessage('Hello');
}
...
}
Update:
Based on the pointers given in the following answers as well as comment sections, I changed my component as #ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using #ViewChild, so code changes as follows,
Earlier wrong code
constructor(private testComponent: TestComponent) { }
Solution
#ViewChild(TestComponent)
testComponent: TestComponent;
I found this article useful.
definitely use #Input() but on set method
#Input()
set someProperty(value) {
// do some code
}
now every time you pass new value here, code will run
basically, your approach is wrong, please use Input() or Services to share data between components.
however, if you want to make ur code work, the below may work
import change detector
constructor(private cdRef: ChangeDetectorRef) {
}
note: import reference ->
import { ChangeDetectorRef } from '#angular/core';
execute detect change after the value is updated
setTestDisplayMessage( message: string ) {
this.testDisplayMessage = message;
this.cdRef.detectChanges();
}
I hope this helps
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!
My app.component.ts
import { Component, Input, OnInit, OnChanges, SimpleChanges} from '#angular/core';
import {Counter } from './counter'
#Component({
selector: 'my-app',
template: `
<custom-counter [(counter)]="counterArray" (counterChange)="myValueChange($event);"></custom-counter>
<p><code>counterValue = {{counterValue}}</code></p>
<hr>
`
})
export class AppComponent implements OnChanges{
counterArray:Counter[]
counterValue = 5;
constructor(){
this.counterArray=[{id:0,value:0},{id:1,value:1}]
}
myValueChange(event:Counter[]) {
console.log(event);
}
}
my counter.ts
export class Counter {
id: number;
value: number;
}
and custom-counter component:
import { Component, Input, Output, EventEmitter } from '#angular/core';
import { Counter } from './counter';
#Component({
selector: 'custom-counter',
template: `
First counter
<button (click)="decrement()">-</button>
<span>{{this.counter[1].value}}</span>
<button (click)="increment()">+</button>
`
})
export class CustomCounterComponent {
#Input() counter : Counter[];
#Output() counterChange = new EventEmitter();
decrement() {
this.counter[1].value--;
this.counterChange.emit({
value: this.counter
})
}
increment() {
this.counter[1].value++;
this.counterChange.emit({
value: this.counter
})
}
}
My plan was that if user presses button on from the child component parent is informed about it and print something in console.
unfortunately when user press button error below is thrown:
"Error in ./CustomCounterComponent class CustomCounterComponent - inline template:3:10 caused by: Cannot read property 'value' of undefined"
I know that this exception is quite strait forward but I can not find why something is undefined while I pass everything.
If I comment out lines with emit no error occur but then I do not have any notifications for parent
Problem was in in-proper calling the emit method.
Calling like this:
this.counterChange.emit({
value: this.counter
})
was creating new object that was emited and that someway messed object binding. ( If anyone can explain this better then please do it).
after changing call to this:
this.counterChange.emit(this.counter)
when I emit strait the input object everything started to be working.
In your CustomCounterComponent class the template has an issue.
Try the following in your template:
template: `
First counter
<button (click)="decrement()">-</button>
<span>{{counter[1]?.value}}</span>
<button (click)="increment()">+</button>
`
? is a safety operator that would not throw an exception when counter[1] is undefined
Also note this is not required with counter in the template
The following method expects an an Array whereas it receives an object.
myValueChange(event:Counter[]) {
console.log(event);
}