ASP.net MVC 2.0 using the same form for adding and editing
I would like to use the same view for editing a blog post and adding a blog post. However, I'm having an issue with the ID. When adding a blog post, I have no need for an ID value to be posted. When model binding binds the form values to the BlogPost
object in the controller, it will auto-generate the ID in entity framework entity.
When I am editing a blog post I do need a hidden form field to store the ID in so that it accompanies the next form post. Here is the view I have right now.
<% using (Html.BeginForm("CommitEditBlogPost", "Admin"))
{ %>
<% if (Model != null)
{ %>
<%: Html.HiddenFor(x => x.Id)%>
<% } %>
Title:<br />
<%: Html.TextBoxFor(x => x.Title, new { Style = "Width: 90%;" })%>
<br />
<br />
Summary:<br />
<%: Html.TextAreaFor(x => x.Summary, new { Style = "Width: 90%; Height: 50px;" }) %>
<br />
<br />
Body:<br />
<%: Html.TextAreaFor(x => x.Body, new { Style = "Height: 250px; Width: 90%;" })%>
<br />
<br />
<input type="submit" value="Submit" />
<% } %>
Right now checking if the model is coming in NULL is a great way to know if I'm editing a blog post or adding one, because when I'm adding one it will be null as it hasn't been created yet. The problem comes in when there is an error and the entity is invalid. When the controller renders the form after an invalid model the Model != null
evaluates to false, even though we are editing a post and there is clearly a model. If I render the hidden input field for ID when adding a post, I get an error stating that the ID can't be null.
Edit
I went with OJ's answer for this question, however I discovered something that made me feel silly and I wanted to share it just in case anyone was having a similar issue. The page that adds/edits blogs does not even need a hidden field for id, ever. The reason is because when I go to add a blog I do a GET to this relative URL BlogProject/Admin/AddBlogPost
This URL does not contain an ID and the action method just renders the page. The page does a POST to the same URL when adding the blog post. The incoming BlogPost
entity is populated by model binding and has a null Id that will be generated by EF during save changes.
Now when editing a blog post the URL is BlogProject/Admin/EditBlogPost/{Id}
. This URL contains the id of the blog post and since the page is posting back to the exact same URL the id goes with the POST to the action method that executes the edit.
The only problem I encountered with this is that the action methods cannot have identical signatures.
[HttpGet]
pub开发者_如何学Clic ViewResult EditBlogPost(int Id)
{
}
[HttpPost]
public ViewResult EditBlogPost(int Id)
{
}
The compiler will yell at you if you try to use these two methods above. It is far too convenient that the Id will be posted back when doing a Html.BeginForm()
with no arguments for action or controller. So rather than change the name of the POST method I just modified the arguments to include a FormCollection
.
Like this:
[HttpPost]
public ViewResult EditBlogPost(int Id, FormCollection formCollection)
{
// You can then use formCollection as the IValueProvider for UpdateModel()
// and TryUpdateModel() if you wish. I mean, you might as well use the
// argument since you're taking it.
}
The formCollection variable is filled via model binding with the same content that Request.Form
would be by default. You don't have to use this collection for UpdateModel()
or TryUpdateModel()
but I did just so I didn't feel like that collection was pointless since it really was just to make the method signature different from its GET counterpart.
Let me know if you find a better way to make this work. The only part I'm shaky on is taking in an unnecessary variable to make the method signature different.
A few options:
- Make your
Id
propertyNullable
and check forHasValue
. - Add some kind of
mode
indicator to yourViewData
and show the Hidden field depending on the value. - Put the body of the form in a partial view and include that in two different views, one with and one without the hidden field.
What I did in my project is the following:
- Have a separate action and view for editing and adding
- Pass along the ID of the object in the edit URL instead of in a hidden field
- Use a shared editor template for the object to avoid writing the same boilerplate code twice
If you do this correctly the add and edit views will be very small.
精彩评论