Error in rendering partial view in asp.net mvc ajax - asp.net

In asp.net ajax, the partial view response replaces the html and only the partial view is rendered.
In the below snippet, when I click the ActionLink "Steps", the partial view is returned and replaces the Details.cshtml.
What I need is this partial view should replace only the "main-content-div" div.
Please help out. Thanks
View: Details.cshtml
<div class="main-body">
<table>
<tr>
<td class="left-sidebar extended">
#Ajax.ActionLink("Steps", "Steps", new { id = ViewBag.Id }, new AjaxOptions { AllowCache = false, UpdateTargetId = "main-content-div", InsertionMode=InsertionMode.Replace })
</td>
<td class="main-wrapper">
<div class="main-content" id="main-content-div">
This is the default stuff which I'll be replacing...
</div>
</td>
<td class="right-sidebar"></td>
</tr>
</table>
Action Method:
[HttpGet]
public PartialViewResult Steps(string id)
{
//reading model from db
return PartialView("~/Views/Author/Exercise/_StepsPartial.cshtml", Model);
}
Partial View:
#model IEnumerable<Model>
<div>
<div>
<input type="text" />
</div>
</div>

You need to add jquery.unobtrusive-ajax.min.js to your project for this to work.
Go to the Nuget Console Package Manager console and type:
Install-Package Microsoft.jQuery.Unobtrusive.Ajax
You then just need to add a reference at the top of your _Layout.cshtml or View to the relevant script:
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>

Related

How to post table rows data to a controller w/o using ajax in asp.net core

What is want to achieve is I have a form to adds rows with data to a html table, it's like a temporary table and all the data from it will be added in just one submit button. How can I possibly do this?
This is my sample table structure, data from it must be added to db
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<table class="table">
<thead>
<tr>
<!--some other fields-->
<th>Amount</th>
<th>Check #</th>
<th>Check Date</th>
</tr>
</thead>
<tbody>
<tr>
<!--some other fields-->
<td>
<input asp-for="Amount" class="form-control" />
<span asp-validation-for="Amount" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_No" class="form-control" />
<span asp-validation-for="Check_No" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_Date" class="form-control" />
<span asp-validation-for="Check_Date" class="text-danger"></span>
</td>
</tr>
<!--row 2-->
<!--row 3-->
<!--row 4-->
<!--etc..-->
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
And here is my controller code so far, i don't know what do I need to change
// POST: Books/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Year,Month,Document_Code,GLA_Code,Expense_Code,Function_Code,Document_Reference_No,Document_Date,Quantity,Amount,Check_No,Check_Date,Remarks,Encoder")] Book book)
{
if (ModelState.IsValid)
{
_context.Add(book);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
What must needed to be change in my view and controller, and help will is appreciated.
Thank you.
There's a couple of issues here.
First, if you've got a table of "books" (plural), the input names need to be indexed. You then also need to accept something like List<Book> instead of Book as the param to your action method. It's a little hard to tell with just the code provided, but I'd imagine you're repeating these inputs, all with the same names for each row. If that's the case, only the values for the last item will be posted. Making it a list of items enables you to post them all.
Simplistically, that means your inputs need to have names like [0].Amount, which Razor will generate for you if you use a for loop and render the inputs like:
<input asp-for="#Model[i].Amount" class="form-control" />
If you're adding the additional rows (and contained inputs) via JavaScript, you'll need to ensure that you're generating these indexed names properly yourself. A JS templating library may help in this regard.
Second, do not use Bind. Just don't. It's awful, horrible, and kills both puppies and kittens. For more explanation see my post, Bind is Evil. Use a view model instead. As a general rule you should never post an entity class. Your entity classes serve the database and its concerns, which are almost always different from the concerns of the view. Additionally, you should never just blindly save something posted by a user. Even if you insist on using your entity class to bind to, you can improve the security and safety of your code exponentially by literally mapping the values from the posted version of the class over to a new instance you create. Then, you know exactly what is being persisted to the database (without the godforsaken Bind) and you also have the opportunity to sanitize input as necessary.
I was facing a similar problem, but using ASP.NET Core 3.1 and Razor Pages. I was looking for a way to add and remove rows from a table with JavaScript, and then post it. My problem was to post the table without Ajax. Based in the question and in the accepted answer, I could do that.
Here is Index.cshtml.cs:
public class IndexViewModel {
public string Name { get; set; }
public IList<ResourceViewModel> Resources { get; set; }
}
public class ResourceViewModel {
public string Key { get; set; }
public string Value { get; set; }
}
public class IndexModel: PageModel {
[BindProperty]
public IndexViewModel ViewModel {get; set; }
public void OnGet() {
// You can fill your ViewModel property here.
}
public void OnPost() {
// You can read your posted ViewModel property here.
}
}
Here is Index.cshtml:
#page
#model IndexModel
#{
ViewData["Title"] = "Index";
}
<form method="post">
<div class="form-group">
<label asp-for="ViewModel.Name"></label>
<input asp-for="ViewModel.Name" class="form-control" />
</div>
<div class="form-group">
<table class="table">
<thead>
<th>Key</th>
<th>Value</th>
</thead>
<tbody>
#for(int i = 0; i < Model.ViewModel.Resources.Count; i++) {
<tr>
<td>
<input asp-for="ViewModel.Resources[i].Key" type="hidden" />
#Model.ViewModel.Resources[i].Key
</td>
<td>
<input asp-for="ViewModel.Resources[i].Value" type="hidden" />
#Model.ViewModel.Resources[i].Value
</td>
</tr>
}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary">Send</button>
</form>
Notice I've used type="hidden" because I didn't want the user to edit the table directly.
I hope you find this useful!

