4.3 Working With Forms
In this lesson, you'll learn how to display data in a form using tag helpers, and how to use data from the user to update your model.
1.Introduction1 lesson, 01:39
2.Getting Started3 lessons, 34:08
3.Routing and Processing1 lesson, 13:51
4.Working with Data3 lessons, 35:32
5.Framework Features1 lesson, 05:22
6.Conclusion1 lesson, 01:15
4.3 Working With Forms
Over the past two lessons we have focused on displaying data and that is very important because, well there's no sense in having a web application if you aren't displaying something. But that's only half of the story. A web application typically needs to do something with user input, either with a user creating a new entity of some kind or updating an entity. So in this lesson we're going to focus on the edit method on our guitars controller. So that we can display a form so that users can then update the data that we have for our products. So, let's start with the edit method. Right now we're just displaying the id that is given to us from the URL. Basically, we want to do the exact same thing that we are doing with the single product. We want to take that id and attempt to find a given guitar. If it doesn't exist then that resource is, of course, not found. So we return a not found result. But if we have a guitar then we pass the guitar on to the view. And we need to change this return type to IActionResult. So we are essentially done with this method. Now we just need a view for our edit method. So let's go to the guitar's folder. Let's create a new file called edit.cshtml. And the first thing we need to do is specify the type of model that we are working with. And that is secondapp.Models.Guitar. Now this is the third time that we've had to use the fully qualified name for our guitar class. Wouldn't it be nice if we could just say model guitar and we can. If we go to the _ViewImports file, we're going to see two lines of code. These are essentially things that are being imported into our views so that we don't have to fully qualify them within our views. So you can see that there is a using statement for secondapp. So anything that is within the namespace secondapp is already being imported in, so that we can use that in our views. So in our case we just need to add another using statement for secondapp.models and we're done there. So now all we have to do is just say the guitar class. Okay so we know we are working with a guitar. Let's have a title that says that we are going to edit a particular guitar. So we can say @model and we will use the manufacturer property and then we will also use the model property as well. And I guess we should go ahead and use the years. So basically what we did in sort of the single product view, but in this case we're also going to have a form. Now we can write to a normal form element where we set the action equal to products /guitars /we need the IDs so that would be at model. .id and then edit. And that would work, but we can also use a feature called tag helpers. Now a tag helper is nothing more than C sharp code that generates something for markup. It could be an actual element or could be part of an element. And I can show you some examples. If we go to the shared folder inside of our views and the layout page, you'll see several tag helpers. The first is this environment element. Now this is not a standard HTML element but that's okay, because the environment element is never sent to the browser. This is a tag helper, so when the razor view engine is building the HTML that's going to send to the browser, it sees this environment element. It knows that this is a tag helper so it executes the code for that environment tag helper. So if the environment is the development environment, then these elements are going to be in the HTML output. But if the environment is staging or production, then it's going to use these, CSS files. So this is a way of conditionally loading resources based upon what environment our application is currently running in. There's another example here for these A elements. You can see that these have these ASP dash attributes. These are of course not standard but they will never reach the browser. These are used by the Razor view engine to generate a URL based upon the controller name and the action for that controller. And you can also specify some route details as well. So when it comes to creating a form, we can use tag helpers as well. We can come in here and we can say asp-controller="guitars", and this is going to be our guitars' controller. We can also say, asp-action="edit". And if we looked at nothing else but the HTML that this outputs, we're going to see a URL that matches what we would expect. So let's go to the browser let's refresh although we do need to go to the edit page. Now we don't see the form, of course, because there's nothing inside of the form. But if we view the source, whenever we scroll down, here is our form action products guitar's one edit. So it automatically generated the URL. And this particular feature is nice because if we ever change our URL structure then that's automatically going to be reflected because all we had to do is specify the controller and the action. We don't have to go back into our views and modify all of our URLs, that's just going to flow through automatically. Now we can also use tag helpers to help us populate fields within our form. So let's create a div that's going to be a form group and we need a label and a text box. So we'll start with the label and we will say asp-for, and then we use the property on our model that this label is for. So we have the manufacturer that we want to edit. So we simply just say manufacturer. Let's give this a class of control label and then we will close that tag. And we will essentially do the same thing for the input element. We don't have to specify the type. We instead say asp-for, we'll say manufacture and then the MVC framework or rather the tag helper is going to make some assumptions based upon the data type of the manufacturer property. And if it's wrong then we can also go in and change that, but we're not going to look at that. Instead, we're just going to let it do what it is supposed to do. So if we go back to the browser and refresh, we are going to see a label that says manufacturers. So the label is pulling in the name of the property. And then the text box is going to have the value of that property. So all we need to do now is just copy and paste three times for our three properties. And then change the value of asp-for. So we have manufacturer, we have model, and then we have year. And then finally we just need a submit button, which we can do in a variety of different ways. One of those is with a button elements, but I am going to use an input element. And that there's nothing special here, there's no tag helpers, it's just input type submit. And the value is going to be Edit Guitar. And so now, whenever we go back to the browser and refresh, we have our complete form that has all of our fields ready to edit. So now we just need to write the code in our controller that's going to accept this data and update our guitar. So let's go back to our guitar's controller. And we're going to write another method called edit. So we're going to have an edit method for get requests and another one for post requests. And in our case, it's going to be okay if we just leave that alone. But it's always a good idea to explicitly specify the HTTP method for a given action method, if there are two methods of the same name. So we can say HttpGet for the first edit method. And then we would say HttpPost for the second one. And we can also go ahead and validate the anti-forgery token because that is automatically added to our form. If we go back and look at the form element, we could come in here and we can say ASP-anti-forgery. And we could say false, and then the anti-forgery token would not be output in the form, but we always want to check for anti-forgery. So that means that all we then have to do is validate that token by specifying an attribute on our post version of the edit method, and then we're good to go. So now we need to get the data from our form and we have that as a form collection. And we'll just call that form. And this is actually going to be an I formed collection this resides within Microsoft ASP.NET Core, HTTP. So let's add a using statement for that. And the beginning of our method is going to look a lot like what we have done before. We are going to attempt to retrieve a guitar with the given ID. If it doesn't exist then we return not found. But then we want to try to update our model. And we can do that with a method called TryUpdateModelAsync. Now this is asynchronous, so this means that we need to add async to our method definition. And the return type is now a task of IActionResult. And then we pass in the object that we are trying to update. And it's going to automatically update based upon the data from our form. So we don't have to specify that, although I do think we can in one of these overloads, but we don't need to here. So it's going to take the data from our form. It's going to try to update our guitar model. But then, we need to save it. And then we can say repo and then Update(guitar). And then we just need to take the user to some other place than the form that they just edited. And one of the things that we can do is just take them back to the index. So we're going to call a method called RedirectToAction. We will specify the index action. But a lot of times it's a good idea to be explicit as to what controller that action is on. So we will say control or equal guitars in a new object. And that should be good enough. So now we just need to implement our update method because that was not done. So let's go to data, let's go to a static guitar repository. And inside of the update method, we want to retrieve the guitar with the given ID. So let's say var existing = we'll call the get method. We'll use the entity's ID and if we don't have an existing then we would definitely want to do something, but in this case we're just going to return, otherwise we want to update the properties on our existing objects. So we will say existing.Manufacturer = entity.Manufacturer;. And we would do the same thing for the Model property as well as the year property. And that should be it. Since this is an in the memory collection, we don't need to actually save this because it's automatically going to be done, at least until we restart our process, so that should be it. Let's go back to the browser. Let's refresh our page here. Hopefully everything is going to compile and work just fine. It looks like it did. And let's change the year to 2016. There was a new line of Talman released in 2016 so that makes sense. And while the data was updated the URL is not correct. This should just be products/guitars and as I look at the links in our menu, those URL's are definitely not correct. So when you start to see issues with your URLs, it comes from your routes. There's something there that just hasn't been set up correctly. And that's very easy to do because of, well, the routing engine is very flexible. We have these parameters so that we can try to fit so many situations with too few routes and I can't tell you how common that is. So when you start to see issues with your routes then it's time to look at your current routes and see what you can do to fix it. Now we could spend a few minutes and fiddle around and try to make all of this work, but I know what is going to work and that is going to use specific routes for our guitars controller. Like in this case, for the products I thought that this would work for all of our products, be it guitars or pianos or something that wasn't musically related. But instead we're just going to have routes specific to our guitars. I'm just pasting this in to save some time, but basically all of our other routes except for the default route are gone. And so instead we have a route called guitars-action. This is products guitars ID and action. The defaults uses the controller set to guitars, so that that is going to fit that route. Then there is a route for a single guitar products guitars ID, and then the controller and action are set for the defaults. And then finally, for the guitars index. We have products/guitars. And the controller and action are set. Now this is going to work except for one case. Whenever we edit our guitar, we submit the form, that's going to take us to a URL that says products guitars, the ID, and then index. And the reason why is because we have all of this route data. We have the controller, we have the action, and we have the ID. So, when it comes time to generate that URL, it's going to try to do the best and it's going to find that hey, this route has all of those pieces. So I'm going to use this URL and that's exactly what happens. So when cases like that arise. What you can do is instead of redirecting to action, you can say redirect to route, and then you specify the route guitard-index and usually this is the best thing to do. Whenever you need to generate URLs, you want to do so based upon the route itself because then you're going to get accurate URL's most of the time. So let's go to the edit page for this and we are going to see our form. Let's change the year once again to 2016. That will edit our guitar. We get sent back to the correct URL. We see the correct Content. And as we look at the menu the URLs are correct. So you now know how to not only display data, but receive data from your users and protect yourself from forged requests. We also talked a bit more about routing. Adjusting your routes is something that you will probably, regularly do as you write your application. Well, in the next lesson, we're going to look at ASP.NETs built-in dependency injection service, so that you can write more maintainable and testable code.