I'm trying to post a list of models to the server, using ASP.NET's model binding and manipulating a bunch of values with JavaScript. When I send the values to the server, this is what I get:
model.inventory.processed_items[0].id: GA-6570
model.inventory.processed_items[0].event:
model.inventory.processed_items[0].subevent:
model.inventory.processed_items[0].restrict_marking:
model.inventory.processed_items[0].cecp_string:
model.inventory.processed_items[0].discrepancies:
model.inventory.processed_items.Index: 0
model.inventory.processed_items[1].id: GD-1000
model.inventory.processed_items[1].event:
model.inventory.processed_items[1].subevent:
model.inventory.processed_items[1].restrict_marking:
model.inventory.processed_items[1].cecp_string:
model.inventory.processed_items[1].discrepancies:
model.inventory.processed_items.Index: 1
These are my model classes that I'm binding to (I've omitted any fields that don't really matter to the question):
public class PackageViewModel
{
public InventoryViewModel inventory { get; set; }
}
public class InventoryViewModel
{
public List<ProcessedItemViewModel> processed_items { get; set; }
}
public class ProcessedItemViewModel
{
public string id { get; set; }
public int #event { get; set; }
public string subevent { get; set; }
public string cecp_string { get; set; }
public string restrict_marking { get; set; }
public string discrepancies { get; set; }
public string highest_classification { get; set; }
public int occurences_count { get; set; }
public IEnumerable<ProcessedOccurenceViewModel> occurences { get; set; }
}
public class ProcessedOccurenceViewModel
{
public string text { get; set; }
public string security_num { get; set; }
public Nullable<int> media_count { get; set; }
public string classification { get; set; }
}
This is my controller:
[HttpGet]
public ActionResult Create()
{
var inventoryVM = new InventoryViewModel
{
processed_items = new List<ProcessedItemViewModel>()
};
var packageVM = new PackageViewModel {
inventory = inventoryVM
};
return View(packageVM);
}
[HttpPost]
public ActionResult Create(PackageViewModel packageVM)
{
if (ModelState.IsValid)
{
...
}
}
When I check packageVM in debugger, the values are not bound to the view model. However, other values excluding this nested list of models are included in the packageVM model during the POST request. I don't understand why this portion is not binding because I have supplied indices and also passed in an empty list to the view.
The property names for the values you are sending do not match the model you are binding to. PackageViewModel does not contain a property named model (it contains one named inventory), so instead of
model.inventory.processed_items[0].id: GA-6570
it needs to be
inventory.processed_items[0].id: GA-6570
An easy way to think about this is to consider how you would access the value of a property of the model in the POST method
public ActionResult Create(PackageViewModel packageVM)
{
// get the id of the first item in processed_items
string id = packageVM.inventory.processed_items[0].id
Because the parameter in the method is named packageVM, just drop that prefix, (i.e. becomes inventory.processed_items[0].id), and that is what the name of the data needs to be in order to bind.
As a side note, it you are using the strong typed ***For() methods inside a for loop to generate your form controls based on your model, they will generate the correct name attributes, and you can just use $('form').serialize() to correctly generate the data to be sent via your ajax call.
Related
i have a model class of the following..
public class RegisterDTO
{
public string Name{ get; set; }
public string Location { get; set; }
public string PhoneNumber { get; set; }
public IFormFile Image { get; set; }
public List<string> Days { get; set; }
//here is the list of costs
public List<CostDTO> Costs { get; set; }
public IFormFile Image1 { get; set; }
public IFormFile Image2 { get; set; }
}
and here is the CostDTO class
public class CostDTO
{
public string itemName { get; set; }
public bool isPercent { get; set; }
public float value { get; set; }
}
and here is the service for registering the values stated above..
public async Task <string> Register(RegisterDTO model)
{
var entity = await _context.Tablename.FirstOrDefaultAsync();
List<Cost> costs = new();
//here is the loop that maps the DTO to an entity and stores it into the list
foreach (var item in model.costs)
{
var cost = _mapper.Map<Cost>(item);
costs.Add(cost);
}
//then save to the database
}
And here is the controller that calls the service.
[HttpPost("info")]
public async Task<ActionResult> Register([FromForm] RegisterDTO model)
{
var result = await _serviceName.Register(model);
return Ok(result);
}
My question is, at the beginning of the for loop, model.costs is empty and its not allowing me to pass a list of objects onto the service call. how do i go about fixing this.
you can fix it like this costs[0].itemName="item1",costs[1].itemName="item 2",
but it's not good solution, it's better to separate your dto ,get images in a model use [fromform] and another model [frombody]
As Hossein said, you should firstly make sure the request has sent the Costs list to the web api controller method.
The asp.net core model binding will auto map the formdata array with the list. So you should use it like below:
Then the result:
The Request URL is like below
?Page=1&Count=10&q[]=Name:[Abc, Xyz]&q[]=City:[XXX, YYY]&q[]=ID:[1,2]
I am using Asp.Net Core API and C#.
Controller code:
public async Task<ActionResult> GetUserListAsync([FromQuery]Data data)
{
----
----
}
Data Object class
public class Filters
{
public List<string> Name { get; set; }
public List<string> City { get; set; }
public List<int> ID { get; set; }
}
public class Data
{
public List<Filters> q { get; set; }
public int Page { get; set; }
public int Count { get; set; }
}
When I tried like this, I get the value for the fields Page and Count. But I not able to get the values for the field q.
How can I take the values for the field q from the request?
Otherwise, If I give like this as mentioned below means, how to take the values?
&query_hash[0][field]=Id&query_hash[0][Values][]=1&query_hash[1][field]=Date&query_hash[1][Values][from]=2020-03-11T18.30.00.000Z&query_hash[1][Values][from]=2020-03-12T18.30.00.000Z
Pass the array in Request url like below
?Page=1&Count=10&q[0].Name=Abc&q[0].Name=xyz&q[0].City=AAA&q[0].ID=1&q[0].ID=2&q[1].Name=rrr
Result:
Long winded out of necessity:
I have a strongly typed MVC 4 form. The form is built dynamically depending on the current URL parameters. When the user submits the form, the form input data is posted to a controller, the controller does it's thing, then the results from the controller method need to be displayed below the form.
Specifically in my case the results are line item ticket data. The user can hit submit as many times as they want and each time the additional results from the controller are displayed on the same page below the form; Each post is an added line item. (The line items are requested/stored by a call to an external webapi, so each time a submit happens I rebuild the line items from my controller method).
What I am trying to do is not have the page refresh every time to build the form all over again while simply updating the line items below the form through an ajax call. The form is expensive to build because it also is built from an external web api call. My model is very large and has a lot of data even though the form itself isn't.
What I have attempted so far is to put the line items into a partial view, which I will update after each form submit. I'm submitting the form with jQuery and Ajax. What seems to be happening is the form is submitted, the ajax method is called, but my controller is returning the partial view and master page layout directly. It's not returning back to the jQuery call. This could be Sitefinity or it could be something I'm doing completely wrong.
What I have so far is this: http://beta.kentuckycenter.org/all-shows/jeff-beck. Click on "Buy Tickets" on this page to see form.
Submit the form (Press "GO") then the form submits and only returns the partial view.
But it should look like this (this is design only):
http://www.kentuckycenter.org.staging01.spiiider.com/select-tickets.php
Sitefinity MVC widgets change the complexity for sure. My setup is Master page (Sitefinity templates are built from master pages), then default view, and partial view. I'm trying to update the partial view inside the default view.
I've tried many different ways including ajax.beginform form but I still can't seem to get a partial view to return inside a default view. I can't pass my original SelectTicketsModel data in hidden fields because some of the items are lists of other models.
Controller Actions simplified:
public ActionResult Index(string performanceID, string pageID)
{
var Model = new SelectTicketsModel();
Model = Model.GetPerformanceDetails(performanceIDnew, productionID);
return View("Default", Model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(GetTicketsModel Model)
{
//call webapi to reserve tickets
ticketsReserved = Model.ReserveTickets(Model.PriceType, performanceID, numberOfTickets, zoneID, Model.Accessible);
if (ticketsReserved == numberOfTickets)
{
LineItemModel lineModel = new LineItemModel();
//Fill LineItemModel from webpapi
}
return PartialView("TicketList", LineItemModel);
}
public class SelectTicketsViewModel
{
public bool Results { get; set; }
public int PerformanceID { get; set; }
public Nullable<DateTime> PreSaleDateStart { get; set; }
public Nullable<DateTime> PreSaleDateEnd { get; set; }
public bool HideSYOS { get; set; }
public bool IsGeneralAdmission { get; set; }
public bool WillCallOnly { get; set; }
public bool IsOnSale { get; set; }
public bool IsSoldOut { get; set; }
public int MaxSeats { get; set; }
public int ZoneMapID { get; set; }
public string TimeSlot { get; set; }
public string Title { get; set; }
public DateTime PerformanceDate { get; set; }
public string Location { get; set; }
// Create Lists from Models below
public List<PriceZoneModel> PriceZones { get; set; }
public List<PriceTypeModel> PriceTypes { get; set; }
public List<LineItemModel> LineItems { get; set; }
}
public class PriceZoneModel
{
public int PriceZoneID { get; set; }
public string PriceZoneDescription { get; set; }
public int PriceTypeID { get; set; }
public string PriceTypeDescription { get; set; }
public decimal Price { get; set; }
public decimal BasePrice { get; set; }
public bool Available { get; set; }
public int AvailableCount { get; set; }
public int Rank { get; set; }
}
public class PriceTypeModel
{
public int PriceTypeID { get; set; }
public string PriceTypeDescription { get; set; }
public string PriceTypeShortDescription { get; set; }
public string Category { get; set; }
public bool IsDefaultPriceType { get; set; }
public bool IsPromo { get; set; }
}
public class LineItemModel
{
int id { get; set; }
bool AccessibleSeats { get; set; }
public List<SubLineItemModel> SubLineItems { get; set; }
}
public class SubLineItemModel
{
public string Section { get; set; }
public int NumberOfSeats { get; set; }
public string SeatNumber { get; set; }
public int SeatID { get; set; }
public decimal Price { get; set; }
public string PriceTypeDescription { get; set; }
}
public class GetTicketsModel
{
//Form Values needed to Reserve Tickets
public string NumberOfTickets { get; set; }
public string PriceType { get; set; }
public string ZoneID { get; set; }
public bool Accessible { get; set; }
public string PerformanceID { get; set; }
}
If the returned ajax is opening up on a new page, it sounds like you are not using the unobtrusive library.
Make sure you have the following file included somewhere on your cshtml page or even in your layout file (if you are using ajax throughout your site).
jquery.unobtrusive-ajax.js
Where can one download Microsoft jQuery Unobtrusive Validation without using NuGet
From the sounds of it, your method of returning the data in a partial and updating the front end element should work fine.
Only thing I would look out for is that you should be passing the variable as the Model, not the class
if (ticketsReserved == numberOfTickets)
{
LineItemModel lineModel = new LineItemModel();
//Fill LineItemModel from webpapi
}
return PartialView("TicketList", lineModel ); //<-- here
EDIT
Yeah, I took a look at your beta link and the page does not contain the unobtrusive-ajax.js. That should fix your problem.
If you are getting the whole page as a response from the controller from an Ajax call it's a sitefinty thing. Try returning your list from your action result as Json(items, allow get ) instead of the partial view? Also kendo observable or kendo grid push the need data to the observable would be great solutions also.
i want to join my list with comma to separate each itemlist from a class but it just can't giving me access to that class property
(i'm sorry i'm not good at explaning this, but i hope with this code sample you will understand)
public class CategoryDetail
{
public string id { get; set; }
public string title { get; set; }
public string href { get; set; }
public string type { get; set; }
public string icon { get; set; }
}
public class RootObjectDetail
{
public List<CategoryDetail> categories { get; set; }
public string categoryList
{
get
{
return string.Join<CategoryDetail>(",", categories.ToArray());
}
}
}
so this is my code that i use for retrieving data from json, and what i want is that in my xaml i will bind it into categoryList and it will showing categoryDetail title property that separated by comma
You don't need to specify generic parameter type - it can be inferred from usage. Also you need to project categories to sequence of titles:
public string categoryList
{
get
{
return String.Join(",", categories.Select(c => c.title));
}
}
I have a form that is flexible. Meaning that depending on what selection you make in a dropdownlist the fields will be different. Also, the controller action that is called will also change. I am trying to get this to work with just a simple example, but I can't find how to submit data to the controller and have the controller map it correctly to a defined class.
Clarification: When a user creates a new question that has only one choice this is the form/controller that they are using. However, when they create a question with multiple choices I would like to use the same form/controller. The error i am getting is that the object is null. Which i think means that whenever the data is being passed to the controller, it is not being properly mapped into the object. How can i map the data explicitly into my defined object? Or should i do this whole thing differently?
Here is the controller:
[HttpPost]
public ActionResult CreateSimpleQuestion(SimpleQuestion question)
{
if (ModelState.IsValid)
{
question.question.is_counted = true;
question.question.DateCreated = DateTime.Now;
db.Questions.Add(question.question);
db.QuestionChoices.Add(question.choices[0]);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(question);
}
Here is the class:
[Serializable]
public class SimpleQuestion
{
public Question question { get; set; }
public QuestionChoices[] choices { get; set; }
}
Here is the script that is calling the controller action:
<script type="text/javascript">
$("form").on("submit", function (event) {
event.preventDefault();
var data = $('form').serialize();
console.log(data);
$.post('/Question/CreateSimpleQuestion/', data);
});
</script>
This is the serialized data:
QuestionTitle=faketitle&Keywords=fakekeywords&Description=fakedescription&Comments=fakecomments&QuestionType=Simple&DisplayText=fakequestiontext&OrderNumber=fakeorder
And in case you need the specifics of the model:
public class Question
{
public int QuestionId { get; set; }
public string QuestionTitle { get; set; }
public DateTime DateCreated { get; set; }
public string QuestionType { get; set; }
public string Keywords { get; set; }
public bool is_counted { get; set; }
public int? ParentId { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string Description { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string Comments { get; set; }
//These define a one to many relationship
public virtual ICollection<TeamQuestionRoster> TeamQuestionRosters { get; set; }
public virtual ICollection<Response> Responses { get; set; }
public virtual ICollection<QuestionChoices> QuestionChoices { get; set; }
}
public class QuestionChoices
{
public int QuestionChoicesId { get; set; }
public string DisplayText { get; set; }
public int OrderNumber { get; set; }
public bool is_correct { get; set; }
//These are the FK properties
public int QuestionId { get; set; }
//This defines the FK Relationships
public virtual Question Question { get; set; }
//These define a one to many relationship
public virtual ICollection<ResponseDetails> ResponsDetails { get; set; }
}
I think you might be having a issue with your media type. Try posting JSON like this:
$.post('/Question/CreateSimpleQuestion', data, function() { /* success callback */ }, 'application/json');
EDIT:
The $.post shorthand method might be expecting 'json' rather than 'application/json'. I typically use $.ajax instead.
TAKE 2:
Based the JSON you posted, I can see that your data is not being serialized properly. You're getting name/value pairs instead of actual JSON objects. Your JSON data should like this:
{
"QuestionId" : 0,
"QuestionTitle" : "My Title",
"Description": "My Description"
}
Here's another SO post explaining how to convert the jQuery serialize results to an appropriate JSON object: Convert form data to JavaScript object with jQuery
Hope that helps :)