Cannot bind [(ngModel)] into angular html - html

I am trying to achieve edit form with angular and I am having an error from binding value with [(ngModel)]
ERROR TypeError: Cannot read property 'name' of undefined
Here is my typescript code:
import { Component, OnInit } from "#angular/core";
import { HttpService } from "./../http.service";
import { ActivatedRoute, Router } from "#angular/router";
#Component({
selector: "app-edit",
templateUrl: "./edit.component.html",
styleUrls: ["./edit.component.scss"],
})
export class EditComponent implements OnInit {
id: string;
private sub: any;
author: any;
error: any;
constructor(
private _httpService: HttpService,
private _route: ActivatedRoute,
private _router: Router
) {}
ngOnInit() {
this.sub = this._route.params.subscribe((params) => {
this.id = params["id"];
});
this.oneAuthorInfoRetrieve();
}
oneAuthorInfoRetrieve() {
let observable = this._httpService.getOneTask(this.id);
observable.subscribe((data) => {
console.log("Successfully got data from get one author", data);
this.author = data["data"];
});
}
onEdit() {
let observable = this._httpService.editTask(this.id, this.author);
observable.subscribe((data) => {
console.log("Successfully update", data);
});
}
}
And here is my HTML code:
<a [routerLink]="['/home']"> Home </a>
<b>
<p>Edit this Author:</p>
</b>
<form class="border">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" class="form-control" name="name" [(ngModel)]="author.name">
</div>
<div class="form-group">
<button class="btn btn-danger" id="cancel">Cancel</button>
<button class="btn btn-primary" id="submit" (click)="onEdit()">Submit</button>
</div>
</form>
What can I do to fix this error?

DemoCreate your author model rather than using any
export class Author{
public name:string="";
constructor(){}
}
then in component
author: Author;
in ngOnInit or constructor initialize it
this.author=new Author();

You have this error because author is undefined. You can add a value to author throw a constructor or simply at the declaration author: any = {};
Or, you can display the form only of author is defined :
<form class="border" *ngIf="!!author">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" class="form-control" name="name" [(ngModel)]="author.name">
</div>
<div class="form-group">
<button class="btn btn-danger" id="cancel">Cancel</button>
<button class="btn btn-primary" id="submit" (click)="onEdit()">Submit</button>
</div>
</form>

You have to initialize object author. Because at first author.name does not exist.
Solution:
// declare author such as
author = {};// not null

The answer as you see that the author is undefined, and this also appears whenever an instance is undefined but you try to access it's properties!
Ex:
[(ngModel)]="author.name" // But author is undefined, it'll tell you that the 'name' is undefined,
// instead of 'author' remember this.
But if [(ngModel)]="author" // This will never cause an error
One more thing, author = {}; will not solve your problem, you should define a class (Recommended) like author = new author('Fes', 'Nguyen'); because {} have no property if you don't init or set value for it pro!

Related

Why doesnt (click) sends my data inside <button>, but (change) inside <input> does in Angular, HTML

