caused by: Cannot read property 'product_name' of undefined - html

When I try to load a html form component in my angular2 app, it will not read a property on one part of the form.
EXCEPTION: Uncaught (in promise): Error: Error in
http://localhost:3000/app/material/material-new.component.html:15:7
caused by: Cannot read property 'product_name' of undefined
I have another component that is identical bar the fields and does not encounter this problem when loaded. Components match and I am going mad about this.
Why does it not read that property of 'product_name' .
Heres the code.
Create Material
<div class="card container form-container">
<div class="row">
<div class="col-md-12">
<form (ngSubmit)="createMaterial(material)" #materialForm="ngForm" >
<div class="form-group">
<label class="label-text" for="product_name">Product
Name</label>
<input type="text"
class="form-control"
id="product_name"
placeholder="Product name"
required
name="product_name"
#product_name='ngModel'
[(ngModel)]="material.product_name">
<div [hidden]="product_name.valid || product_name.pristine">
Input product name
</div>
</div>
import { Component } from '#angular/core';
import { Material } from './material';
import { MaterialService } from './material.service';
import { Observable } from 'rxjs/Rx';
#Component({
moduleId: module.id,
selector: 'material-new',
templateUrl: 'material-new.component.html',
styleUrls: ['material-new.component.css'],
providers: [ MaterialService ]
})
export class MaterialNewComponent {
material : Material;
submitted : boolean = false;
constructor(
private materialService : MaterialService
) {}
createMaterial( material : Material) {
this.submitted = true;
this.materialService.createMaterial(material)
.subscribe(
data => { return true },
error => { console.log("error saving material")
return Observable.throw(error);
}
)
}
}

You error points to [(ngModel)]="material.product_name"
Your material object is undefined, because you have not initialized it. So all you need to do, is to initialize your Object.
So change:
material : Material;
to
material : Material = <Material>{};
and it will no longer be undefined.

You should use async pipe to unwrap the Observable because it does the job of subscribing and unsubscribing automatically.
What you'll need to do:
TS Code:
material: Observable<Material>;
// In createMaterial
this.material = this.materialService.createMaterial(material);
Template:
[(ngModel)]="(material | async)?.product_name"
The ? will check if material | async is undefined.

Related

error TS2322: Type 'Event' is not assignable to type 'boolean'

I‘m writing a todolist demo.
When ng serve it, it shows an error:
Error: src/app/app.component.html:17:58 - error TS2322: Type 'Event' is not assignable to type 'boolean'.
17 <input class="toggle" type="checkbox" [(ngModule)]="todo.isDone" >
~~~~~~~~~~~~~~
18
19 <label>{{ todo.title }}</label>
~~
Also all items not be checked.(even their isDone status is true)
I def an object in app.component.ts.
public todos:{
id:number,
title: string,
isDone: boolean
}[]=todos
const todos=[
{
id:1,
title:'study',
isDone:true
},{
id:2,
title:'sleep',
isDone:true
},{
id:3,
title:'drink',
isDone:true
}
]
app.component.html as below.
<li *ngFor="let todo of todos">
<div class="view">
<input class="toggle" type="checkbox" [(ngModule)]="todo.isDone" >
<label>{{ todo.title }}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
Can anyone see what I'm doing wrong?
thanks!
When I got this error, it was because I forgot to import the FormsModule in app.module.ts. So in app.module.ts:
import { FormsModule } from '#angular/forms';
...
imports: [
..,
FormsModule
],
change you code from <input class="toggle" type="checkbox" [(ngModule)]="todo.isDone" > to this...
<input class="toggle" type="checkbox" [(ngModel)]="todo.isDone" >
Here, ngModule is not recognizing..
In my case it was because I had made my own component and it didn't have the output event emitter (visibleChange) defined.
Code sample with emitter highlighted:
export class DialogComponent {
#Input() header = '';
#Input() message = '';
#Input()
get visible(): boolean { return this._visible; }
set visible(value: boolean) {
this._visible = value;
}
private _visible = false;
#Output() visibleChange = new EventEmitter<boolean>();
#Output() closeDialog = new EventEmitter<void>();
onCloseDialog(): void {
this.closeDialog.emit();
}
}
This error can also happen in the following situation:
you are using CUSTOM_ELEMENTS_SCHEMA in your "outer" module
you are binding to a component from another module
and you do not have the component to which you are binding as part of the exports from the module declaring the component.
The fix is to export the component from its declaring module.

