MVC 3 - Entity Framework - Post data from ajax-invoked Partial View - asp.net-mvc-3

I have a problem with the binding of a complex model from a View that has updatable content, first things first, entites:
public partial class Diagnosis
{
public Diagnosis()
{
this.DiagZones = new HashSet<DiagZone>();
...
}
public int diagnosisid { get; set; }
public int playerid { get; set; }
public int userid { get; set; }
...
public virtual ICollection<DiagZone> DiagZones { get; set; }
}
My DiagZones Collection is the intermediate table between Diagnosis and Zones but it exist in my Model cause has more fields than the id's.
I have a Select control where you can select and unselect the Zones, when onchange fires, I Get a Partial View with an ajax call.
The code:
EditDiagnosis.cshtml
#model Gfire.Models.DiagnosisViewModel
<h2>
#ViewBag.playername</h2>
#using (Html.BeginForm("EditDiagnosis", "Diagnosis", FormMethod.Post))
{
#Html.HiddenFor(d => d.Diagnosis.diagnosisid)
<table>
...
</table>
<table>
<tr>
<td>
Zones:
</td>
<td>
#Html.ListBoxFor(d => d.SelectedZones, new SelectList(Model.Zones, "zoneid", "description"), new
{
style = "width:305px;",
onchange = "QualityMovement(this);",
id = "lbZone",
#class = "chzn-select"
})
</td>
<td>
...
</td>
</tr>
</table>
<div id="qualitymovement">
#Html.Partial("_QualityMovement", Model.Diagnosis.DiagZones)
</div>
<div>
<input type="submit" value="Save" />
&nbsp | &nbsp #Html.ActionLink("Cancel", "IndexDiagnosisPlayer", new { playerid = ViewBag.playerid })
</div>
}
Partial View (_QualityMovement.cshtml):
#model List<Gfire.Domain.Entities.Diagnosis.DiagZone>
#if (Model.Count() != 0)
{
<table>
#foreach (var dz in Model)
{
<tr>
<tr>
<td>
Flex:
</td>
<td>
#Html.TextBoxFor(d => dz.flex))
</td>
</tr>
</tr>
}
</table>
}
The Ajax.Get call:
<script type="text/javascript" language="javascript">
function JointBalance(item) {
...
$.ajax({
url: '#Url.Action("GetJointBalances", "Diagnosis")',
data: arrayToParamObject('zonesid', items),
contentType: "application/json; charset=utf-8",
success: function (data) {
// Successful requests get here
$("#jointbalance").html(data);
$("#jointbalance").fadeIn('slow');
},
type: "GET",
datatype: "json"
});
...
}
</script>
In server I have a Method that initialize a new list of DiagZones and update correctly the EditView.cshtml.
The problem comes when I try to Submit the complete Diagnosis object with all the fields and the list of DiagZones but my method:
[HttpPost]
public ActionResult EditDiagnosis(DiagnosisViewModel DiagnosisViewModel)
{
if (ModelState.IsValid)
{
// Save the model
...
return RedirectToAction("IndexDiagnosisPlayer", new { playerid = SessionHelper.Player.playerid });
}
else
{
return View("EditDiagnosis", new { diagnosisid = DiagnosisViewModel.diagnosisid });
}
}
My Model has empty the DiagnosisViewModel.DiagZones list.
I've tried to use EditorFor, pass the complete model to the partial View, add several forms... but it was useless, how can I bind that list to my model?
Thanks in advance.
UPDATE
Here is what the server side action is expecting:
[HttpGet]
public ActionResult GetJointBalances(int[] zonesid) { ... }
GET ajax request data looks like:
Request URL:http://localhost/Gfire.WebUI/Diagnosis/GetQualityMovement?zonesid=47
Request Method:GET
Status Code:200 OK
Request Headersview source
...
Connection:keep-alive
Content-Type:application/json; charset=utf-8
...
Referer:http://localhost/Gfire.WebUI/Diagnosis/EditDiagnosis?diagnosisid=0&playerid=23
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22
X-Requested-With:XMLHttpRequest
Query String Parametersview sourceview URL encoded
zonesid:47