I want to send some data to my Glassfish RESTful server. When I use (change) inside the input tag it activates the method and succesfully sends the data, but when I use (click) or (change) to activate the method, it doesn't do anything. I tried to seperate the Sending data method and the router method, but to no avail.
How can I solve this?
html file:
<div class="container py-5">
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="col-md-6 mx-auto">
<div class="card rounded-0">
<div class="card-header">
<h3 class="mb-0">Organize</h3>
</div>
<div class="form-group">
<label for="codes" class="m-2">Choose a restaurant:</label>
<form #f="ngForm">
<input
type="text"
list="codes"
class="m-2"
(change)="saveCode($event)"
>
<datalist id="codes">
<option *ngFor="let c of codeList" [value]="c.name">{{c.name}}</option>
</datalist>
<button
type="button"
class="btn btn-primary btn-lg float-none m-2"
id="btnAanmaken"
(click)="routerRed()"
>Aanmaken</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
typescript file:
import {Component, OnInit, ViewChild} from '#angular/core';
import {Router} from "#angular/router";
import {NgForm} from "#angular/forms";
import {HttpClient, HttpHeaders} from "#angular/common/http";
#Component({
selector: 'app-organize',
templateUrl: './sendinvite.component.html',
styleUrls: ['./sendinvite.component.css']
})
export class SendinviteComponent implements OnInit {
// public codeValue: string;
// List of restaurants
codeList = [
{ name: 'Mcdonalds', address: 'Kalverstraat 5' },
{ name: 'Kentucky Fried Chicken', address: 'Kalverstraat 4' },
{ name: 'Burger King', address: 'Kalverstraat 3' },
{ name: 'Dominos pizza', address: 'Kalverstraat 2' },
{ name: 'New York Pizza', address: 'Kalverstraat 1' }
];
// Assign empty value to id, name and address
#ViewChild('f', { static: true }) form: NgForm;
restaurant = {
name: ' ',
address: ' '
};
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'my-auth-token'
})
};
constructor(private http: HttpClient, private router: Router) {
}
ngOnInit() {
}
// Method to post data to backend
public saveCode (e): void {
const name = e.target.value;
const list = this.codeList.filter(x => x.name === name)[0];
this.restaurant.name = list.name;
this.restaurant.address = list.address;
console.log(list.name);
console.log(list.address);
// Additional information to the server content type
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
// Making an array with the values of the restaurant
const data = {
name: list.name,
address: list.address
};
console.log(data);
// POST method
this.http.post('http://localhost:8080/aquadine-jee/resources/restaurant',
JSON.parse(JSON.stringify(data)) , httpOptions)
// wait till it gets posted to the backend
.subscribe( // subscribe to observable http.post
res => {
console.log("response" + " " + res); // log results otherwise log error
},
err => {
console.log('Error occured');
}
);
}
routerRed(){
this.router.navigateByUrl('/menu');
}
I tried to use:
<button
type="submit"
class="btn btn-primary btn-lg float-none m-2"
id="btnAanmaken"
routerLink="/menu"
(change)="saveCode($event)"
>Aanmaken</button>
and:
<button
type="submit"
class="btn btn-primary btn-lg float-none m-2"
id="btnAanmaken"
routerLink="/menu"
(click)="saveCode($event)"
>Aanmaken</button>
The (change) event inside the button is going to do nothing, the (click) is more appropiate, but the $event passed inside the click event is going to return the information about the click itself (mouse position, etc).
What you should do is, whenever click is called, get the value of the form and send that to the saveData function. (Maybe by doing something like saveData(this.f.value), but I can't tell with certainty)
I don't have experience with template driven forms, maybe it's worth for you to take a look at Reactive Forms, it is going to make your life easier IMHO
The (change) event will not be triggered on the click of a button as there is no "change" as such. You should use (click) instead. The reason why your saveCode() is not being invoked when you set both routerLink and (click) is because of the routerLink. When you click the button, Angular is navigating to /menu before it triggers your click event. You can navigate from your component in your saveCode() instead.
Since you also have an API call in your saveCode(), it would probably be better if you navigate on success as it might not make sense to re-route the user if the API call fails (especially if the new route depends on the saved data)
Try this in your code.
HTML
<button type="submit"
class="btn btn-primary btn-lg float-none m-2"
id="btnAanmaken"
(click)="saveCode($event)">
Aanmaken
</button>
component
.subscribe( // subscribe to observable http.post
res => {
console.log("response" + " " + res); // log results otherwise log error
this.router.navigateByUrl('/menu');
},
err => {
console.log('Error occured');
});
Edit: Rather than using $event to pass your values to the component, you can make use of the ngForm you have used in your code along with ngModel.
component
<input type="text"
list="codes"
class="m-2"
name="restaurantName"
ngModel
>
component
#ViewChild('f', { static: true }) form: NgForm;
...
public saveCode (): void {
const name = this.form.value.restaurantName;
...
}
Here is another example from the documentation on how to bind values to the form using ngForm and ngModel

How to send dynamically added row values from angular to java controller

I created a dynamically adding/deleting rows using Angular reactive forms. Rows are getting added and deleted. But how you can send all these row values from Angular application to java controller.I am pasting the code below.
app.component.html
<div class="container">
<h3 class="page-header">Seasons</h3>
<button type="button" class="btn btn-primary" (click)="addTeam()">Add New Row</button><br/>
<form [formGroup] = "seasonsForm">
<div formArrayName = "teamRows">
<div *ngFor = "let team of seasonsForm.controls.teamRows.controls; let i=index" [formGroupName] = "i">
<h4>Team- #{{i+1}}</h4>
<div class="form-group">
<label>Team Name</label>
<input formControlName = "teamName" class="form-control">
</div>
<div class="form-group">
<label>Stadium</label>
<input formControlName = "stadiumName" class="form-control">
</div>
<div class="form-group">
<label>Capacity</label>
<input formControlName = "capacity" class="form-control">
</div>
<div class="form-group">
<label>Founded</label>
<input formControlName = "founded" class="form-control">
</div>
<div class="form-group">
<label>Head Coach</label>
<input formControlName = "headCoach" class="form-control">
</div>
<div class="form-group">
<label>Last Season</label>
<input formControlName = "lastSeason" class="form-control">
</div>
<button *ngIf = "seasonsForm.controls.teamRows.controls.length" (click) = "deleteTeam(i)" class="btn btn-danger">Delete Button</button>
</div>
</div>
</form>
<pre>{{ seasonsForm.value |json }}</pre>
</div>
app.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup,FormArray,FormBuilder,Validators} from '#angular/forms';
import { Teams } from '../service/http-client.service';
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
public seasonsForm: FormGroup;
public teams:Teams[];
constructor(private _fb: FormBuilder) { }
ngOnInit() {
this.seasonsForm = this._fb.group({
teamRows: this._fb.array([this.initTeamRows()])
});
}
get formArr() {
return this.seasonsForm.get('teamRows') as FormArray;
}
initTeamRows() {
return this._fb.group({
teamName: [''],
stadiumName: [''],
capacity: [''],
founded: [''],
headCoach: [''],
lastSeason: ['']
});
}
addTeam() {
this.formArr.push(this.initTeamRows());
}
deleteTeam(index: number) {
this.formArr.removeAt(index);
}
}
createTeam(): void {
this.httpClient.post<Teams>("http://localhost:8080/addTeam", seasonsForm);
.subscribe( res => {
alert("Successful");
})
};
export class Teams {
constructor(
public teamName:string,
public stadiumName:string,
public capacity:string,
public founded:string,
public headCoach:string,
public lastSeason:string,
) {}
}
I tried to send entire formGroup(seasonsForm) but it is getting failed.I am relatively new to Angular and i searched in Google but i didn't find much help. So any help in this would be appreciated.
you need to send form value on your createTeam function. If you console.log(seasonsForm), you can see there are some other attributes which is only about your form.
Also if you want you can check is form valid.
createTeam(): void {
if(seasonsForm.valid){
this.httpClient.post<Teams>("http://localhost:8080/addTeam", seasonsForm.value);
.subscribe( res => {
alert("Successful");
})
};
}
First of all if you are using NoSQL database then you can send a JSON file containing arrays. So here i decided to send a json file containing Array values to server side and then i converted the form group name to JSON.Stringify. In Server side you can retrieve that json in String format and parse it and send to DB. Here is the code below
onSubmit() {
this.httpClient.post("http://localhost:8080/addTeams",JSON.stringify(this.seasonsForm.value))
.subscribe((response: Response) => {
})
alert("Successful");
}