ngFor giving error 'Identifier is not defined __type does not contain such a member'

I have implemented a sub-component in which the user can dynamically add and remove a set of controls to and from a collection. The solution was based on the answer from this SO question.
It compiles and works like a charm but there is an annoying message on the *ngFor directive that says:
Identifier 'sections' is not defined. '__type' does not contain such a
member Angular
I am using VS Code as my IDE.
I have seen similar errors on *ngIf directives and the message goes away when you add a double exclamation point (!!) at the beginning of the condition statement but in this case the directive is using a collection not a Boolean value.
How can I make this eyesore go away?
The HTML looks like this:
<div class="row" [formGroup]="saveForm">
<label for="sections" class="col-md-3 col-form-label">Sections:</label>
<div class="col-md-9">
<a class="add-link" (click)="addSection()">Add Section</a>
<div formArrayName="sections">
<!-- The "problem" seems to be "saveForm.controls.sections" -->
<div *ngFor="let section of saveForm.controls.sections.controls; let i=index" [formGroupName]="i">
<label for="from">From:</label>
<input class="form-control" type="text" formControlName="from">
<label for="to">To:</label>
<input class="form-control" type="text" formControlName="to">
</div>
</div>
</div>
</div>
And this is the component:
import { FormGroup, FormBuilder, FormArray, ControlContainer } from '#angular/forms';
import { Component, OnInit, Input } from '#angular/core';
import { ISection } from '../shared/practice.model';
#Component({
selector: '[formGroup] app-sections',
templateUrl: './sections.component.html',
styleUrls: ['./sections.component.scss']
})
export class SectionsComponent implements OnInit {
#Input() sections: ISection[];
saveForm: FormGroup;
get sectionsArr(): FormArray {
return this.saveForm.get('sections') as FormArray;
}
constructor(private formBuilder: FormBuilder, private controlContainer: ControlContainer) { }
ngOnInit() {
this.saveForm = this.controlContainer.control as FormGroup;
this.saveForm.addControl('sections', this.formBuilder.array([this.initSections()]));
this.sectionsArr.clear();
this.sections.forEach(s => {
this.sectionsArr.push(this.formBuilder.group({
from: s.from,
to: s.to
}));
});
}
initSections(): FormGroup {
return this.formBuilder.group({
from: [''],
to: ['']
});
}
addSection(): void {
this.sectionsArr.push(this.initSections());
}
}
Turns out Florian almost got it right, the correct syntax would be:
<div *ngFor="let section of saveForm.get('sections')['controls']; let i=index" [formGroupName]="i">
That way the error/warning goes away and the component still works as expected.

How to set an Angular2/4 Class Variable via html

I am trying to set an Angular2 Class Variable via html.
This is what I've Tried
The problem is that although the value gets set via
<div class="row" *ngVar="client.clientName as clientName">
It doesn't keep the value on the loop. It's only seems to have a value after the declaration.
Directive
import { Directive, Input, ViewContainerRef, TemplateRef } from '#angular/core';
#Directive({
selector: '[ngVar]',
})
export class VarDirective {
#Input()
set ngVar(context: any) {
this.context.$implicit = this.context.ngVar = context;
this.updateView();
}
context: any = {};
constructor(private vcRef: ViewContainerRef, private templateRef: TemplateRef<any>) {}
updateView() {
this.vcRef.clear();
this.vcRef.createEmbeddedView(this.templateRef, this.context);
}
}
Component
#Component({
selector: 'details',
template: `
<div class="row" *ngVar="'' as clientName">
<ng-template *ngIf="clients.length" ngFor [ngForOf]="clients" let-client>
aaaaaaaaa|{{clientName}}|aaaaaaaa
<div class="row" *ngIf="clientName != client.clientName">
<div>
<div class="row" *ngVar="client.clientName as clientName">
bbbbbbbb|{{clientName}}|bbbbbbbbb
</div>
</div>
</div>
</ng-template>
</div>
`
})
export class ComponentName {
className: string = "";
}
Ok the way I achieved this was
Removing the directive.
Creating a class function to set the variable
setVariable(clientName) {
this.clientName = clientName;
}
Then in the loop I added the function in an interpolation tag
<ng-template *ngIf="clients.length" ngFor [ngForOf]="clients" let-client>
aaaaaaaaa|{{clientName}}|aaaaaaaa
<div class="row" *ngIf="clientName != client.clientName">Hey
<div>
{{setVariable(statesWithClient.client.clientName)}}
bbbbbbbb|{{clientName}}|bbbbbbbbb
</div>
</div>
</ng-template>
The issue I am having now is getting an error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'
The Hack
Reading this github I ran across "when changing a component 'non model' value" - simple enough I'll just add the ngmodel.
<input type="hidden" ngModel #clientName />
It Works. no more error
But Now
But now error code no longer detects clientName != statesWithClient.client.clientName.