I found a temporal solution, not the best of course, but I thougth it would help to understand what I was trying to do and how to solve this.
The problem, as Justin said, was binding the list<DiagZones> to main model, the Diagnosis object, following the posts of this:
ASP.Net MVC4 bind a "create view" to a model that contains List
I understand a little bit of the binding functionality, with that in mind I code my new PartialView _QualityMovement.cshtml:
#model List
#if (Model.Count() != 0)
{
<table>
#foreach (var dz in Model)
{
<tr>
<tr>
<td>
Flex:
</td>
<td>
Html.TextBox("Diagnosis.DiagZones[" + i + "].ext", Model[i].ext)
</td>
</tr>
</tr>
}
</table>
}
It's a bad solution but at least I had my Model binded in server side.
The problem is the abstraction made by the entity framework to my entities, as an ICollection so I cannot iterate as a List and found myself "casting" everywhere.
I suppose a better approach should be a CustomBinder to retrieve the data in the Request or type it in a neater and understandable way.
Thanks Justin for all the help you gave me.

Related

Net Core- add data to different model

I am pulling the extra materials on the order page in my project. Since the model on this page is product, I need to run a cart class to add to cart. But I don't know how to do this with asp-for. How can I add data for class cart because the model on the page is product?
#if (product.ProductExtras.Any())
{
<h5>Ekstra Malzemeler</h5>
<ul class="clearfix">
#foreach (var extra in product.ProductExtras)
{
<li>
<label class="container_check">
#extra.Extra.Name<span>+ #extra.Extra.Price.ToString("c2")</span>
<input type="checkbox">
<span class="checkmark"></span>
</label>
</li>
}
</ul>
}
Include your cart class and product class in a single class, and use this class in the view, you can access the data of both the cart class and the product class.
Like this:
public class ViewModel
{
//Choose the data type depending on your needs
public List<cart> cart { get; set; }
public List<product> product { get; set; }
}
For more details you can refer to this link.
Update:
I try to send a post request with the state of the checkbox, maybe that's what you mean.
Controller:
[HttpGet]
public IActionResult Test()
{
ViewModel viewmodel = new ViewModel();
viewmodel.product = new List<Product>();
viewmodel.product.Add(new Product() {Id =1,Name="Product1" });
viewmodel.product.Add(new Product() { Id = 2, Name = "Product2" });
viewmodel.product.Add(new Product() { Id = 3, Name = "Product3" });
return View(viewmodel);
}
[HttpPost]
public JsonResult Test([FromBody]ViewModel viewmodel)
{
//your own processing logic
return Json(viewmodel);
}
View:
#model _2022080202.Models.ViewModel
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.product[0].Id)
</th>
<th>
#Html.DisplayNameFor(model => model.product[0].Name)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.product) {
<tr>
<td name = "Id">#Html.DisplayFor(modelItem => item.Id)</td>
<td name = "Name">#Html.DisplayFor(modelItem => item.Name)</td>
<td><input type="checkbox" value="#item.Id" /></td>
</tr>
}
</tbody>
</table>
<script src="https://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
<script>
$('input[type=checkbox]').change(function() {
if ($(this).is(":checked")) {
var id = $(this).val(); //The value bound in the checkbox
var row = $(this).parent("td").parent("tr");
ProductId = row.find("[name='Id']").text();
ProductName = row.find("[name = 'Name']").text();
var data = { ProductId: parseInt(ProductId), ProductName: ProductName };
var cart = new Array();
cart.push(data);
var viewmodel = { cart: cart};
$.ajax({
type: "post",
url: '/Home/Test',
data: JSON.stringify(viewmodel),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(result) {
//your own processing logic
},
error: function(error) { }
});
}
});
</script>
I send all the data out, you can just send an ID if you want and then do the following in the controller.
Test Result:

How can I make a Like/Dislike button in an Asp .net core web site like a blog?

