#Input and #Output are always undefined in Angular-Cli - javascript

Whatever values are inside the individuals are printed without issues but whatever is obtained using #Input or #Output is not displayed.
child.component.ts
#Component({
selector: 'app-form-input',
templateUrl: './form-input.component.html',
styleUrls: ['./form-input.component.scss']
})
export class FormInputComponent implements OnInit {
#Input() fieldType: string;
//with another Input and 1 Output DOM
constructor(
) {
}
ngOnInit() {
console.log(this.fieldType);
}
}
parent.component.html
<app-form-input (value)="action($event)"
[fieldType]="date"
[maxAllowItem]="1">
</app-form-input>
Is there anything goes wrong in syntax?
The Log always show 'undefined' in all cases.
Thanks

I think this is trying to pull in a variable defined within your component.
Try the following syntax, wrap the string again, this should ensure you are passing a string and not a variable from the component, the input will then know to expect a string.
[fieldType]="'date'"
This is wrapping the string in " and '.

You may need to initialize the initial values of your #Input and #Output variables inside your component because #Input properties will be undefined unless they are provided from outside and #Output properties need to be initialized with EventEmitter
Also you need to check the values inside ngOnChanges which will be executed during Change Detection
Your code will be like this:
#Component({
selector: 'app-form-input',
templateUrl: './form-input.component.html',
styleUrls: ['./form-input.component.scss']
})
export class FormInputComponent implements OnInit {
#Input() fieldType: string;
#Output() event: EventEmitter<any>
//with another Input and 1 Output DOM
constructor() {
this.fieldType = ''
this.event = new EventEmitter()
}
ngOnInit() {
}
ngOnChanges() { // <- it will run every time and give you the latest value of fieldType
console.log(this.fieldType);
}
}

Related

How to pass a class component value to custom decorator in Angular?

When I tired to access an angular component value inside a custom decorator, I am getting an error stating this is not defined. From online documentation, I have found out that decorator function will get called first even before the class initialization. Because of this decorators will not have access to instance variables.
Note: I have tried creating a static variable and tried to access the static variable inside the custom decorator. Only problem with this approach is customDecorator runs first before assigning the value to static variable. Hence the AccountId is undefined when i tried to access inside customDecorator.
How I can access this AccountId inside the custom decorator? Any help would be greatly appreciated.
import { Component,Input, OnInit } from '#angular/core';
import {CustomDecorator } from '#sharedDecorators';
#Component({
selector: 'app-account-display',
templateUrl: './accounts.component.html',
styleUrls: ['./account s.component.scss']
})
export class AccountsComponent implements OnInit {
#Input() AccountId: string
constructor() {
}
#CustomDecorator(this.AccountId)
ngOnInit() {
}
...........................
}
hu Ane!
if AccountId is passed statically from parent you can simply:
ngOnInit() {
console.log(this.AccountId)
}
if AccountId is asynchronous you can do something like this
#Input() get AccountId(value: string) {
if(value) {
this.accountID$.next(value)
}
}
accountId$ = new ReplaySubject<any>()
ngOnInit() {
this.accountId$.subscribe((x) => console.log(x) ) <-- remember to unsubscribe
}
here you can find more info

Rendering angular component conditionally based on its input variable doesn't infer the existence of the input variable

I have an angular component with an array of Input variables that is asynchronously initialized in the parent.
#Component({ ... })
export class ChildComponent {
#Input inputVariable: string;
}
#Component({ ... })
export class ParentComponent implements OnInit {
inputVariables: string[] = [];
constructor( private http: HttpClient ) { }
ngOnInit(): void {
this.http.get<string[]>('someUrl')
.subscribe(res => this.inputVariables = res)
}
}
Now I want to render in parent.component.html a ChildComponent for each inputVariable like this:
<div *ngFor="let inputVariable of inputVariables">
<child-component [inputVariable]="inputVariable" />
</div>
In ChildComponent I can be sure that inputVariable is defined. However, typescript complains, that the type of inputVariable must be string | undefined. However, then I need to check in every usage of inputVariable in ChildComponent whether inputVariable is defined or not, which is not, what I want to do.
Is there a solution for ts to infer, that inputVariable always is defined for any rendered ChildComponent?
Value for #Input is set later than on initialisation (on ngOnInit hook to be precise).
To stop typescript from complaining, just set the default value for inputVariable like that:
#Component({ ... })
export class ChildComponent {
#Input inputVariable: string = "";
}
It tells you to set type to string | undefined, because the default value of every variable in javascript is undefined. Giving it the default value of empty string, you don't have to worry about different types.

How to set a value of a property of a nested item in order to make it visible via *ngIf directive