AmbiguousMatchException in my VIew Page

I am using ExcelReader to upload new sheet of containing data and get all old data from the DbSet.I am using Entity Framework POCO way to POST/GET data. I want to use same view for both POST/GET.I have two submit button one for saving current data and one for get all data from datacontext.Now my View Code is below I followed the steps included here!
I getting following error "System.Reflection.AmbiguousMatchException". I am guessing this because of HTNL.Beginfrom() creating it because of action result name.
Can any one help me how to resolve this issue if I need to use #Html.AntiForgeryToken();
#Html.ValidationSummary(); in my both the GET/POST call.
[MultipleButton(Name = "action", Argument = "Index")]
public ActionResult Index(HttpPostedFileBase uploadfile,dataViewModel dataView1)
{
// code for upload
}
[MultipleButton(Name = "action", Argument = "ExcelData")]
public ActionResult ExcelData()
{
ReadContext Context = new ReadContext();
return View(Context.dataext.ToList());
}
My View code:
#{
ViewBag.Title = "Read Excel File and Display in mvc5";
}
#model System.Data.DataTable
#using System.Data
<h2>Read Excel File and Display in mvc5</h2>
#using (Html.BeginForm("Index", "ReadExcel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken();
#Html.ValidationSummary();
<label class="text-info">Upload Excel File</label>
<input type="file" class="form-control" name="uploadfile" id="uploadfile" />
<input type="submit" value="submit" class="btn btn-default" name="action:Index" />
<input type="submit" value="data" class="btn btn-default" name="action:ExcelData" />
if (Model != null)
{
<table class="table table-responsive table-bordered">
<thead>
<tr>
#foreach(DataColumn column in Model.Columns)
{
<th>#column.ColumnName</th>
}
</tr>
</thead>
<tbody>
#foreach(DataRow row in Model.Rows)
{
<tr>
#foreach(DataColumn col in Model.Columns)
{
<td>#row[col.ColumnName]</td>
}
</tr>
}
</tbody>
</table>
}
}

Can only update the first record of a result set in MVC