I am new to web development with .net core mvc and am building a web application that works much like Wordpress.
Basically I am building a site in which articles will be posted by an administrator and the user will come to read these articles there.
I must now make a "like" button and a "dislike" button to allow the reader to give his opinion. The process is very simple for the moment, I created an Article table in my database and I added two columns to it: NumberOfLikes and NumberOfDislikes.
Then I added the two buttons on the article page. What I want now is like I said, to make sure that when we click on the like button, it adds +1 to NumberOfLikes and conversely when we click on the dislike button, it adds +1 a NumbreOfDislikes.
I can't seem to figure out how to do this feature that's why I'm turning to you for help.
Thanks in advance.
Edit
this is the Article Model class class:
public class Article{
public long Id{ get; set; }
public long NumberOfLikes{ get; set; }
public long NumberOfDisLikes{ get; set; }
}
This is the Index action in ArticleController.cs
public async Task<IActionResult> Index(long id)
{
Article article = await _context.Articles
.Where(a => a.Id == id)
.FirstOrDefaultAsync();
if (article == null)
{
return NotFound();
}
ArticleViewModel model = new ArticleViewModel();
model.Article = article;
await GetDataForLayout(_context);
await _context.SaveChangesAsync();
return View(model);
}
and in the /Article/Index.cshtml View we have two buttons like this:
<div>
<button class="btn btn-success like-article">
<i class="fas fa-thumbs-up"></i>
<span>Like #article.NumberOfLikes</span>
</button>
<button class="btn btn-danger dislike-article">
<i class="fas fa-thumbs-down"></i>
<span>DisLike #article.NumberOfDisLikes</span>
</button>
</div>
I don't know if putting #article.NumberOfLikes can allow me to see the updated values but it's the values that I want there.
So I Want to click on these buttons and update values of NumberOfLikes/NumberOfDislikes of the article without changing the page or refresh it and make the changes visible by the reader.
You can use to replace buttons,try to pass id and like or dislike to action,then update the article.Here is a demo with efcore:
Article:
public class Article
{
public int Id { get; set; }
public int NumberOfLikes { get; set; }
public int NumberOfDislikes { get; set; }
}
Actions:
public async Task<IActionResult> Index()
{
return View(_context.Article.ToList());
}
public async Task<List<Article>> Edit(int id, bool like)
{
var article = await _context.Article.FindAsync(id);
if (like)
{
article.NumberOfLikes++;
}
else {
article.NumberOfDislikes++;
}
_context.Update(article);
await _context.SaveChangesAsync();
return _context.Article.ToList();
}
Index.cshtml:
#model IEnumerable<WebApplication_efcore.Models.Article>
<<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Id)
</th>
<th>
#Html.DisplayNameFor(model => model.NumberOfLikes)
</th>
<th>
#Html.DisplayNameFor(model => model.NumberOfDislikes)
</th>
<th></th>
</tr>
</thead>
<tbody id="tbody1">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.NumberOfLikes)
</td>
<td>
#Html.DisplayFor(modelItem => item.NumberOfDislikes)
</td>
<td>
<button onclick="Edit(#item.Id,true)">Like</button>
<button onclick="Edit(#item.Id,false)">Dislike</button>
</td>
</tr>
}
</tbody>
</table>
<script>
function Edit(id,like)
{
$.ajax({
type: "POST",
data: { id: id, like: like },
url: 'Edit',
}).done(function (result) {
var html=""
for (var i = 0; i < result.length; i++)
{
html += '<tr><td>' + result[i].id + '</td><td>' + result[i].numberOfLikes + '</td><td>' + result[i].numberOfDislikes + '</td><td><button onclick="Edit(' + result[i].id + ',true)">Like</button><button onclick="Edit(' + result[i].id +',false)">Dislike</button></td></tr>'
}
document.getElementById("tbody1").innerHTML = html;
});
}
</script>
result:

popup a dialog and submit partial view in asp.net mvc