I have created a component to reuse the mat-progress-spinner from angular material. I need this in order to avoid putting for every single page the same code. Here is the code that is working:
<div id="overlayProgressSpinner">
<div class="center">
<mat-progress-spinner
style="margin:0 auto;"
mode="indeterminate"
diameter="100"
*ngIf="loading">
</mat-progress-spinner>
</div>
</div>
It is simple. Only to set "loading" as true or false.
What did I do?
I put above code inside a custom component. Now it is like so:
<app-progress-spinner></app-progress-spinner>
its HTML code is the same and its TS code is as a follows:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-progress-spinner',
templateUrl: './progress-spinner.component.html',
styleUrls: ['./progress-spinner.component.scss']
})
export class ProgressSpinnerComponent implements OnInit {
loading = false;
constructor() { }
ngOnInit() {
}
public isLoading(value: boolean) {
this.loading = value;
}
public changeSpinnerCSSClass() {
const htmlDivElement = (window.document.getElementById('overlayProgressSpinner') as HTMLDivElement);
if (this.loading) {
htmlDivElement.className = 'overlay';
} else {
htmlDivElement.className = '';
}
}
}
when the property "loading" belongs to the current component, I can show and hide the "mat-progress-spinner" component. Otherwise, when it belongs to "app-progress-spinner" it is set but it is not being displayed. The code that I am trying to make it visible is as follows:
this.progressSpinner.isLoading(false); // it is set, but it does not work.
this.progressSpinner.changeSpinnerCSSClass(); // it works
it appears that *ngIf="loading" cannot be set by using the approach the works if the logic behind belongs to the current component.
How to achieve this?
You need to create an input in your ProgressSpinnerComponent. To do that, add the #Input() decorator before the property loading:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-progress-spinner',
templateUrl: './progress-spinner.component.html',
styleUrls: ['./progress-spinner.component.scss']
})
export class ProgressSpinnerComponent implements OnInit {
#Input() loading = false;
So anywhere you need to use the app-progress-spinner you do:
<app-progress-spinner [loading]="loading"></app-progress-spinner>
Note: The loading variable assigned to the input loading belongs to the component that contains theapp-progress-spinner.
This happens because every component have it own scope, meaning that it have no access to external world unless you create an input or output in order to receive or send data. There's also the ngModel that can be used for bi-diretional data, but not recommend in most cases.

changing value of a component property via template

I have a component like this:
import { Component, OnInit,Input } from '#angular/core';
#Component({
selector: 'topic-details',
templateUrl: './detailpane.component.html',
styleUrls: ['./detailpane.component.scss']
})
export class DetailpaneComponent implements OnInit {
#Input() topics;
myprop;
constructor() { }
assign(property,value){
property = value;
}
ngOnInit() {
}
}
And in my template:
<button (click)='assign(myprop,"some value")'>testing</button>
{{myprop}}
Take a look at the myprop property. In essence assign method has no effect! I was expecting myprop in the template to refer exactly same myprop in the component. I am beginnning to think that as soon as myprop goes out of the component inside the template, it becomes local to the template and doesn't reference the component (original) property.
Any explanation would be great!
You are assigning the value to wrong property, your property below, only exists in that function:
assign(property,value){
property = value;
}
but you want to assign it to myProp instead, which is also the variable you display in your view:
assign(property,value){
this.myProp = value;
}
Make notice of the keyword this
And if you actually have just one myProp (and not part e.g of iteration), you don't need to pass it to the function, just the new value:
<button (click)='assign("some value")'>testing</button>
assign(value){
this.myProp = value;
}
showPane is a primitive, thus is immutable.
This is more a javascript thing than an angular 2 issue.
you can either set it the template (my favorite)
(click)='showPane = !showPane'
or use an object
//in class
flagsCollection = {showPane:false}
toggle(flag){
this.flagsCollection[flag] = !this.flagsCollection[flag];
}
// in template
(click)='toggle("showPane")'
Your toggle method change locally the flag parameter. If you want to update showPane you could try this :
#Component({
selector: 'topic-details',
templateUrl: './detailpane.component.html',
styleUrls: ['./detailpane.component.scss']
})
export class DetailpaneComponent implements OnInit {
#Input() topics;
showPane = false;
constructor() { }
toggle(){
this.showPane = !this.showPane;
}
ngOnInit() {
}
}
<div class="col-md-12 list-group topics" [ngClass]="{'topics-small':showPane}" >
<a class="list-group-item cursor-pointer"
*ngFor='let topic of topics'
(click)='toggle()'
>{{topic.name}}</a>>
</div>

Angular 2 Component #Input not working