How to POST checkbox value instead of boolean value - Angular 5

I have this template-driven form:
<form #form="ngForm" (submit)="submitForm()" novalidate>
<div class="form-group" [class.has-error]="linkName.invalid">
<label class="control-label" for="name">Name</label>
<input #linkName="ngModel" type="text" class="form-control" name="name" id="name" [(ngModel)]="finalData.name" required />
<div *ngIf="linkName.invalid" class="alert alert-danger">Name is required</div>
</div>
<div class="form-group">
<label for="url">Url</label>
<input type="text" class="form-control" id="url" name="url" [(ngModel)]="finalData.url" readonly/>
</div>
<div class="checkbox" *ngFor="let tag of finalData.tags; index as i" [attr.data-index]="i">
<label><input type="checkbox" name="{{ 'checkbox' + tag }}" id="{{ 'checkbox' + tag }}" (ngModel)="finalData.tags[i]"/>{{ tag }}</label>
</div>
<button type="submit" [disabled]="form.invalid" class="btn btn-primary btn-success">Save</button>
</form>
I want to POST value from checkboxes based on whether they checked or not.
This way with one-way binding (ngModel) it will POST all tags whether they checked or not. If I use two-way binding [(ngModel)] it will POST boolean values and it will change name and id based on boolean value, which I don't want.
Example:
If i check this I want POST foo instead of true
<label><input type="checkbox" name="footag" id="footag" value="foo" [(ngModel)]="finalData.tags[i]"/>foo</label>
This is component used for data binding:
import { Component, OnInit, Injectable } from "#angular/core";
import { DataService } from "../shared/dataService";
import { FinalFormData } from "../shared/finalFormData";
import { ActivatedRoute, Router } from '#angular/router';
import { Observable } from "rxjs/Observable";
import { NgForm } from "#angular/forms";
#Component({
selector: "final-form",
templateUrl: "finalForm.component.html"
})
export class FinalFormComponent implements OnInit{
constructor(private data: DataService, private route: ActivatedRoute, private router: Router) {
}
public finalData: FinalFormData;
public errorMessage = "";
public isBusy = true;
submitForm() {
this.data.postData(this.finalData)
.subscribe(data => {
this.finalData = data;
this.router.navigate(["/"]);
}, err => console.log(err));
}
ngOnInit(): void {
//let url = encodeURIComponent(this.route.snapshot.queryParams['url']);
//this.data.loadData(url)
// .subscribe(finalData => {
// this.finalData = finalData;
// });
this.finalData = this.route.snapshot.data['finalData'];
}
}
FinalFormData class:
export class FinalFormData {
name: string;
url: string;
dateCreated: Date;
tags: any[];
}
you can use the ngModelChange method to pass the value and set it to any variable you want. Refer sample code snippet below:
<label><input type="checkbox" name="footag" id="footag" value="foo" [ngModel]="finalData.tags[i]" (ngModelChange)="CheckBoxValueChange('foo',finalData)"/>foo</label>
in the component.ts file, you can access the value and set it to any variable you want:
CheckBoxValueChange(checkboxValue:string,finalData:any){
finalData.checkboxText=checkboxValue;
alert(checkboxValue);
}