I have taken over support of an MVC application, but unfortunately have pretty much zero experience or knowledge of MVC, so I apologise in advance if this is a stupid question.
We are placing every single result into a listed item in the HTML, then hiding all but one record, so you can filter through the different entries using First/Prev/Next/Last buttons, all via jQuery:
$("a#next").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":last-child")) {
return false;
}
nextli.removeClass("first").addClass("record").next().addClass("first").removeClass("record");
});
$("a#previous").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":first-child")) {
return false;
}
nextli.removeClass("first").addClass("record").prev().addClass("first").removeClass("record");
});
This is fine, and displays the records without any problem, however when you try to edit any record but the first, you get a "System.ArgumentNullException: Value cannot be null." error.
Here's the code in the controller for the edit function:
[HttpPost]
public ActionResult Edit(RecordsView rv)
{
if (ModelState.IsValid)
{
repository.EditRecord(rv.Records.FirstOrDefault().DogIncident);
}
return RedirectToAction("Index");
}
This is defined at the start of the cshtml file:
#using (Html.BeginForm("Edit", "Home", FormMethod.Post,new {#class="record-view"}))
And finally, here is how the HTML is generated on the view:
<li class="first" id="record-1805">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
<li class="record" id="record-1804">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
<li class="record" id="record-1803">
<form action="/Home/Edit" class="record-view" method="post">
<ul>
[form elements]
<li><input type="submit" style="margin: 18px 0;" value="Save"></li>
</ul>
</form>
</li>
Does anyone please know why I can only edit the first record that is displayed and not the others? Even if I go through the records using the next/back buttons and back to the first record to submit it, it updates fine, but updating any other record generates an error.
Referencing Darin's post: Pass a parameter to a controller using jquery ajax takes you halfway. He's using an input val in jQuery and passing it to the Controller Action.
Typically you'd provide one input link or #Html.ActionLink per record to be clicked for editing and MVC will bind to the controls parameter to your action (ID or what ever you want) just use the same name on the action link as the parameter and it will be mapped during the post.
data: { input: $('#caption').val() },
Change the signature of your controller action , or add another that takes an 'int' as shown below.
[HttpPost]
public ActionResult Edit(int ID = 0)
{
if (ModelState.IsValid)
{
repository.EditRecord(rv.Records.Where(r => r.ID == ID).DogIncident);
}
return RedirectToAction("Index");
}
Can you post the complete view so we can complete a solution ?
Was expecting the cshtml view. Here's an example:
#model IEnumerable<yourModelName>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.NID)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedOn)
</th>
<th>
#Html.DisplayNameFor(model => model.ExtensionAttribute15)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.yourElement1)
</td>
<td>
#Html.DisplayFor(modelItem => item.yourElement2)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
</td>
</tr>
}
</table>
#section Scripts{
<script type="text/javascript">
$("a#next").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":last-child")) {
return false;
}
nextli.removeClass("first").addClass("record").next().addClass("first").removeClass("record");
});
$("a#previous").on("click", function () {
var nextli = $("li.first");
if ($(nextli).is(":first-child")) {
return false;
}
nextli.removeClass("first").addClass("record").prev().addClass("first").removeClass("record");
});
</script>
}

Code still loads content on new page