So what I am trying to do here is that in the View,
when I click on the button, I want to pass some values generated from the foreach loop (in my case Country and City) to the javascript function.
Inside that javascript function, I want to open up a dialog(partial view) by passing those values (countryName, cityName) to the controller.
Hence in my controller, it will pass those values to the partial view which will be appeared as a dialog and can submit the form.
I've tried with the version without the pop-up dialog (it worked), but having a hard time doing it with a dialog. #3 works btw, but I think I am having a trouble with #1 and #2. Any help would be appreciated. Thanks.
View:
#model IEnumerable<test.Models.Employee>
<table class="table">
<tr>
<th>Country</th>
<th>City</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Country)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
<button onclick="OpenDialog(item.Country, item.City)">
Open Dialog
</button>
</td>
</tr>
}
</table>
<div id="dialog"></div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/jquery-ui-1.12.0.js"></script>
<script>
function OpenDialog(countryName, cityName) {
$('#dialog').dialog({
autoOpen: true,
width: 400,
resizable: false,
title: 'My Table',
modal: true,
open: function(event, ui) {
$(this).load('#Url.Action("getEmployee", "Employee", new
{
country = countryName,
city = cityName
})');
},
buttons: {
"Close": function () {
$(this).dialog("close");
}
}
});
}
</script>
}
Controller:
public ActionResult getEmployee(string country, string city)
{
var viewModel = new EmployeeViewModel()
{
Country = country,
City = city
};
return PartialView("EmployeeDialog", viewModel);
}
Partial View:
#model test.ViewModels.EmployeeViewModel
#using (Html.BeginForm("PostEmployee", "Employee", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Country)
#Html.HiddenFor(model => model.City)
#Html.EditorFor(model => model.Comments)
<input type="submit" value="Submit"/>
}
#Url.Action() is razor code and is parsed in the server before its sent to the view. countryName and cityName are javascript variables and do not even exist at that point (they are not in scope). Change the code in the OpenDialog() function to
var url = '#Url.Action("getEmployee", "Employee")';
var data = { country: countryName, city: cityName };
$(this).load(url, data);
As a side note, there is really no need to be calling the server each time to return values that you already have in the view. You could just render the form in the dialog initially (for a default EmployeeViewModel and then in the OpenDialog just set the values of the inputs for properties Country and City, for example $('#Country').val(countryName);

How to use Simple Ajax Beginform in Asp.net MVC 4? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am new in Asp.net MVC and i researched about Ajax.BeginForm but when i apply codes it did not work. Can you share very simple example with Ajax.Beginform with View, Controller, Model?
Thanks.
Simple example: Form with textbox and Search button.
If you write "name" into the textbox and submit form, it will brings you patients with "name" in table.
View:
#using (Ajax.BeginForm("GetPatients", "Patient", new AjaxOptions {//GetPatients is name of method in PatientController
InsertionMode = InsertionMode.Replace, //target element(#patientList) will be replaced
UpdateTargetId = "patientList",
LoadingElementId = "loader" // div with .gif loader - that is shown when data are loading
}))
{
string patient_Name = "";
#Html.EditorFor(x=>patient_Name) //text box with name and id, that it will pass to controller
<input type="submit" value="Search" />
}
#* ... *#
<div id="loader" class=" aletr" style="display:none">
Loading...<img src="~/Images/ajax-loader.gif" />
</div>
#Html.Partial("_patientList") #* this is view with patient table. Same view you will return from controller *#
_patientList.cshtml:
#model IEnumerable<YourApp.Models.Patient>
<table id="patientList" >
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Number)
</th>
</tr>
#foreach (var patient in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => patient.Name)
</td>
<td>
#Html.DisplayFor(modelItem => patient.Number)
</td>
</tr>
}
</table>
Patient.cs
public class Patient
{
public string Name { get; set; }
public int Number{ get; set; }
}
PatientController.cs
public PartialViewResult GetPatients(string patient_Name="")
{
var patients = yourDBcontext.Patients.Where(x=>x.Name.Contains(patient_Name))
return PartialView("_patientList", patients);
}
And also as TSmith said in comments, don´t forget to install jQuery Unobtrusive Ajax library through NuGet.
All This Work :)
Model
public partial class ClientMessage
{
public int IdCon { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Controller
public class TestAjaxBeginFormController : Controller{
projectNameEntities db = new projectNameEntities();
public ActionResult Index(){
return View();
}
[HttpPost]
public ActionResult GetClientMessages(ClientMessage Vm) {
var model = db.ClientMessages.Where(x => x.Name.Contains(Vm.Name));
return PartialView("_PartialView", model);
}
}
View index.cshtml
#model projectName.Models.ClientMessage
#{
Layout = null;
}
<script src="~/Scripts/jquery-1.9.1.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
//\\\\\\\ JS retrun message SucccessPost or FailPost
function SuccessMessage() {
alert("Succcess Post");
}
function FailMessage() {
alert("Fail Post");
}
</script>
<h1>Page Index</h1>
#using (Ajax.BeginForm("GetClientMessages", "TestAjaxBeginForm", null , new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "SuccessMessage",
OnFailure = "FailMessage" ,
UpdateTargetId = "resultTarget"
}, new { id = "MyNewNameId" })) // set new Id name for Form
{
#Html.AntiForgeryToken()
#Html.EditorFor(x => x.Name)
<input type="submit" value="Search" />
}
<div id="resultTarget"> </div>
View _PartialView.cshtml
#model IEnumerable<projectName.Models.ClientMessage >
<table>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.IdCon)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.Email)</td>
</tr>
}
</table>
Besides the previous post instructions, I had to install the package Microsoft.jQuery.Unobtrusive.Ajax and add to the view the following line
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>