Customer filter pipe - Angular2

In the below component view,
<h2>Topic listing</h2>
<form id="filter">
<label>Filter topics by name:</label>
<input type="text" [(ngModel)]="term">
</form>
<ul id="topic-listing">
<li *ngFor="let topic of topics | filter: term">
<div class="single-topic">
<!-- Topic name -->
<span [ngStyle]="{background: 'yellow'}">name - {{topic.name}}</span>
<!-- Topic type -->
<h3>{{topic.type}}</h3>
</div>
</li>
</ul>
Custom filter syntax is: let topic of topics | filter: term
where custom filter is:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(topics: any, term: any): any {
// check if search term is undefined
if(term === undefined) return topics;
return topics.filter(function(topic){ // javascript filter(function)
// if below is false, then topic will be removed from topics array
return topic.name.toLowerCase().includes(term.toLowerCase());
})
}
}
the component class maintains data for topics:
export class DirectoryComponent implements OnInit {
topics = [
{type:"Fiction", name:"Avatar"},
{type:"NonFiction", name:"Titanic"},
{type:"Tragedy", name:"MotherIndia"},
];
constructor() { }
ngOnInit() {
}
}
Edit: Without form tag, code works fine.
<label>Filter topics by name:</label>
<input type="text" [(ngModel)]="term">
Why custom filter FilterPipe does not filter term provided in input element?
Add brackets to your filter condition
<ul id="topic-listing">
<li *ngFor="let topic of (topics | filter: term)">
<div class="single-topic">
<!-- Topic name -->
<span [ngStyle]="{background: 'yellow'}">name - {{topic.name}}</span>
<!-- Topic type -->
<h3>{{topic.type}}</h3>
</div>
</li>
</ul>
Check the TS file
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
term : any = "avatar";
topics = [
{type:"Fiction", name:"Avatar"},
{type:"NonFiction", name:"Titanic"},
{type:"Tragedy", name:"MotherIndia"},
];
}
Remove the word function and change it to below code. refer the working version here
return topics.filter((topic)=>{
return topic.name.toLowerCase().includes(term.toLowerCase());
})
update - root cause of the issue
if you want to use NgModel inside the form tags, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions

form validation in angular 2

I am writing a form validation in angular2.
my error is Cannot read property 'valid' of undefined
My HTML file contains a form i.e.
<form id="commentform" class="comment-form" novalidate
[ngFormModel] = "contact" (ngSubmit)="submit(contact.value)">
<div class="form-input">
<input type="text" id="author" name="author" placeholder="Name *" value=""
[ngFormControl] = "contact.controls['author']" pattern="[a-zA-Z ]*"/>
<div [hidden]="author.valid">Name is required
</div>
</div>
I am getting error at div [hidden]="author.valid".
The error is Cannot read property 'valid' of undefined"
My component file contains
import {FormBuilder ,ControlGroup,Validators } from '#angular/common';
#Component({
selector: 'my-contact',
templateUrl : 'app/contact.html'
}
export class ContactComponent {
contact : ControlGroup;
constructor(private _OnChange:OnChange,private _formBuilder : FormBuilder){
this.ngAfterViewInit();
}
ngOnInit() : any{
this.contact = this._formBuilder.group({
'author' : ['',Validators.required] }); }
submit(){
console.log(JSON.stringify(this.contact.value));
}
Use
[hidden]="contact.controls['author'].valid"
Because author is not a variable. Use
<div [hidden]="contact?.controls?.author?.valid">Name is required</div>
Actually, even better. you should use the new forms module #angular/forms
https://scotch.io/tutorials/using-angular-2s-model-driven-forms-with-formgroup-and-formcontrol