I am stuck on trying to pass a property value into my component. From what I've read everything looks correct. But it is still not working. My test value gets output to the screen and the console as null. :(
This is my test component:
import {Component, Input} from 'angular2/angular2';
#Component({
selector: 'TestCmp',
template: `Test Value : {{test}}`
})
export class TestCmp {
#Input() test: string;
constructor()
{
console.log('This if the value for user-id: ' + this.test);
}
}
This is how I am calling the component from the parent page.
<TestCmp [test]='Blue32'></TestCmp>
When the page render's the test value is empty. I only see 'Test Value :'.
Instead of 'Test Value : Blue32'.
You have four things that I can note :
You are passing an input in the root component, which will not work.
As #alexpods mentioned, you are using CamelCase. You should not.
You are passing an expression instead of an string through [test]. That means that angular2 is looking for a variable named Blue32 instead of passing a raw string.
You are using the constructor. That will not work, it must be after the view has been initialized data-bound properties have been initialized (see docs for OnInit).
So with a few fixes it should work
Example updated to beta 1
import {Component, Input} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
#Component({
selector : 'childcmp',
template: `Test Value : {{test}}`
})
class ChildCmp {
#Input() test: string;
ngOnInit() {
console.log('This if the value for user-id: ' + this.test);
}
}
#Component({
selector: 'testcmp',
template : `<childcmp [test]="'Blue32'"></childcmp>`
directives : [ChildCmp]
})
export class TestCmp {}
bootstrap(TestCmp);
See this plnkr as an example.
Update
I see that people still reach this answer, so I've updated the plnkr to beta 1 and I corrected one point on the explanation : You can access inputs in ngAfterViewInit, but you can access them earlier in the lifecycle within ngOnInit.
It's that easy as surrounding the string with double quotes, like this:
<TestCmp [test]="'Blue32'"></TestCmp>
If you use brackets [] the Angular uses property binding and expects to receive an expression inside the quotes and it looks for a property called 'Blue32' from your component class or a variable inside the template.
If you want to pass string as a value to the child component you can pass it like so:
<child-component childProperty='passing string'></child-component>
or if you for some reason want to have the brackets:
<child-component [childProperty]="'note double quotes'"></child-component>
And then take it in to child.component.ts like this:
import { Component, Input } from "#angular/core";
#Component({})
export class ChildComponent {
#Input()
childProperty: string;
}
This angular class could make the trick for static attributes:
ElementRef
https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html
import {ElementRef} from 'angular2/core'
constructor(elementRef: ElementRef) {
elementRef.nativeElement.getAttribute('api')
}
Sharing what worked for me:
Adding an input to the Angular 4 app
Assuming that we have 2 components:
parent-component
child-component
We wanted to pass some value from parent-component to child-component i.e. an #Input from parent-component.html to child-component.ts. Below is an example which explains the implementation:
parent-component.html looks like this:
<child-component [someInputValue]="someInputValue"></child-component>
parent-component.ts looks like this:
class ParentComponent {
someInputValue = 'Some Input Value';
}
child-component.html looks like this:
<p>Some Input Value {{someInputValue}}</p>
child-component.ts looks like this:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'child-component',
templateUrl: './child-component.html'
})
export class ChildComponent implements OnInit {
#Input() someInputValue: String = "Some default value";
#Input()
set setSomeInputValue(val) {
this.someInputValue += " modified";
}
constructor() {
console.log('someInputValue in constructor ************** ', this.someInputValue); //someInputValue in constructor ************** undefined
}
ngOnInit() {
console.log('someInputValue in ngOnInit ************** ', this.someInputValue); //someInputValue in ngOnInit ************** Some Input Value
}
}
Notice that the value of the #Input value is available inside ngOnInit() and not inside constructor().
Objects reference behaviour in Angular 2 / 4
In Javascript, objects are stored as references.
This exact behaviour can be re-produced with the help of Angular 2 / 4. Below is an example which explains the implementation:
parent-component.ts looks like this:
class ParentComponent {
someInputValue = {input: 'Some Input Value'};
}
parent-component.html looks like this:
{{someInputValue.input}}
child-component.html looks like this:
Some Input Value {{someInputValue}}
change input
child-component.ts looks like this:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'child-component',
templateUrl: './child-component.html'
})
export class ChildComponent implements OnInit {
#Input() someInputValue = {input:"Some default value"};
#Input()
set setSomeInputValue(val) {
this.someInputValue.input += " set from setter";
}
constructor() {
console.log('someInputValue in constructor ************** ', this.someInputValue); //someInputValue in constructor ************** undefined
}
ngOnInit() {
console.log('someInputValue in ngOnInit ************** ', this.someInputValue); //someInputValue in ngOnInit ************** Some Input Value
}
changeInput(){
this.someInputValue.input += " changed";
}
}
The function changeInput() will change the value of someInputValue inside both ChildComponent & ParentComponent because of their reference. Since, someInputValue is referenced from ParentComponent's someInputValue object - the change in ChildComponent's someInputValue object changes the value of ParentComponent's someInputValue object. THIS IS NOT CORRECT. The references shall never be changed.
I believe that the problem here might have to do with the page's life cycle. Because inside the constructor the value of this.test is null. But if I add a button to the template linked to a function that pushes the value to the console (same as I am doing in the constructor) this.test will actually have a value.
Maybe look like a hammer, but you can put the input wrapped on an object like this:
<TestCmp [test]='{color: 'Blue32'}'></TestCmp>
and change your class
class ChildCmp {
#Input() test: any;
ngOnInit() {
console.log('This if the value for user-id: ' + this.test);
}
}
you have to import input like this at top of child component
import { Directive, Component, OnInit, Input } from '#angular/core';
When you are making use of #Input for the angular interaction.It is always preferred approach to pass the data from parent to child in JSON object apparently it doesn't not restrict by #Angular Team to use local variable or static variable.
In context to access the value on child component make use ngOnInit(){} angular life hook cycle regardless of constructor.
That will help you out. Cheers.
When I had this issue there was actually just a compile error that I had to fix first (circular dependency needed resolved).

Categories

Resources