I have a basic angular component that allows some one to edit the details of a user once they go to their profile and click on "edit".
Component:
export class EditUserComponent implements OnInit {
// Define our vars
user: Users[];
editUserForm: FormGroup;
message: {};
displayMessage = false;
userID: number;
errorMessage: any = '';
constructor(
private fb: FormBuilder,
private _userService: UserService,
private activatedRoute: ActivatedRoute
) {
}
ngOnInit(): void {
// Get the userID from the activated route
this.activatedRoute.params.subscribe((params: Params) => {
this.userID = params['id'];
});
// Call our service and pass the UserID
this._userService.getUser(this.userID)
.then(res => {
this.user = res;
this.createForm();
});
}
// Generate the form
createForm() {
this.editUserForm = this.fb.group({
QID: ['', Validators.required],
favoriteColor: [''],
favoriteNumber: [''],
favoriteActor: ['']
});
}
}
Service:
// Fetch a single user
getUser(userID: number) {
return this._http.post(this.baseUrl + '/fetchUser', { "userID": userID }, { "headers": this.headers })
.toPromise()
.then(res => res.json())
.catch(err => { this.handleError(err); });
}
Interface:
export interface Users {
RecordID?: number;
QID: string;
favoriteColor?: string;
favoriteNumber?: number;
favoriteActor?: string;
}
I am trying to pass the values to my formGroup but I am having trouble figuring out how to access the values.
I assumed I could do something like this where I could access the user model and select a property from it but that is throwing an undefined error.
Would I pass the values here in the form group or bind them to the elements directly somehow? I am receiving the data back from the service just fine, just not sure how to get each of the values back to their respective fields.
createForm() {
this.editUserForm = this.fb.group({
QID: [this.user.QID, Validators.required],
favoriteColor: [''],
favoriteNumber: [''],
favoriteActor: ['']
});
}
If I understand correctly ... this is what my code looks like:
onProductRetrieved(product: IProduct): void {
if (this.productForm) {
this.productForm.reset();
}
this.product = product;
// Update the data on the form
this.productForm.patchValue({
productName: this.product.productName,
productCode: this.product.productCode,
starRating: this.product.starRating,
description: this.product.description
});
this.productForm.setControl('tags', this.fb.array(this.product.tags || []));
}
I'm using patchValue for the values and setControl for the array.
OR
Since you are creating the form after retrieving the data, you could do something like this:
createForm() {
this.editUserForm = this.fb.group({
QID: [this.user.QID, Validators.required],
favoriteColor: [this.user.favoriteColor],
favoriteNumber: [this.user.favoriteNumber],
favoriteActor: [this.user.favoriteActor]
});
}
AND just to be complete ... each input element needs a formControlName property like this:
<input class="form-control"
id="productNameId"
type="text"
placeholder="Name (required)"
formControlName="productName" />
<span class="help-block" *ngIf="displayMessage.productName">
{{displayMessage.productName}}
</span>
</div>
You can find a complete working example here: https://github.com/DeborahK/Angular2-ReactiveForms
Bind a submit event to your form, then use this.editUserForm.value to access the data from the form.
In the component template:
<form [formGroup]="editUserForm" (submit)="saveIt()">
In the Component:
saveIt() {
if (this.editUserForm.dirty && this.editUserForm.valid) {
alert(`Number: ${this.editUserForm.value.favoriteNumber} Actor: ${this.editUserForm.value.favoriteActor}`);
}
}
Related
I've got a reactive form that I'm initializing oninit() like this, along with a couple other properties I'm using to grab the passed id out of the URL and tell whether or not the form is being used to update, or create a new entry in a mysql table. The issue I'm having is in using patchValue to pass the data returned from my service into my form:
component.ts
export class formComponent implements OnInit, AfterViewInit {
constructor(
private dataService: dataService,
private route: ActivatedRoute,
private router: Router,
private formBuilder: FormBuilder,
private ticketModel: ticketModel,
) {}
Form!: FormGroup;
isNewMode!: boolean;
id!: string;
ticket!: ticketModel[];
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
this.Form = this.formBuilder.group({
field1: ['', Validators.required],
field2: ['', Validators.required],
field3: ['', Validators.required],
field4: ['', Validators.required]
});
}
ngAfterViewInit(){
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData;
},
});
this.Form.patchValue({
field1: this.ticket.field1, //error, "Property 'field1' does not exist on type 'ticketModel[]'"
field2: this.ticket.field2, //error, "Property 'field2' does not exist on type 'ticketModel[]'"
field3: this.ticket.field3, //error, "Property 'field3' does not exist on type 'ticketModel[]'"
field4: this.ticket.field4, //error, "Property 'field4' does not exist on type 'ticketModel[]'"
});
}
}
}
ticketModel.ts
export class ticketModel {
id: string = '';
field1: string = '';
field2: string = '';
field3: string = '';
field4: string = '';
}
service.ts
export class dataService {
constructor(private errorHandlerService: errorHandlerService, private http: HttpClient) {}
private url = "/api/tickets";
httpOptions:{ headers: HttpHeaders } = {
headers: new HttpHeaders({ "Content-Type": "application/json" }),
};
getById(id: string): Observable<ticketModel[]> {
return this.http
.get<ticketModel[]>(`${this.url}/${id}`, {responseType: "json"})
.pipe(tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel[]>("fetchAll", [])
)
);
}
and just in case it's helpful, this is an example of the response json I'm getting when this method is run
[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
if there isn't an id that gets passed in, isNewMode is true and the form is initialized with blank values, works fine from there. When an id is passed in, I'm passing that to a method in the data service to query the database and return just that row. This also seems to work fine as I'm able to log that data in json format to the console. I just can't figure out how to get the data to patch into the form after trying this out a few different ways.
Currently, the way that I think this should work which is what this code is an example of, in patchValue() the compiler throws an error that "property field1 does not exist on type ticketModel[]" when it absolutely does exist as a property on that model.
I feel like I'm probably missing something pretty small and any help in figuring out what would be wildly appreciated, thank you!
You have declared ticket!: ticketModel[] as an Array type.
Your response is also an array -
[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
then why are you not treating this.ticket as an array here ?
field1: this.ticket.field1,
Either use it this way - field1: this.ticket[0].field1 or use for loop on it to get the field1 and other values from this.
And you need to patch the form inside the subscribe block, because it's an async operation.
really your service getById should return one 'TicketModel' nor an array of TicketModel. better than mannage in the component, mannage in the service using map
//see that is an observable of "ticketModel"
getById(id: string): Observable<ticketModel> {
//you needn't use {responseType:'json'}, by defect Angular expect a Json
return this.http
.get<ticketModel[]>(`${this.url}/${id}`)
.pipe(
//just return the uniq element of the array
map(res=>res.length?res[0]:null),
tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel>("fetchAll", [])
)
);
}
Futhermore, you need use the "patchValue" inside subcription function, and you can use the patchValue wihout create a new object because has the same properties
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData; //<--really you needn't use ticketData
//unless you want compare old and new Values
//here you make the patchValue
this.form.patchValue(ticketData);
})
}
(you can also put in ngOnInit instead of ngAfterViewInit)
Update another aproach to the "clasic" problem to create a component to edit/create an element.
If you has a function like
getForm(data:TicketModel=null){
data=data || {} as TicketModel
return new FormGroup({
id: new FormControl(data.id,Validators.required),
field1: new FormControl(data.field1,Validators.required),
field2: new FormControl(data.field2,Validators.required),
field3: new FormControl(data.field3,Validators.required),
field4: new FormControl(data.field4,Validators.required)
})
}
You can in ngOnInit make some like
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
if (this.isNewMode)
this.Form=this.getForm()
else
{
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe(res=>{
this.Form=this.getForm(res)
})
}
}
I have problem with sending data to server from my form in angular, i can get user current data from database and show them but i am not able to send data back, instead it sends empty values.
Screenshots
playload of sending data to server
profile edit page with current data
Code
HTML
<form [formGroup]="userUpdate" (ngSubmit)="update()" *ngIf="user">
<ion-item class="ion-margin-top">
<ion-label position="floating">Name</ion-label>
<ion-input type="text" [value]="user.name" formControlName="name"></ion-input>
</ion-item>
//rest of the fields
<ion-button class="ion-margin-top" type="submit" expand="full" color="success" >Update</ion-button>
</form>
profile.page.ts
export class ProfilePage implements OnInit {
public userUpdate: FormGroup;
imageURI: any;
user = null;
constructor(
private authService: AuthService,
private navCtrl: NavController,
private menu: MenuController,
private modalController: ModalController,
private alertService: AlertService,
private alertCtrl: AlertController,
public formBuilder: FormBuilder,
private camera: Camera,
) {
this.userUpdate = this.formBuilder.group({
name: ['', [Validators.required]],
username: ['', [Validators.required]],
email: ['', [Validators.email, Validators.required]],
password: ['', Validators.required],
phone: ['', [Validators.required]],
avatar: ['', [Validators.required]],
});
}
ngOnInit() {
this.menu.enable(true);
}
ionViewWillEnter() {
this.authService.getToken().then(() => {
if (this.authService.isLoggedIn) {
this.navCtrl.navigateRoot('/profile');
}
});
this.authService.user().subscribe((user: any) => {
this.user = user.success;
console.log('user success', user.success);
});
}
update() {
const userUpdate = this.userUpdate.value;
this.authService.update(
userUpdate.name,
userUpdate.username,
userUpdate.email,
userUpdate.password,
userUpdate.phone,
userUpdate.avatar = this.imageURI
).subscribe(
data => {
this.alertService.presentToast(data);
},
error => {
this.alertService.presentToast(error);
}
);
}
}
profile.service.ts
update(
name: String,
username: String,
email: String,
password: String,
phone: String,
avatar: String
) {
const headers = new HttpHeaders({
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
'Authorization': 'Bearer' + " " + this.token.success.token
});
return this.http.post(this.env.BASE_URL + '/updateUser',
{
name: name,
username: username,
email: email,
password: password,
phone: phone,
avatar: avatar
}, { headers: headers }
);
}
Any idea why it sends empty data and how to fix it?
Don't set user value using HTML value attribute, Angular reactive form source of control is class so setting value in html does not add the value to formControl instance. Instead you can use setValue or patchValue method if you want to set Value dynamically to input field.
this.authService.user().subscribe((user: any) => {
this.user = user.success;
this.userUpdate.patchValue({
name:user.name,
username: user.username ,//Assuming user object has username property
email: user.email,
password: user.password ,
phone: user.phone
});
console.log('user success', user.success);
});
For More Info
Example
I am having trouble creating an update component that reads the passed in id from the url, makes a get request, and populates a reactive form. I can confirm that the get request is returning what it should in the network tab of the browser.
In my service:
productUrl= 'http://localhost:8080/api/products';
getProduct(id: number): Observable<Product> {
const url = `${this.productUrl}/${id}`;
return this.http.get<Product>(url);
}
In my component:
product: Product;
productForm= this.fb.group({
name: ['', Validators.required],
price: ['', Validators.required]
});
ngOnInit() {
this.getProduct();
console.log(this.product);
this.productForm.controls['name'].setValue(this.product.name);
this.productForm.controls['price'].setValue(this.product.price);
}
getProduct(): void {
const id = +this.route.snapshot.paramMap.get('id');
this.productService.getProduct(id)
.subscribe(product=> this.product = product);
}
The problem is you are setting the data to form before it comes from the backend, (subscribe is asynchronous, which means the setvalue functions will execute while the subscribe function is in the process )the best way to do is to trigger the setValue/patch function when the data has arrived from the backend like this
getProduct(): void {
const id = +this.route.snapshot.paramMap.get('id');
this.productService.getProduct(id)
.subscribe(product=> {
this.product = product;
console.log(this.product);
this.productForm.patchValue({
price: this.product.price
name: this.product.name
});
}
);
}
I think you are setting the from before the data is come form server so you should set the form after data come form server as follows:
product: Product;
productForm= this.fb.group({
name: ['', Validators.required],
price: ['', Validators.required]
});
ngOnInit() {
this.getProduct();
}
getProduct(): void {
const id = +this.route.snapshot.paramMap.get('id');
this.productService.getProduct(id)
.subscribe(product=> {
this.product = product;
console.log(this.product);
this.productForm.controls['name'].setValue(this.product.name);
this.productForm.controls['price'].setValue(this.product.price);
}
);
}
you can pathValue wth the value
this.productService.getProduct(id)
.subscribe(product=> {
this.product = product;
if (this.productForm) {
this.productForm.patchValue(this.product);
// this.productForm.patchValue(this.product, {emitEvent: false}); if you don't want valueChange on form be fired
}
});
Hello I am new to angular I am facing a problem while displaying country name in a drop downlist ,the drop down is not displaying any option while data is retrieved in the type script I think I am runing into a problem of mapping the ts with the html correctly
here is my ts code
export class SignupComponent{
registrationForm: FormGroup;
username: AbstractControl;
useremail: AbstractControl;
countryCode: String = "JOR" ;
viewCountryCode: String = "JOR";
languageCode: String;
CountryId:number; countries = CountryMapping.countryCode;
submitAttempt: boolean = false;
userResponse: UserResponse;
apiResponseData: any;
newCountries = [];
urlCopied = true;
countryList: any =[];
constructor(public builder: FormBuilder, public homeService: HomeService,
private router: Router,private _http: Http,
private loaderService: LoaderService, private helperService:
HelperService, private translate: TranslateService) {
this.languageCode = "EN"
this.CountryId=96;
this.homeService.getCountryByIp().subscribe(UserResponse => {
this.userResponse = UserResponse;
if (UserResponse.success) {
var cc = UserResponse.data.countryCode;
cc=cc.toLowerCase();
this.viewCountryCode = cc;
}
});
ngOnInit() {
this.signUpDone = false;
this.createSignupForm();
this.getCountryList();
this.getMerchantType();
}
createSignupForm() {
this.registrationForm = this.builder.group({
username: ['', Validators.compose([
Validators.required,
UserNameValidator.userNameRange,
UserNameValidator.OnlyAlphabets
])],
useremail: ['', Validators.compose([
Validators.required,
EmailValidator.mailFormat,
EmailValidator.domainCheckFormat,
EmailValidator.mailLength,
EmailValidator.specialCharacters
])],
countryCode:[this.viewCountryCode],
commercialWebSiteLink: ['', Validators.compose([
Validators.required,
UrlValidator.urlFormat,
])],
corporateWebSiteLink: ['', Validators.compose([
Validators.required,
UrlValidator.urlFormat,
])],
merchantType: ['PSP']
});
this.username = this.registrationForm.controls['username'];
this.useremail = this.registrationForm.controls['useremail'];
this.commercialWebSiteLink = this.registrationForm.controls['commercialWebSiteLink'];
this.corporateWebSiteLink = this.registrationForm.controls['corporateWebSiteLink'];
this.regestrationErrorMessage = " ";
}
getCountryList() {
this.homeService.getCountryList().subscribe(response => {
if (response.success) {
this.countryList = response.data;
console.log("retrieved the country list");
}
});
}
<div class="form-group ">
<label class="control-label" for="countryCode">Country</label>
<select class="form-control" id="countryCode" formControlName="countryCode">
<option *ngFor="let country of countryList" [ngValue]="country" [attr.value]="country.countryCode" [selected]="country.countryName == viewCountryCode">
{{country.countryName}}</option>
</select>
</div>
I would really appreciate any help i really need to get this to work by tomorrow,so please help me
THAT IS funny it seems in the back end (Spring)no country list a query that is wrong was retrieving no data thus no country was generated in the drop down list ,was retrieved ,no problem in the code above it work like charm.
I am trying to get data on the basis of id which is set in url
working on add mode but getting error for edit
export class GradeComponent implements OnInit {
public isNew:boolean=true;
public frmGrade: FormGroup;
public subscription:any;
public oldGrade:Grade;
constructor(
private formBuilder:FormBuilder ,
private gradeService:GradeService,
private router:Router,
private activatedRoute:ActivatedRoute
) { }
ngOnInit() {
if(typeof this.activatedRoute.snapshot.params['id'] ==='undefined'){
this.frmGrade = this.formBuilder.group({
grade: ['', Validators.required],
description: ''
});
}else{
this.setForUpdate();
}
}
private setForUpdate(){
this.isNew=false;
this.gradeService
.getOneGrade(this.activatedRoute.snapshot.params['id'])
.subscribe(
data => {
this.oldGrade = data,
this.frmGrade = this.formBuilder.group({
grade: [this.oldGrade.grade, Validators.required],
description: this.oldGrade.description
});
},
err => console.error(err),
() => console.log('done')
);
}
but i am getting error
this.formBuilder is undefined how to handle this .
FormBuilder is a service Injectable, just get an instance with Dependancy Injection.
Add this in your class:
constructor(private formBuilder: FormBuilder) {}
And you are ready to go.
export class GradeComponent implements OnInit {
public isNew:boolean=true;
public frmGrade: FormGroup;
public subscription:any;
public oldGrade:Grade;
constructor(
private formBuilder:FormBuilder ,
private gradeService:GradeService,
private router:Router,
private activatedRoute:ActivatedRoute
) { }
ngOnInit() {
this.frmGrade = this.formBuilder.group({
grade: ['', Validators.required],
description: ''
});
if(typeof this.activatedRoute.snapshot.params['id'] !=='undefined') {
this.setForUpdate();
}
}
private setForUpdate(){
this.isNew=false;
this.gradeService
.getOneGrade(this.activatedRoute.snapshot.params['id'])
.subscribe(
data => {
this.oldGrade = data,
this.frmGrade = this.formBuilder.group({
grade: [this.oldGrade.grade, Validators.required],
description: this.oldGrade.description
});
},
err => console.error(err),
() => console.log('done')
);
}