3.2 Creating Tags
We have the ability to edit and delete tags; now we just need to create them. As we're writing the code to do this, we'll write a custom model binder.
1.Introduction2 lessons, 05:56
2.Managing Posts3 lessons, 38:13
3.Tag Management2 lessons, 27:29
4.Storing Data3 lessons, 47:22
5.Testing Functionality2 lessons, 33:06
6.User Management3 lessons, 1:11:48
7.Security and User Authentication2 lessons, 34:17
8.Enhancing the User Experience2 lessons, 36:38
9.The Front-End2 lessons, 36:01
10.Conclusion1 lesson, 01:22
3.2 Creating Tags
We have the ability to edit and delete tags, but of course we need some way to create them. As I mentioned in the previous lesson, we are going to do that inside of the post controller. When the user either creates or edits a post, that is where we will determine if we need to create a tag. And this is what I have in mind. This is a demo of WordPress. And this is a form for creating a new post. If you scroll down, we have a section for tags that has a single text box, and this is where we type our tags. And you can see that we have to separate them with a comma. So if we wanted to apply the C# tag to this post, we just type C#. And you can see that we have an autosuggestion here. That would be nice to implement within our system. And the reason why we have an autosuggestion is because there is a tag that is already created of C#. But if we wanted to create a new tag, like foo. We don't have any autosuggestion here at all. So whenever I click on the Add, it's going to add the C# and the foo tags. But right now the foo tag isn't created. If we go and look at the tags, we see that we have a ASP.NET, C#, MVC, and Tag1 But we don't have foo. That's because the foo tag is only created when the post is created or edited. And that's exactly what I want to do here. So let's start by going to our view, or at least the partial view that displays the form for editing or creating a post. Because this is where we need to add a new field for our tags. So, we have our ID, which is the slug, or the part of the URL that identifies the post. But before that, we have Title, then we have ID, then we have the content. We have the date for when it was created, the date for when it was published and after the date for published that's where we can apply our tags. So let's just copy the HTML for our published field. And let's add it in here. And then we can change this model.Published to model.Tags. Now if you'll remember, this is an IList. I believe it's an IList. Let's look at our model class. And it is an IList, so if we use an EditorFor with the Ilist, then I believe the default behavior is to display a text box for each individual element within our list, and that is not what we want. We want a single text box that contains a comma separated list of all of our tags. And we can approach this in a variety of different ways. For one, we could add a new property to our post class. It would be a string property that contains all of our tags separated by commas, and then inside of our form, we could have a field for editing that new property. And we have might have to do something like that anyway, because if we choose a SQL database as our data store, and if we use Entity Framework, then we will need another property for representing the tags within the database table itself. Because an IList of string isn't a very good representation of a column within a database table, but we will cross that bridge when we get there. For now, I want to use a feature of ASP.NET MVC, one that allows us to define our own editors for any type of data that we might need an editor for. So let's go to our solution explorer and inside of the shared folder, in our views, let's right-click. And we want to add a new folder called editor templates. And this is where we can put our custom editor templates for anything that we want to edit. In our case, we want to edit an IList of string. So let's add a new view. And let's call this view TagEditor. And we want this as a partial view. And let's click on Add. Now we need to specify our model, which is an IList of string, although we can probably get away with an IEnumerable of string, so lets do that. And then we simply want the HTML for editing our tags. So we can have an Html.Textbox, we don't need to specify anything for the name or the ID because that going to automatically filled in for us, but we do need to supply the value here. So we want to take our IEnumerable of string and turn it into a comma separated list of values, so we can use string.Join. We want to join each of the elements with a comma space, and then the collection is our model. But then we also want to get the html attributes that were specified from whenever we called the EditorFor method. You can see that over here we have this htmlAttributes, so we want to apply these attributes to this text box, and we do that by specifying our view data, and then htmlAttributes. And that would give us basically everything that we need for editing our tags. So let's save this, and we can close this. We can go back to our form, and we need to specify that we want to use our tag editor fo the editor of our tags. And we do that after our model expression here, so we have model.Tags, and then we have "TagEditor" and then we have our HTML attributes. So now let's go to our post controller and let's go to the Create action method. Whenever we create a post, just for the sake of testing, let's create a new list for our tags. So new list of string and let's populate it with two items. Let's have test-1 and then test-2 and then we will run our application and whenever we go to the form for creating the post, we should see our two tags separated by a comma and a space, although we have a build error here, so let's see what the problem is. Let's see, we have two delete methods with the same parameters in our tag controller. So we have our get which is a string, and then our post, which is a string. That is of course a problem. So let's add another parameter to one of these methods just to have a different signature between the two overloads. Let's run this, and everything should be fine now. So, we want to go to admin and then post and create, although we run into another issue. No parameterless constructor defined for this object. This is for our post controller. And the reason is because we have a constructor, but it accepts an IPostRepository. So we are going to have to bite the bullet, and create a post repository. So let's add a new class, let's call it PostRepository, and this needs to implement the IPostRepository interface. We can hit Ctrl dot and Enter to implement those methods, but we aren't going to write them. We're just going to leave this as is. So let's run, oh, we can't run it just yet because we need a default constructor here. So, public PostController() and this is going to call this() and pass in a new PostRepository object. So now everything should work. So whenever we go to Admin, Post, and Create, we should our form, and we should see our list of tags inside of a textbox. And we do, we have our comma-separated values. So before we submit this form, let's go back to Visual Studio and I want to set a breakpoint whenever we submit the form. So right here on line 50, whenever we submit the form, we will hit this breakpoint and I want to inspect our model object. So let's click on Submit, we hit the breakpoint, and let's look at our model. So we have a Tags property, and it has a single element inside of our list and if we drill down, we see what that value is. So, the single element in our IList of string is the actual value that was in the text box. The model binder did not split this string on the commas. So we are going to have to do that. We need to tell the model binder that says that, hey we need to take this value for our Tags field, split it on a comma, and then set that collection to our Tags property. And in order to do that, we need to write a custom model binder. So let's go back to our Solution Explorer and inside of our Models namespace, let's add another folder called ModelBinders. We are going to add a class inside of this folder called PostModelBinder. And then we are going to inherit from the DefaultModelBinder. This is inside of the System.Web.Mvc namespace. Now if you notice that there was another namespace that we could choose, but that's not what we want. We want the DefaultModelBinder in System.Web.Mvc. And there are many things that we can overload from the default ModelBinder. But in our case, we only want to change the behavior of getting the value from our form and binding it to a property on our Model class. And in our case it's only one property, the Tags property. So we are going to override a protected method called GetPropertyValue. This has four parameters. One is the ControllerContext, the other's the bindingContext. Then we have the PropertyDescriptor which gives us information about the property. And then we have the propertyBinder. Now in most cases for our post class, we only want custom behavior for the Tags property. So, the first thing we can check to see if the property is not tags, then, we will use the default behavior. So, we will use the property descriptor object. It has a name property and we want to ensure that it is not Tags, when it's not, we will use the base GetPropertyValueMethod. Otherwise, we need our own custom functionality. So the first thing we need is the value from the form. So let's create a variable called tags. And when we use our bindingContext object here, so bindingContext. This has a property called value provider. And this has a method called GetValue. Now if we look at the value provider. Actually we aren't going to be able to see it, but the value provider gives us a value for a variety of different things. One of those is our form. So whenever we call the GetValue method, we have to specify the key from our form, and that is Tags. And then this has a property called AttemptedValue. So it's possible that we don't have an actual value because tags could be empty. So let's first of all check to see if string is null or whitespace. We'll pass in tags, and if this is the case, then we still want to return something because we have to return something. But we don't want to return null, because this is a collection of tags. A more useful value would be just an empty list. So we will return a new list of string. Otherwise we want to take our tags and we need to split that on a comma. So return tags.Split. And here we will have a new array of characters. A single character will be a comma. Now it's possible that our tags are separated by a comma space. So, we need to do something more than just split on a comma here. We need to select the string. Which I'm going to use t here for tag. Then we need to call the Trim method on that string and then we can call the ToList method. So let's just go over this once again. We're checking to see if the property is not a tag. If it's not, then we use the default GetPropertyValue otherwise we get the Tags value from our form. We check to see is we have something to work with, if we don't then we return an empty list of string. Otherwise, we take our Tags value, we split it on a comma. Then we Trim each string and the resulting array from calling Split and then we convert that to a list. So now we just need to tell our application to use the post ModelBinder whenever we want to bind data to our post class. So let's go back to our solution explorer. Let's do that to inside of global.asax. So after we set up the area registration and the routes config we want ModelBinders.Binders and we want to Add, the key is the typeof our Post class. And then we new up our PostModelBinder. So whenever we run our application once again, we will go to our create form. So we need to go to admin, post and create. We will see our tags within the text box. Let's ensure that we still have that breakpoint, so lets go back to our PostController. And we no longer have it so that it's set it here, that's submit the form, and then let's inspect the Model class, or the Model object. So we see our tags, and we have a count of 2, and if we drill down, we have the first element of test-1, the second element of test-2. So now we are all set for editing tags within our create and edit post form. Now we just need the functionality of not only creating tags, but creating the posts and storing them. And we will get started with that in the next lesson.