In this post, we are going to update the owner entity by creating a form for the update action and by sending the PUT request towards our server.
Without further ado, let’s dive right into it.
If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction of the .NET Core series.
For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series.
For the previous part check out: Creating Angular client side – Form Validation and Create Actions
The source code is available at GitHub .NET Core, Angular and MySQL. Part 14 – Source Code
This post is divided into several sections:
- Folder Structure and Routing
- Handling Angular PUT Actions in the HTML File
- Handling Angular PUT Actions in the Component
- Conclusion
Folder Structure and Routing
Prior to any update action, we need to create our component files.
So, let’s create them by using the Angular CLI
command which is going to create all the files and import the created OwnerUpdate
component in the owner.module.ts file:
1 |
ng g component owner/owner-update |
We need to modify the owner.module.ts
file:
1 2 3 4 5 |
RouterModule.forChild([ { path: 'list', component: OwnerListComponent }, { path: 'details/:id', component: OwnerDetailsComponent }, { path: 'create', component: OwnerCreateComponent }, { path: 'update/:id', component: OwnerUpdateComponent} |
Now we are going to change our owner-list.component.html
file and the owner-list.component.ts
file, to enable navigation between the OwnerList and the OwnerUpdate components:
1 |
<button type="button" id="update" class="btn btn-success" (click)="redirectToUpdatePage(owner.id)">Update</button> |
1 2 3 4 |
public redirectToUpdatePage(id){ let updateUrl: string = `/owner/update/${id}`; this.router.navigate([updateUrl]); } |
Handling Angular PUT Actions in the HTML File
Our owner-update.component.html file is going to be almost the same as the HTML file for creating the owner. Since that’s the case, let’s start with the implementation.
Firstly, add the wrappers code in the owner-update.component.html
file:
1 2 3 4 5 6 7 8 |
<div class="container-fluid"> <form [formGroup]="ownerForm" autocomplete="off" novalidate (ngSubmit)="updateOwner(ownerForm.value)"> <div class="form-horizontal well"> </div> </form> </div> |
We already know from the previous post that the formGroup
is going to contain all of the controls inside its value. This value is exactly what we send as a parameter inside the updateOwner
action.
Secondly, we are going to create our controls between the form-horizontal
div tag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<div class="form-group"> <label for="name" class="control-label col-md-2">Name of owner: </label> <div class="col-md-5"> <input type="text" formControlName="name" id="name" class="form-control" /> </div> <div class="col-md-5"> <em *ngIf="validateControl('name') && hasError('name', 'required')">Name is required</em> <em *ngIf="validateControl('name') && hasError('name', 'maxlength')">Maximum allowed length is 60 characters.</em> </div> </div> <div class="form-group"> <label for="dateOfBirth" class="control-label col-md-2">Date of birth: </label> <div class="col-md-5"> <input type="text" formControlName="dateOfBirth" id="dateOfBirth" class="form-control" appDatepicker (change)="executeDatePicker($event)" readonly /> </div> <div class="col-md-5"> <em *ngIf="validateControl('dateOfBirth') && hasError('dateOfBirth', 'required')">Date of birth is required</em> </div> </div> <div class="form-group"> <label for="address" class="control-label col-md-2">Address: </label> <div class="col-md-5"> <input type="text" formControlName="address" id="address" class="form-control" /> </div> <div class="col-md-5"> <em *ngIf="validateControl('address') && hasError('address', 'required')">Address is required</em> <em *ngIf="validateControl('address') && hasError('address', 'maxlength')">Maximum allowed length is 100 characters.</em> </div> </div> |
Every input element contains a formControlName
attribute which we are going to use in the component file for the validation. Furthermore, the validateControl
and the hasError
functions are the custom functions that will help us display error messages (still the same thing that we did in the CreateOwner component).
Below the last form-group
, add the code for displaying the buttons:
1 2 3 4 5 6 7 8 9 |
<br><br> <div class="form-group"> <div class="col-md-offset-5 col-md-1"> <button type="submit" class="btn btn-info" [disabled]="!ownerForm.valid">Update</button> </div> <div class="col-md-1"> <button type="button" class="btn btn-danger" (click)="redirectToOwnerList()">Cancel</button> </div> </div> |
Finally, below the form tag, let’s add our custom modal components:
1 2 3 4 5 6 7 8 |
<app-success-modal [modalHeaderText]="'Success message'" [modalBodyText]="'Action completed successfully'" [okButtonText]="'OK'" (redirectOnOK)="redirectToOwnerList()"></app-success-modal> <app-error-modal [modalHeaderText]="'Error message'" [modalBodyText]="errorMessage" [okButtonText]="'OK'"></app-error-modal> |
Excellent.
Now we have our HTML file and it is time to implement a business logic for the owner-update.component
file.
Handling Angular PUT Actions in the Component
Modify the owner-update.component.ts
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { ErrorHandlerService } from './../../shared/services/error-handler.service'; import { RepositoryService } from './../../shared/services/repository.service'; import { Router, ActivatedRoute } from '@angular/router'; import { Owner } from './../../_interfaces/owner.model'; import { DatePipe } from '@angular/common'; @Component({ selector: 'app-owner-update', templateUrl: './owner-update.component.html', styleUrls: ['./owner-update.component.css'] }) export class OwnerUpdateComponent implements OnInit { public errorMessage: string = ''; public owner: Owner; public ownerForm: FormGroup; constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, private router: Router, private activeRoute: ActivatedRoute, private datePipe: DatePipe) { } } |
This is the basic setup for this component. We are creating our properties and injecting all the services we need inside this component.
Let’s add this code, below the constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
ngOnInit() { this.ownerForm = new FormGroup({ name: new FormControl('', [Validators.required, Validators.maxLength(60)]), dateOfBirth: new FormControl('', [Validators.required]), address: new FormControl('', [Validators.required, Validators.maxLength(100)]) }); this.getOwnerById(); } private getOwnerById() { let ownerId: string = this.activeRoute.snapshot.params['id']; let ownerByIdUrl: string = `api/owner/${ownerId}`; this.repository.getData(ownerByIdUrl) .subscribe(res => { this.owner = res as Owner; this.ownerForm.patchValue(this.owner); $('#dateOfBirth').val(this.datePipe.transform(this.owner.dateOfBirth, 'MM/dd/yyyy')); }, (error) => { this.errorHandler.handleError(error); this.errorMessage = this.errorHandler.errorMessage; }) } |
In the ngOnInit
function, we instantiate the ownerForm
with all the form controls and add the validation rules. Then we call the getOwnerById
function to fetch the owner with exact id from the server.
Inside this function, we execute the familiar actions. Pulling the id from the url, sending the PUT request and processing the response whether it is success or error response.
One thing to pay attention to is the converting the dateOfBirth
value from the format with hours, minutes and seconds, to the format we expect inside our input control. With the JQuery function, we place that value in the input field. Notice that in this example using the date pipe is not restricted only to HTML files. But to make it work inside a component, we need to import the DatePipe
pipe and inject it inside the constructor. We also need to import the DatePipe
inside the app.module.ts
file and to place it inside the “providers
” array.
Let’s add additional functions below the getOwnerById
function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public validateControl(controlName: string) { if (this.ownerForm.controls[controlName].invalid && this.ownerForm.controls[controlName].touched) return true; return false; } public hasError(controlName: string, errorName: string) { if (this.ownerForm.controls[controlName].hasError(errorName)) return true; return false; } public executeDatePicker(event) { this.ownerForm.patchValue({ 'dateOfBirth': event }); } public redirectToOwnerList(){ this.router.navigate(['/owner/list']); } |
These are the familiar functions for validating the input fields, patching the value in the ownerForm property and redirecting to the OwnerList component.
Finally, we need to execute the update action by adding the code right below the redirectToOwnerList
function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public updateOwner(ownerFormValue) { if (this.ownerForm.valid) { this.executeOwnerUpdate(ownerFormValue); } } private executeOwnerUpdate(ownerFormValue) { this.owner.name = ownerFormValue.name; this.owner.dateOfBirth = ownerFormValue.dateOfBirth; this.owner.address = ownerFormValue.address; let apiUrl = `api/owner/${this.owner.id}`; this.repository.update(apiUrl, this.owner) .subscribe(res => { $('#successModal').modal(); }, (error => { this.errorHandler.handleError(error); this.errorMessage = this.errorHandler.errorMessage; }) ) } |
That’s it.
Give it a try and make some updates. Try to create success responses and error responses from the server to test the modal components out as well. After that, you can check if the form validation works.
Conclusion
By reading this post you have learned:
- How to create HTML part of the update action
- The way to patch values from the server into the form group
- How to send the PUT request to the server
Thank you for reading the post, hopefully, it was helpful to you.
In the next part of the series, we are going to write the delete part of the project, and slowly wrap the coding part of the series up.
If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published Angular content we encourage you to subscribe to our blog.
hey man, when i click in update, miss something datePipe and a looked in google to solve the trouble and this way is possible
solve :
in class owner-update-component
put
@Component({
providers: [DatePipe]
});
Hi Leonardo. I believe your solution is just fine but there is no need to it. Again I have checked my code and it is working perfectly. You don’t have to populate providers array inside the owner-update-component, you may do exactly as I wrote in the article:
“But to make it work inside a component, we need to import the DatePipe pipe and inject it inside the constructor. We also need to import the DatePipe inside the app.module.ts file and to place it inside the “providers” array.”
So as you may see, you need to import the DataPipe pipe in a component, to register it in a constructor and as well to import it in the app.module and to place it in the providers array.
With your way you have registered your pipe just in one component and to use it in another you need again to put it in providers array of the component. But with my way, you only need to import it and register it with the constructor method.
All the best.
Good morning Marinko. Im writing to you because i have a problem at this point and i dont know how to solve it. Could be fantastic if u can help me. I have a problem when I press the button from the list to update one of the “owners” (i named it TiposProyecto).
When i press the button in the owner-list to update, it jumps to the /404 route.
I have no errors in the code but when I press F12 in google chrome to see the developers console, I get this error. Also i have the same problem when i want to delete from the list
zone.js:2969 GET http://localhost:5000/api/TiposProyecto/undefined 404 (Not Found)
Could you tell me how to solve this problem?
Thx dude
Hello Sergio. Thank you very much for reading our posts. I hope it is helpful to you. Let me try to help you about your error.
First of all i f you look at your GET request (GET http://localhost:5000/api/TiposProyecto/undefined) you are going to see the undefined part. This is the place where the GUID of your owner object (that you want to update) should be placed. But you are mapping it wrong thus having the undefined value. Your app works perfectly because your server can’t find the user with the none existing Id and returns to you 404 NotFound().
Now how to solve it:
1) Take a look at your owner.module.ts file, there must be these two lines of code
{ path: ‘update/:id’, component: OwnerUpdateComponent },
{ path: ‘delete/:id’, component: OwnerDeleteComponent }
2) Take a look at your owner-list..component.html file. Both update and delete buttons should call the corresponding functions and pass the owner.id parameter. Also check if your owner object returned from the server has that id property.
If none of this helps, you could update your project and send me the link. Then I could take a look at it. It seems as if you made mistake in the owner.module file or that you don’t have the id property (maybe it is named ownerId or something like that)
At this point this is all I could do, until you send me your code.
All the best mate.
Good morning, Marinko. First of all, I wanted to thank you for helping me out the other day. As you said, I was mapping the id incorrectly, specifically in the list-component.html form. Now when I press the update or delete button from the list, I can access into both forms for that particular id that I’m accessing without problems.
But I have another problem and I’d like to tell you if you can help me. As I told you before, I can now access both the update and delete forms of a specific id, but when I click the button to confirm the changes, the modal window doesnt appear and the google chrome development console gives me these errors:
PUT http://localhost:5000/api/TiposProyecto/undefined 400 (Bad Request) :5000/api/TiposProyecto/undefined:1
DELETE http://localhost:5000/api/TiposProyecto/undefined 404 (Not Found)
5000/api/TiposProyecto/undefined:1
If you can help me out here, I’d really appreciate it, thanks again, Marinko.
Hello Sergio. I am sorry to see that you still have some problems, but I am glad that I have helped you previously. So I will try to do it again. It is very strange that you are not getting any modal messages, I have tried to simulate your bad requests and the modal window is always there with appropriate message. So, probably you have some wrong implementation in there as well.
Concerning your current problem, as you may see, you have undefined part again in your requests. So I am assuming that your object id is undefined and maybe complete object is bad. Do you see any data populated in the input fields, once you land on the update page? If not than something is wrong with your object retrieved from the server while landing the Update or Delete page.
Again, I can’t help you a lot like this. All I know is that your Id param i undefined. It could help you if you could place a break point inside chrome developer tools to the place where you send that request and to inspect what is the Id value.
Finally you can upload your code and send me the link, than I will go through it and try to find what is wrong.
All the best mate.
Hi Marko,
Thanks for putting this tutorial, its awesome..
Sergio,
I was also facing the same problem. I think the solution is a change in the owner.model.ts In one of the previous tutorials, it was changed to “ownerId”, change it to “id”. It should work.
OwnerUpdateComponent.ts makes us of this in the “let apiUrl =
api/owner/${this.owner.id}
;” and intellisence suggests this.owner.ownerId and then the trickle effect is Api is having an issue.Hope this 2 cents helps.
Thanks,
Hello Asterix. First of all thank you for the kind words. Second thing, thank you so much on your suggestion. When I have red your comment I thought whaaat??? why would I leave the ownerId property anywhere? And then went through all of the articles and in part 11 Error Handling I saw it 😀 😀 My god. I have changed the owner.module.ts file by adding an Account array and the God knows why, I left ownerId instead of id. Can’t believe I did that 😀 One more time, thank you so much.
All the best mate.
Thanks Marinko,
My apologies for calling you Marko.
I am looking forward to more of your tutorials. Can you build up on this and make a series on OAuth for Core webapi?
Thanks
You don’t have to apologizes it is almost the same 😀 We have published JWT with .NET Core and Angular, maybe you will find that interesting: https://code-maze.com/authentication-aspnetcore-jwt-1/ Also we have the web api best practices article, so you may find a lot of good advice there. About OAuth, it is a good idea, right now we have some plans but as soon as we find spare time, it would be done.
One more time thank you a lot, it is always a joy to talk to you.
Hi I have a question. Does your angular solution handle DataAnnotations ( [Required(ErrorMessage = “Name is required”)]) which you have marked in Owner.cs in your .net backend?
or are you basically repeating the validation in the angular component?
Hello Truth. Well you are correct and you are not 😀 This is the Angular series, and we show you how to consume the .NET Core Web API server application. So basically when you write your client app, you can’t rely on the server side validation, because you don’t know is it written at all. Same way goes for the server side.
In this situation we are writing both sides (server and client) but in many cases you would maybe just write consumer app or just the server app, so in that way you have to secure your app side. This is why I have created validations on both sides.
I understand why did you ask this question, but I hope my answer clarifies that.
Yes you answered it thanks. Web api with mvc razor pages and fluent validation, one is able to get away with just validating in backend api and it some still generates client side code. That way you dont have validation written twice. Im new to angular so seeing if there is something similar.