Vini Vini - 6 months ago 32
Ajax Question

Rendering a partial View with Ajax Unobtrusive

I have the following View and Partial View. In the index action method I need the Create to rendered as a Partial View. Based on the answer in this question I tried using Ajax helper methods(I find AJAX and JS so hard for me to comprehend).

@model IEnumerable<TEDALS_Ver01.Models.UserRight>

@{
ViewBag.Title = "Index";
}
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.UserName)
</th>
<th>
@Html.DisplayNameFor(model => model.UserCode)
</th>
<th>
@Html.DisplayNameFor(model => model.IsReader)
</th>
<th>
@Html.DisplayNameFor(model => model.IsEditor)
</th>
<th>
@Html.DisplayNameFor(model => model.IsExporter)
</th>
<th>
@Html.DisplayNameFor(model => model.IsAdmin)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserCode)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsReader)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsEditor)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsExporter)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsAdmin)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.UserRightID }) |
@Html.ActionLink("Details", "Details", new { id=item.UserRightID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.UserRightID })
</td>
</tr>
}

</table>
<p>
@Ajax.ActionLink("Creates", "Creates", "UserRights", new { area = "Creates" }, new AjaxOptions { UpdateTargetId="Creates"})
</p>
<div id="Creates">
@Html.RenderPartial("_Creates",Model);
</div>


Partial View

@model TEDALS_Ver01.Models.UserRight
@Scripts.Render("~/bundles/jqueryval")
@using (Ajax.BeginForm("Creates", "Creates", new AjaxOptions { HttpMethod = "Post", InsertionMode = InsertionMode.Replace }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })

@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })

@Html.LabelFor(model => model.UserCode, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.UserCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserCode, "", new { @class = "text-danger" })

@Html.LabelFor(model => model.IsReader, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.IsReader)
@Html.ValidationMessageFor(model => model.IsReader, "", new { @class = "text-danger" })

<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}

<div>
@Html.ActionLink("Back to List", "Index")
</div>


I get error while executing :

Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

Compiler Error Message: CS1502: The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments


Any help would be appreciated. I have tried changing the arguments for RenderPartial in many ways and found no success.

Comments

I have added all the scripts in my Layout.

Error

An exception of type 'System.InvalidOperationException' occurred in System.Web.Mvc.dll but was not handled in user code

Additional information: The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[TEDALS_Ver01.Models.UserRight]', but this dictionary requires a model item of type 'TEDALS_Ver01.Models.UserRight'.


EDIT based on the inputs from Greg

Main View

@model IEnumerable<TEDALS_Ver01.Models.UserRight>
@using TEDALS_Ver01.Models
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.UserName)
</th>
<th>
@Html.DisplayNameFor(model => model.UserCode)
</th>
<th>
@Html.DisplayNameFor(model => model.IsReader)
</th>
<th>
@Html.DisplayNameFor(model => model.IsEditor)
</th>
<th>
@Html.DisplayNameFor(model => model.IsExporter)
</th>
<th>
@Html.DisplayNameFor(model => model.IsAdmin)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserCode)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsReader)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsEditor)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsExporter)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsAdmin)
</td>

<td>
@Html.ActionLink("Edit", "Edit", new { id=item.UserRightID }) |
@Html.ActionLink("Details", "Details", new { id=item.UserRightID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.UserRightID })
</td>
</tr>
}

</table>

<input type="button" value="Create" id="Creates" />
<div id="Creates">
@{
Html.Partial("_Creates",new TEDALS_Ver01.Models.UserRight());
}
</div>


I understand some scripts that should be used to get it right is missing. But I don not know where the scripts should be added. I have come across the same problem before and unable to resolve I excluded the part that used scripts.

My Create button doesn't do anything. In the Browser console I get the error Element not found.

Answer

The error message that you get states

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[TEDALS_Ver01.Models.UserRight]', but this dictionary requires a model item of type 'TEDALS_Ver01.Models.UserRight'

This is saying that the view you created only handles one UserRight but you gave it a List<UserRight>. The error seems to happen here in your main view:

<div id="Creates">
@Html.RenderPartial("_Creates",Model);
</div>

The current Model is a collection so rendering _Creates.cshtml with a that object will fail.

Normally, the way around this is to loop over model

 @foreach(var modelItem in Model) {
  <div id="Creates">
    @Html.RenderPartial("_Creates", modelItem);
  </div>
 }

But in this case, I don't think it's what you want at all.

Right now if you deleted the call to RenderPartial it will likely compile and when you click the Creates link it will send a GET to the Creates action which will render whatever view its wired to (which sounds like the partial you posted in your question).

The user then fills out the form in the partial and POSTs it to Creates and the div#Creates is then replaced again with whatever view comes from the post action.

It sounds like what you really want is to render the form and then replace the form when it's POSTed. To do that you'll first have to remove the @Ajax.ActionLink since you don't want div#Creates to be replaced by a link when the page loads. You're on the right track rendering that partial, but the problem is object you gave it.

You can try giving it an empty UserRight

<div id="Creates">
  @Html.Partial("_Creates", new TEDALS_Ver01.Models.UserRight());
</div>

But if that doesn't work you'll need to create a ViewModel for that view that contains the list of UserRights you want displayed as well as a template UserRight that your form can use.


As a side note, because I don't have enough points to comment on @dylan-slabbinck answer, you want to use @Html.Partial, not @Html.RenderPartial.