In a Razor page I want to change the data of an entity. The data is loaded in OnGet and saved in OnPost. The data that is loaded in OnGet is saved to a property called Person. At a later time I can simply retrieve them in OnPost (it's the identical object).
However, if I use a handler called by an Ajax call, the property object is initialized (integer properties are 0, object properties are zero) but it is not the original object anymore.
What do I have to do so that the original object is also available in the handler called by the Ajax call?
I already tried to use [BindProperty] attribute and to use a hidden input in the razor page. Or to access ViewData.Model But is does not work. Person and other data of the model is still kind of null.
Ajax-Call:
function addEntitlement() {
var vacationEntitlement = {};
vacationEntitlement["Year"] = $('#newEntitlementYear').val();
vacationEntitlement["Days"] = $('#newEntitlementDays').val();
vacationEntitlement["PersonID"] = $('#hiddenPersonID').val();
$.ajax({
type: "POST",
url: "./Edit?handler=AddEntitlement",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(vacationEntitlement)
}).fail(function () {
alert('error');
}).done(function () {
});
}
PageModel:
public IActionResult OnGet(int? id)
{
if (id == null)
{
return NotFound();
}
Person = _unitOfWork.PersonRepository.GetByID(id);
if (Person == null)
{
return NotFound();
}
return Page();
}
public JsonResult OnPostAddEntitlement([FromBody] VacationEntitlement vacationEntitlement)
{
///Tried to acces Person or ViewData.Model.Person here.
///But Person is just intialized but does not contain the expected data.
}
Try using TempData it allows you to pass data from one action to another action
Person = _unitOfWork.PersonRepository.GetByID(id);
TempData["Person"] = Person
then
public JsonResult OnPostAddEntitlement([FromBody] VacationEntitlement vacationEntitlement)
{
if(TempData.ContainsKey("Person")) {
var person = TempData["Person"] as Person; /* (as Person} I just assume the class name is Person */
// place your logic here
}
}
and also make sure to properly setup your TempData configuration
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.2#tempdata
Related
I'm trying to figure out how to simply pass a JSON object into a controller method param. I'll often use JSON.serialize() when posting forms and the serialization to C# is automatic. However, I'm struggling to get this to work using GET while trying to load a partial.
I built a small sample project as a basic simulation of what I need, which is a controller method that accepts an int param and a separate model class, which I'd like to pass in as JSON. The partial html response is loaded into a div.
The view:
<div class="row">
Loading a partial via Ajax, passing a JSON object as a param!
</div>
<div class="row">
<div id="peoplePartial"></div>
</div>
#section Scripts {
<script>
$(function() {
var id = 1;
var person = {
Name: "Homer Simpson",
Age: 45
};
load(id, person);
});
function load(id, person) {
$.ajax({
cache: false,
type: "GET",
dataType: "html",
url: '/Home/PeoplePartial',
data: {
id: id,
person: JSON.stringify(person)
}
}).done(function(data) {
$("#peoplePartial").html(data);
}).fail(function(data) {
console.log(data.responseText);
});
}
</script>
}
The partial:
#model MVCLearning.Models.PeoplePartialModel
<div>
<span>Person found! Name is #Model.Person.Name</span>
</div>
The partial ViewModel:
public class PeoplePartialModel {
public int Id { get; set; }
public PersonModel Person { get; set; }
}
The data model/entity I'm trying to serialize:
public class PersonModel {
public string Name { get; set; }
public int Age { get; set; }
}
And finally, the controller:
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
public ActionResult PeoplePartial(int id, PersonModel person) {
if (person != null) {
var model = new PeoplePartialModel {
Id = id,
Person = person
};
return PartialView("_People", model);
} else {
throw new System.Exception("An Error Has occoured");
}
}
}
The "id" param always works, of course, but the PeopleModel param is always null when using JSON.stringify(). I would have expected it to serialize to the model and contain the JSON values I passed in from the JS in the view. I can fix this by not using JSON.stringify() but then the values are always ignored e.g. always passed in as Name: null, Age: 0. I just can't seem to pass values like this.
I've tried changing the ajax call to POST but it changes nothing. I've tried setting the contentType to html and json, neither change anything.
Thanks in advance.
I got it. Here's the magic combination of properties:
function load(id, person) {
$.ajax({
type: "POST",
dataType: "html",
url: "/Home/PeoplePartial",
data: {
id: id,
person: person
}
}).done(function(data) {
$("#peoplePartial").html(data);
}).fail(function(data) {
console.log(data.responseText);
});
}
I think one of the variations I had tried was POST with the contentType set. That throws an error saying both controller method params are null, strangely. I also could have omitted dataType on this and it works. I could not get any variation of GET to work, even though GET is what I'm trying to do. I'd really like to know why this is the case.
I'm trying to get some data (multidimensional array) to my GET controller (for display in a modal/dialog box) from a list (the user checks some values and then gets sent to a modal/dialog box that should display the chosen values):
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
var prop= [];
prop['Name'] = "TEST";
prop['Id'] = "123"
data.push(prop);
}
});
When I log this (above) data, it looks fine. Then I use it the ajax call:
$.ajax({
type: "GET",
url: url,
data: JSON.stringify({ data }),
contentType: "application/json; charset=utf-8",
success: function () {
alert("OK");
}
});
I've a Model for using the data in the action (and the partial view):
public class MyClass
{
public string Name { get; set; }
public string Id { get; set; }
}
This is my action:
public ActionResult Merge(MyClass[] theData)
{
...
}
But in the action the 'theData' is always null. If I use 'POST' in the ajax, the POST action gets called, and I don't want to do that in this step. I want to use the POST action after, when the user have made some modifications (eg. Title change) and then saves. THEN I do a POST and saves the new data.
Please try this
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
var prop= {};
prop.Name = "TEST";
prop.Id = "123"
data.push(prop);
}
});
The parameter in your Merge() method is MyClass[] theData which means you need to send an array of objects, not an array of arrays. Start by changing the script to generate the data to
var data = [];
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
data.push({ Name: 'TEST', Id: '123' });
}
});
Next, you need to change the method to a [HttpPost] (rename it if necessary to avoid any conflict with any existing [HttpPost]public ActionResult Merge( .. ) method).
Then you need to change the type to "Post" and stringify the data with the name of the methods parameter before sending it
$.ajax({
type: 'Post', // change this
url: url,
data: JSON.stringify({ theData: data }), // change this
contentType: 'application/json; charset=utf-8',
success: function () {
alert("OK");
}
});
Side note: If you did want to do this to a [HttpGet] method, then it would be be necessary to send the data as .../Merge?[0].Name=TEST&[0].Id=123&[1].Name=TEST&[1].Id=123 etc (i.e. with indexers), however you should not generally be sending collections of objects to a GET method. Apart from the ugly query string, you could exceed the query string limit and throw an exception.
You're sending your data as a string. Don't use JSON.stringify
Change
data: JSON.stringify({ data })
to
data: data
I am still new to MVC and working my way around it. I need to get the "name" column of my district table into a dropdownlist to be able to pick from different Districts. The end game is that the user will pick a District from the dropdownlist and then be directed to a page where a list of schools(in a different table) will be shown with the selected district (i think that would be a query on the database using the value given from the dropdownlist). Basically what I have done so far is:
Create an MVC Application.
Create a Entity Framework Model.
Create an empty controller.
Create a view model(since every tutorial/ site answer has said to do
so)
Create a view.
I replicate step by step what these tutorials are telling me to do, but I get a different result. My dropdownlist gives me this outcome:
I need help sorting out what could be going wrong and why the data is not showing up in my dropdownlist.
Try I believe you are using the wrong SelectList constructor. Assuming you want the value of the drop down list to be the "leaID" property
#Html.DropDownList("myList", new SelectList(ViewBag.districts, "leaId", "name")
I would however, approach it another way which will keep it mostly strongly typed:
public class DistrictViewModel
{
public string SelectedDistrictId { get; set; }
public IEnumerable<SelectListItem> Districts { get; set; }
}
Action:
public ActionResult Index()
{
var viewModel = new DistrictViewModel()
{
Districts = new SelectList(db.Districts.ToList(), "leaID", "name")
}
return View(viewModel);
}
cshtml:
#model DistrictViewModel
#Html.DropDownListFor(m => m.SelectedDistrictId, Model.Districts)
Here is my answer to your comment using ajax
//Model
public class DistrictViewModel
{
public string name {get;set;}
public SelectList District {get;set;}
public int SelectedDistrict {get;set}
}
//Controller
public class DistrictController : Controller
{
KUDEREntities db = new KUDEREntities();
public ActionResult Index()
{
var model = new DistrictViewModel();
model.Districts = db.Districts.ToList();
model.SelectedDistrict=0;
return view(model);
}
[HttpPost]
public ActionResult Search(int id)
{
//do the search with the id of the selected district
var data = db.Districts.Where(m=>m.leaId = id).FirstorDefault();//this would return the whole object.
return Json(data, JsonRequestBehavior.AllowGet);
}
}
//Index View
#Model DistrictViewModel
<div>
//with this your selector would be "#SelectedDistrict"
#Html.DropDownListFor(model=>model.SelectedDistrict, new SelectList(Model.Districts,"leaId","name",Model.SelectedDistrict), new {#class=""})
//with this your selector would be "#myList"
//#Html.DropDownList("myList", new SelectList(ViewBag.districts, "leaId", "name", Model.SelectedDistrict)
</div>
<script>
$(document).ready(function(){
//when you want to search for the id selected, you just need to call the Search function below
function Search() {
//... now you have the value of the selecteditem
var parameters = { id: $("#SelectedDistrict").val() };
$.ajax({
type: 'post',
url: '#Url.Action("Search", "District")',
cache: false,
contentType: "application/json; charset=utf-8",
dataType: "html",
async: true,
data: JSON.stringify(parameters),
success: function (data) {
//...do whatever with the data
},
failure: function (msg) {
//... show a message of error
}
});
}
});
</script>
I have the below code
<script type="text/javascript">
$(function () {
$('#cboDeptID').change(function () {
var selectedDepartmentId = $(this).val();
var depmodel = {
Employees:'#Model.Employees',
Departments:'#Model.Departments',
DepartmentId:'#Model.DepartmentId',
DepartmentName:'#Model.DepartmentName',
SelectedDepartmentId:'#Model.SelectedDepartmentId'
};
$.ajax({
url: '#Url.Action("GetEmployees","DepartmentController")',
contentType: 'application/json; charset=utf-8',
type: 'POST',
dataType: 'json',
data: JSON.stringify({ model: depmodel })
})
.success(function (result) {
alert('Success!');})
.error(function (xhr, status){ alert('The provider could not be updated.');});
});
});
</script>
on this combo change i want to pass model to the controller. The model contains above members such as employees,departments,departmentid etc. The controller action will be fired on combo change but model member values are null. Am I doing it wrong? pls help.
Controller method is as shown below
public ActionResult GetEmployees(DepartmentModel model)
{
// code goes here
return View ("DepartmentView",model);
}
You need to change your JSON.stringify line to:
data: JSON.stringify(depmodel)
The automatic model binding when you post this data to the controller is getting confused because you're posting your array of key value pairs (employees, departments etc) inside another array when you call JSON.stringify. So what it's receiving is something like:
{ model: { Employees: <value>, ... } }
But what it wants is only the key value pairs of the properties inside the model to which it is trying to bind
Recently we updated our application from MVC3 to MVC4. In MVC4 we have discovered that having property names like Studio and StudioExecutive in our ViewModel will cause problems when posting. In the controller method we will always get Studio = null when StudioExecutive is being populated.
Here is an example of our issue and hope there is an answer for this problem.
Data Classes:
public class TestContact
{
public List<TestContactItem> Studio { get; set; }
public List<TestContactItem> StudioExecutive { get; set; }
public TestContact()
{
Studio = new List<TestContactItem>();
StudioExecutive = new List<TestContactItem>();
}
}
public class TestContactItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Controller Methods:
public ActionResult TestContactView()
{
var vm = new TestContact();
vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
return View(vm);
}
[HttpPost]
public ActionResult SaveTestContact(TestContact model)
{
return Content("success");
}
View / JavaScript with Ajax POST:
#using System.Web.Script.Serialization
#model TestContact
<button type="button" onclick="SaveTestContact();">Click Here to Post</button>
<script type="text/javascript">
$(document).ready(function() {
globalTestModel = #Html.Raw(new JavaScriptSerializer().Serialize(Model));
});
function SaveTestContact() {
// passing additional studio & studio executive parameter to controller because it was not mapping correctly to the server side viewmodel without it
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(globalTestModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
}
</script>
In this example we are populating our object with at least one Studio and one StudioExecutive and render the view. When the button in the view is clicked, we POST the same object to the controller method but the ViewModel is not binding correctly the Studio property is set to null.
*Unfortunately I'm not able to post images I have a screenshot of the object showing that the Studio count was 0 and the StudioExecutive count was 1
We did put a breakpoint before the POST to make sure the serialization on the JavaScript was correct and the object was populated.
We have concluded that this has to do with the naming convention of the 2 properties one being a substring of the other. Any one who has encounter the same problem and can point us on the right direction.
I know this is not the "complete answer" to the question but I feel that this will give additional insight to the issue, that (I hope) will eventually lead to the "complete solution".
Say we have a single item in each of Studio and StudioExecutive fields as shown in the example of the OP, and do the following in js:
globalTestModel = '#Html.Raw(new JavaScriptSerializer().Serialize(Model))';
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(globalTestModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
The controller method will only receive StudioExecutive as already mentioned. Now if we do the same and build the object in js as shown below, we get the same result.
o = {
Studio: [{
Id: 1,
Name: 'Studio Contact ID=1',
}],
StudioExecutive: [{
Id: 2,
Name: 'Studio Exec Contact ID=2',
}]
};
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(o),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
Now here comes the interesting part. If we add another item to the Studio field and post the same way we will get the same result.
// Do this in the controller and do the same ajax post
public ActionResult TestContactView()
{
var vm = new TestContact();
vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
vm.Studio.Add(new TestContactItem(){Id=3, Name = "Studio Contact ID=3"});
vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
return View(vm);
}
But if we will build the object in js as shown below the controller will receive both the Studio, with 2 items, and the StudioExecutive, with one item.
o = {
Studio: [{
Id: 1,
Name: 'Studio Contact ID=1',
},
{
Id: 2,
Name: 'Studio Contact ID=2',
}],
StudioExecutive: [{
Id: 2,
Name: 'Studio Exec Contact ID=2',
}]
};
// then do the ajax post
So what have we learned
This tells us two things:
There is something wrong with the default model binder in that it cannot bind two, or more, fields that starts with the same name
JSON.stringify can also NOT do it if there is only one item, but it can do it if there are more than one item in the field that has the shortest name (e.g. Studio)