Angular2 Edit profile: Display retrieved data in HTML input tag

I have an edit profile page. So when the user clicks on the edit profile page the users details should be displayed in the input tag fields in the HTML. But I am unable to display the details I am retrieving.
edit-profile.component.html
<form novalidate (ngSubmit)="onFormSubmit(signupForm)" #signupForm="ngForm">
<div class="form-inputs clearfix">
<div class="row">
<div class="col-md-6">
<p>
<label class="required">First Name<span>*</span></label>
<input
type="text"
[value]="accountDetails.firstName"
name="firstName"
[(ngModel)] = "user.firstName"
#firstName = "ngModel"
required><p>{{accountDetails.firstName}} </p>
<span *ngIf="firstName.invalid && (firstName.dirty || firstName.touched)" class="input-error">
<span *ngIf = "firstName.errors?.required">
First Name field can't be blank
</span>
</span>
</p></form>
edit-profile.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiServiceProvider } from '../services/api.service';
#Component({
selector: 'app-edit-profile',
templateUrl: './edit-profile.component.html',
styleUrls: ['./edit-profile.component.css']
})
export class EditProfileComponent implements OnInit {
public user: any = {};
public accountDetails: any = {}
constructor(
private api: ApiServiceProvider
) { }
ngOnInit() {
let profile = JSON.parse(localStorage.getItem("profile"));
this.accountDetails = profile.user;
console.log(this.accountDetails);
}
public onFormSubmit({ value, valid }: { value: any, valid: boolean }) {
this.user = value;
this.api.put("/users/editprofile", value.userId, false)
.subscribe((data) => {
console.log(data)
localStorage.setItem("profile", JSON.stringify(data));
location.href = "/profile"
}, (err) => {
alert("Registartion failed " + err);
})
}
}
you only populate your accountDetails variable but you try to bind your user variable.
so you have:
this.accountDetails = profile.user;
but you bind your input to the user variable:
<input type="text" [(ngModel)]="user.firstName" #firstName="ngModel" required>
Either populate your user variable:
this.user = profile.user;
or bind to your account details variable:
<input type="text" [(ngModel)]="accountDetails.firstName" #firstName="ngModel" required>
Also you need to close your div clearfix, div row and div column correctly before closing the form tag. </div></div></div></form>

Angular 4.3 throwing TypeError of a field in form that bind with model

Above is the error message I found in the console section of Developer Tool when running the code. It is weird that the page works fine as expected but the error message still popped out. I tried many ways to identify the problems but still out of luck. Please provide some advice on solution for this problems, thank you. My HTML code which shows the errors in is as below:
<!doctype html>
<html>
<body>
<div class="container">
<h1>Movie</h1>
<form (ngSubmit)="onSubmit()" #movieForm="ngForm">
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" id="title" required [(ngModel)]="model.title" name="title">
</div>
<button type="submit" class="btn btn-success" [disabled]="!movieForm.form.valid" >Save</button>
</form>
</div>
</body>
</html>
The Angular component script is as below:
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { OnInit } from '#angular/core';
import { Movie } from './movie';
#Component({
selector: 'movie-form',
templateUrl: './movie-form.component.html'
})
export class MovieFormComponent implements OnInit{
model: Movie;
constructor(private http: HttpClient) {
}
ngOnInit(): void {
this.http.get('http://localhost:3000/api/movie').subscribe(data => {
this.model = new Movie( data['title']);
});
}
submitted = false;
onSubmit(){
this.submitted = true;
this.http.post('http://localhost:3000/api/movie', {
"title": this.model.title
}).subscribe(data => {
});
}
}
Change your declaration as
model = new Movie();