Hi I have problem with my MVC application.
I would like click on ActionLink > load data from server > and insert data to div in same page.
But my code still loads content on new page and I don`t have idea why. Anybody to help me ?
This is my Index.cshtml in HomeController,
#model IEnumerable<Kango_kmen.KangoKmenWS.WSEntityInfo>
#Content.Script("jquery-1.9.1.min.js", Url)
#Content.Script("jquery.unobtrusive-ajax.min.js",Url)
#Content.Script("jquery.unobtrusive-ajax.js",Url)
#Content.Script("myScript.js",Url)
#section Entities{
<table>
<tr>
<th>
<input type="submit" value="zobrazit" />
</th>
<th>
iD
</th>
<th>
name
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Ajax.ActionLink("..", "CheckEntity", new { id = item.iD },
new AjaxOptions{ UpdateTargetId="properties",
HttpMethod="GET",
InsertionMode=InsertionMode.InsertAfter,
})
</td>
<td>
#Html.DisplayFor(modelItem => item.iD)
</td>
<td>
#Html.DisplayFor(modelItem => item.name)
</td>
</tr>
<tr>
<td>
<div id="properties">
</div>
</td>
</tr>
}
</table>
}
#section PropertyInfo{
<p>Property info</p>
}
#section Properties{
<p>properties</p>
}
And here is my Controller in HomeController
public PartialViewResult CheckEntity(int id)
{
var model = WSConnect.getEntityInfo(id);
if(model !=null){
return PartialView("CheckEntity", model.property);
}
var modell = new WSEntityInfo[0];
return PartialView("CheckEntity", modell);
}
and ChecktEntity is the Partial view but everytime I click on ActionLink controller load url: /Home/ChceckEntity/1000 for exmaple on view data on this new page "(
There are 2 things wrong with your code:
You have included the jquery.unobtrusive-ajax.js script twice, once the minified version and once the standard version. You should include it only once
The jquery.unobtrusive-ajax.js is not compatible with jquery 1.9.1 because one of the breaking changes in jQuery 1.9 is that the .live() method was removed and the jquery.unobtrusive-ajax.js script relies on it. If you look at your javascript console you would see the following error: The .live method is undefined.. So if you want to use the unobtrusive AJAX functionality in ASP.NET MVC you will have to downgrade the version of jQuery. For example the 1.8.3 version should be fine.
So:
#model IEnumerable<Kango_kmen.KangoKmenWS.WSEntityInfo>
#Content.Script("jquery-1.8.3.min.js", Url)
#Content.Script("jquery.unobtrusive-ajax.min.js", Url)
#Content.Script("myScript.js", Url)
Also please learn how to debug your javascript code. Use a tool such as FireBug or Chrome developer toolbar where all those errors will be shown.

ASP.NET MVC3 Partial View on HTTPPost

I Have a View and Partial View. The layout for the view is something like this:
<html>
...
<div id="MainView">#RenderBody()</div>
<!--Partial View-->
<div id="partialView">#Html.Action("PartialViewForm", "Main")</div>
...
</html>
My Partial View (named as _Register) is something like this:
#model PartialViewModel
<div id="form">
#using (Html.BeginForm("PartialViewForm", "Main", FormMethod.Post))
{
#Html.ValidationSummary(true)
<table >
<tbody>
<tr>
<td align="left">#Html.LabelFor(model => model.Name)*</td>
<td>#Html.EditorFor(model => model.Name)</td>
<td align="left">#Html.ValidationMessageFor(model => model.Name, "")</td>
</tr>
<tr>
<td align="left"><input type="submit" value="Go" class="submit2"/></td>
</tr>
</tbody>
</table>
}
</div>
In my MainController I have methods like this:
public class MainController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult PartialViewForm()
{
var partialViewModel= new PartialViewModel();
return PartialView("_Register", partialViewModel);
}
[HttpPost]
public ActionResult PartialViewForm(PartialViewModel partialViewModel )
{
// if Validation is not successfull
return PartialView("_Register", partialViewModel);
// else
....
}
}
This is what I want to do... when validation fails on the partial view I want to g back to the main view... however in my case on the post action when validation fails all I can see is the partialview... there is no main page content.
There are posts on the forum that show the same kind of behavior but I am not able to solve my issue. Can anyone please tell me how to fix it (it will be really helpful if you can modify my example and show it)
Thanks
I'm not sure if I totally understand what you are trying to do but if what I'm thinking is right, you should just use
[HttpPost]
public ActionResult PartialViewForm(PartialViewModel partialViewModel )
{
// if Validation is not successfull
model = _db.getBlah(); //get the original model for the main view
return View("MainView", model);
// else
....
}
However I think your issue might be that you really should have your form submission in your main view and not in your partial - the partial is just there to render the editors for your Create/Edit views, etc; the data should be submitted to the main view's action so that it can create/update the proper model.

Resources