How to pass a list of objects instead of one object to a POST action method

I have the following GET and POST action methods:-
public ActionResult Create(int visitid)
{
VisitLabResult vlr = new VisitLabResult();
vlr.DateTaken = DateTime.Now;
ViewBag.LabTestID = new SelectList(repository.FindAllLabTest(), "LabTestID", "Description");
return View();
}
//
// POST: /VisitLabResult/Create
[HttpPost]
public ActionResult Create(VisitLabResult visitlabresult, int visitid)
{
try
{
if (ModelState.IsValid)
{
visitlabresult.VisitID = visitid;
repository.AddVisitLabResult(visitlabresult);
repository.Save();
return RedirectToAction("Edit", "Visit", new { id = visitid });
}
}
catch (DbUpdateException) {
ModelState.AddModelError(string.Empty, "The Same test Type might have been already created,, go back to the Visit page to see the avilalbe Lab Tests");
}
ViewBag.LabTestID = new SelectList(repository.FindAllLabTest(), "LabTestID", "Description", visitlabresult.LabTestID);
return View(visitlabresult);
}
Currently the view display the associated fields to create only one object,, but how i can define list of objects instead of one object to be able to quickly add for example 10 objects at the same “Create” request.
My Create view look like:-
#model Medical.Models.VisitLabResult
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#section scripts{
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>VisitLabResult</legend>
<div class="editor-label">
#Html.LabelFor(model => model.LabTestID, "LabTest")
</div>
<div class="editor-field">
#Html.DropDownList("LabTestID", String.Empty)
Your viewModel
public class LabResult
{
public int ResultId { get; set; }
public string Name { get; set; }
//rest of the properties
}
Your controller
public class LabController : Controller
{
//
// GET: /Lab/ns
public ActionResult Index()
{
var lst = new List<LabResult>();
lst.Add(new LabResult() { Name = "Pravin", ResultId = 1 });
lst.Add(new LabResult() { Name = "Pradeep", ResultId = 2 });
return View(lst);
}
[HttpPost]
public ActionResult EditAll(ICollection<LabResult> results)
{
//savr results here
return RedirectToAction("Index");
}
}
Your view
#model IList<MvcApplication2.Models.LabResult>
#using (Html.BeginForm("EditAll", "Lab", FormMethod.Post))
{
<table>
<tr>
<th>
ResultId
</th>
<th>
Name
</th>
</tr>
#for (int item = 0; item < Model.Count(); item++)
{
<tr>
<td>
#Html.TextBoxFor(modelItem => Model[item].ResultId)
</td>
<td>
#Html.TextBoxFor(modelItem => Model[item].Name)
</td>
</tr>
}
</table>
<input type="submit" value="Edit All" />
}
Your view will be rendered as follows, this array based naming convention makes it possible for Defaultbinder to convert it into ICollection as a first parameter of action EditAll
<tr>
<td>
<input name="[0].ResultId" type="text" value="1" />
</td>
<td>
<input name="[0].Name" type="text" value="Pravin" />
</td>
</tr>
<tr>
<td>
<input name="[1].ResultId" type="text" value="2" />
</td>
<td>
<input name="[1].Name" type="text" value="Pradeep" />
</td>
</tr>
If I understand your question correctly,
you want to change your view to be a list of your model object #model List, then using a loop or however you wish to do it, create however many editors you need to for each object
then in your controller your receiving parameter of create will be a list of your model instead too.

Resources