Losing complete Model when posting Ajax values to controller - c#

What i want to do is have 2 listboxes (left, right) where left would have all products entities except the ones that are present in my Contract Entity and in the right the products in my Contract Entity.
I am having this issue that when i pass 1 parameter (string) I can receive them correctly in my controller but whenever i pass my model with it as a second parameter i loose my model completely. This is what i have:
Controller:
public ActionResult EditContract(ContractViewModel model, string selectedProducts)
View (Javascript/JQuery):
function GetSelectedProducts() {
var listbox = document.getElementById("productsForContractListbox");
var txt = "";
var i;
for (i = 0; i < listbox.length; i++) {
txt = txt + "\n" + listbox.options[i].text;
}
$('#SelectedProductForContracts').val(txt);
var selectedProducts = $('#SelectedProductForContracts').val();
var model = $('form').serialize;
$.post('#Url.Action("EditContract", "Contract")', { "model": model, "selectedProducts": selectedProducts});
}
Html helper listboxes:
// listbox for my contract products
#Html.ListBoxFor(c => c.Contract.Products, productsForContract, new { ID = "productsForContractListbox", #class = "form-control" })
// listbox where all products except the ones in my contract are loaded
#Html.ListBox("allProducts", allProducts, new { ID = "allProductsListbox", #class = "form-control" })
HiddenFor for the SelectedProductForContracts from my Model:
#Html.HiddenFor(c => c.SelectedProductForContracts, new { ID = "SelectedProductForContracts", name = "SelectedProductForContracts" })
Model:
[HiddenInput(DisplayValue = false)]
public List<SelectListItem> SelectedProductForContracts { get; set; }
When having the post data containing only the selectedProducts, i get my values but lose my model, when i add my model i get my model but lose my selectedProducts values.
I tried several things like THIS but couldn't get them to work in my case (i am doing something wrong probably but don't know what ...)
Can anyone help me towards the proper way of achieving this cause i see many ways but i hope there must be an elegant way of binding everything to 1 model without having to use javascript/Jquery ?
Kind regards!

List<SelectListItem> is not able to be defined in a single hidden variable

Related

Getting the selected value from a DropDownListFor using a ViewBag list

I'm having trouble getting the data from a DropDownListFor using a ViewBag list with my model. Here is my Controller code:
[HttpGet]
public ActionResult JoinTeam()
{
var TeamList = _db.TeamModels.ToList();
SelectList list = new SelectList(TeamList, "Id", "TeamName");
ViewBag.TeamList = list;
return View();
}
And the Razor view form looks like this:
#using (Html.BeginForm("JoinTeam", "Home", FormMethod.Post))
{
#Html.TextBoxFor(m => m.DisplayName, new { #class = "form-control form-control-lg", placeholder = "Enter your Battle Net ID" })
<br/>
#Html.DropDownListFor(m => m.TeamModel, (SelectList)ViewBag.TeamList, "- Select a Team to Join -", new { #class= "form-control form-control-lg" })
<br />
<button type="submit" class="btn btn-primary" style="width:100%;text-align:center;">Submit</button>
}
The TextBoxFor helper is returning the data correctly, but whatever option I have selected in the drop down does not get passed into my post method. Does anyone have any ideas?
The post action does work as it's getting the data from the model for the TextBoxFor help, but here's what it looks like:
[HttpPost]
public async Task<ActionResult> JoinTeam(GuardianModel model)
{
try
{
string BNETId = model.DisplayName.Replace("#", "%23");
long memberId = 0;
if (ModelState.IsValid)
{
Bungie.Responses.SearchPlayersResponse member = await service.SearchPlayers(MembershipType.Blizzard, BNETId);
memberId = member[0].MembershipId;
}
using (var context = new CoCodbEntities1())
{
var g = new GuardianModel
{
MembershipId = memberId.ToString(),
DisplayName = BNETId,
MembershipType = 4,
TeamID = model.TeamModel.Id
};
TempData["UserMessage"] = ViewBag.TeamList.Id;
return RedirectToAction("Success");
}
}
catch
{
}
return View();
}
These are the values getting passed into the Post action
From the screenshot you shared, it looks like TeamModel property is the virtual navigational property of type TeamModel. You should not bother about loading that. All you need to worry about loading the forign key property value (usually a simple type like an int or so.
Your SELECT element name should be TeamID. When the form is submitted, it will map the selected option value to the TeamID property value of your model which is the foreign key property.
#Html.DropDownListFor(m => m.TeamID, (SelectList)ViewBag.TeamList,
"- Select a Team to Join -", new { #class= "form-control form-control-lg" })
While this might fix the issue, It is a good idea to use a view model instead of using your entity class.
I found the issues I was having. All I needed to get passed into the post action was the Id of the TeamModel. So I changed this line:
#Html.DropDownListFor(m => m.TeamModel.Id, (SelectList)ViewBag.TeamList, "- Select a Team to Join -", new { #class= "form-control form-control-lg" })
I just added the Id and it seemed to work.

c# MVC post not populating view model

I have a ViewModel which has a property defined as an IList of type Division. The Division object is as follows:
public class DivisionViewModel :
{
public int Id { get; set; }
public int DivisionId { get; set; }
public string Name { get; set; }
}
In the past, I have just created the following in the View:
<div class="divisions">
#for (int i = 0; i < Model.Divisions.Count(); i++)
{
#Html.Hidden(Model.Divisions[i].DivisionId.ToString(), new { #name = "Divisions[" + i + "].DivisionId", #id = "Divisions_" + i + "__.DivisionId" })
#Html.Hidden(Model.Divisions[i].Id.ToString(), new { #name = "Divisions[" + i + "].Id", #id = "Divisions_" + i + "__.Id" })
#Html.Hidden(Model.Divisions[i].Name, new { #name = "Divisions[" + i + "].Name", #id = "Divisions_" + i + "__.Name" })
}
</div>
When I submit the page, on checking the returned ViewModel, I find that the DivisionId is populated, but both the Id and Name property is not.
I have commented out the line that sets the DivisionId, but it is still returned without the Id and Name properties
What have I missed?
The most common cause for model binding failing is that the name attributes of your inputs do not match the names of the properties in your model, and the first thing you should always do is inspect the html your code is actually generating. Assuming the value of the DivisionId in the first item is 1, what you are generating is
<input name="1" type="hidden" value="{ #name = Divisions[0].DivisionId, #id = "Divisions_0__.DivisionId }" />
which has no relationship to your model.
The 1st parameter is the property to bind to, and you are telling it
to bind to a property named 1 - the result of Model.Divisions[i].DivisionId.ToString(), which does not exist, and is illegal
anyway
The 2nd parameter of #Html.Hidden() is the value of the property,
so your telling it to use your anonymous object as the value
Even if you added the 2nd parameter (so that the html attributes
would be generated from the anonymous object), it would still not work since using
#name="..." fortunately does nothing at all - the purpose of using
the HtmlHelper methods is to correctly bind to your model)
Just use
#for (int i = 0; i < Model.Divisions.Count; i++)
{
#Html.HiddenFor(m => m.Divisions[i].DivisionId)
#Html.HiddenFor(m => m.Divisions[i].Id)
#Html.HiddenFor(m => m.Divisions[i].Name)
}
And the correct name and value attributes will be generated for binding to your collection.
Having said that, all you have are hidden inputs, so it makes no sense to generate all that extra html, send it to the browser and send it all back again unchanged. If you need that collection in the POST method, then just get it from your repository.
As a side note, your new { #id = ".." } makes no sense. The purpose of adding an id attribute is for use in css and javascript/jquery selectors, which do not really make sense for collection items, but in any case would not work because you have a . (dot) which would act as a class name selector

razor dropdown list add the data but show after refresh the page

#{
HotelManagementEntities db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
#Html.DropDownList("tblCategories",
ViewBag.Category as SelectList,
new
{
#class = "drplist",
id = "drpItemCat",
ng_show = "selectfieldforcat"
})
There are problems in your code, you are populating list from database here:
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
but down in the code when creating SelectList, you are again getting records form the database using the dbContext:
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
// note the db.tblCategories as first parameter, this is wrong
and if you have actually written this code in view, then you don't even need ViewBag, but your approach is not right, you should be writing the poupulation code in the controller action and then use it in the view:
public ActionResult SomeAction()
{
HotelManagementEntities db = new HotelManagementEntities();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
In Controller:
public ActionResult Add()
{
ViewBag.countryList = GetCountries();
return View();
}
In razor View .csHtml
#Html.DropDownListFor(x => x.countryId, new SelectList(ViewBag.countryList, "id", "Name"), 'Select Country',
new { #id = "ddlCountry", #rows = 1 })
Despite the fact, it's not a good practice to have data retrieval in views, based on you code, you could try the following:
#{
var db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new
{
m.intseqid,
m.varCategory
}).ToList();
}
#Html.DropDownList("tblCategories", new SelectList(#list, "intseqid", "varCategory"));
A more appropriate approach it would be to create a model, which would contain as a property a SelectList object. Then at the corresponding action at the controller to create a new model and set the categories to mentioned above property. Last pass at your view (so now you have to define that your view expects a model of a specific type, that you declared above) the model and at the dropdownlist html helper do the following:
#Html.DropDownList("tblCategories", Model.Categories)
This way you are more adhered to separation of concerns that is built in the MVC pattern and your code would be more readable and maintainable in the long run. Your example is pretty simple. So that I mentioned before cannot be seen at this point. However, while you model would be more complex and you have to do more things in the views, you will see that I mean by saying more readable and maintainable.
Last but not least, try to avoid using the ViewBag. Apparently you can accomplish what you have to do with the use of ViewBag, but it's far better you pass a strongly typed object as a model to your view and have there all you need to show on the view rather than adding things in the ViewBag at the controller and then use casts in the view to do whatever you want.

adding new values to database from view whose model is a list

in an ASP.NET-MVC 5 application I have the following models
class Employee {
int EmployeeID {get;set;}
string FirstName {get;set;}
List<OfficeLocations> OfficeLocations {get;set;}
}
class OfficeLocations {
int OfficeLocationsID {get;set;}
//foreign key
int EmployeeID {get;set;}
string Value1 {get;set;}
string Value2 {get;set;}
}
I have an edit view for modifying or ADDING different office locations that an employee could belong to. It looks something like this:
#model List<Project.Models.OfficeLocations>
#for (int i = 0; i < Model.Count; i++) {
#Html.EditorFor(m => m[i].CitLocation, new { htmlAttributes = new { #class = "my_editor" } })
#Html.HiddenFor(m => m[i].OfficeLocationsID)
#Html.HiddenFor(m => m[i].EmployeeID)
}
//extra editor box for adding a new value
#Html.Editorfor(??????.Value)
I'm a little confused as to how to add new entries to my model (list) in the database table. What do I put in the parameter for the extra Editorfor box (where all the ???? are)
also, what would the controller action method look like?
change your viewmodel to have an officelocation and a list of officelocation... With that you can add the non list officelocation object in you extra editor box... Or you can just retain your viemodel like that and just manually create a model using jquery and pass it using an ajax jquery...
To fix this issue I came up with the following javascript:
<script>
$(document).ready(function () {
index = 0;
});
$('#add_button').click(function () {
var placeHolderNameAttribute = "List2[#].Value1";
var indexedNameAttribute = "List2[" + index + "].Value1";
var placeHolderIdAttribute = "new_lastName_input";
var indexedIdAttribute = "new_lastName_input" + index;
document.getElementById(placeHolderIdAttribute).name = indexedNameAttribute;
document.getElementById(placeHolderIdAttribute).id = indexedIdAttribute;
var clone1 = $("#new_lastName_input" + index).clone();
document.getElementById(indexedIdAttribute).name = placeHolderNameAttribute;
document.getElementById(indexedIdAttribute).id = placeHolderIdAttribute;
if (index == 0) {
$('#nlnPlaceHolder').remove();
}
$('#LN_editor_box').append(clone1);
index += 1;
});
</script>
and the following placeholder input field
<input id="new_lastName_input" class="my_editor" type="text" name="List2[#].Value1" value="New Last Name" />
and now my controller post method accepts two parameters, the original list of updated/edited values, and a new list of only new values.
ActionResult myMethod(List<OfficeLocations> list1, OfficeLocations[] list2)
and if the value is in list1 then it will update in the database, and if it's in list2 it will be added

How to send an object to the Telerik MVC Grid Ajax Select() Controller Method

I am using the Telerik MVC Grid with Ajax binding, and am having a problem passing an object to the controller to be used to filter data. I am able to pass simple data (string, int), but not a more complex object.
For instance, I can to this no problem:
.DataBinding(dataBinding => dataBinding.Ajax().Select("_CasesAjaxBinding", "Home", new {orderId = "12345"} ))
And then in my controller, handle the orderId like this:
public ActionResult _CasesAjaxBinding(string orderId)
The problem I am having is when I try to pass a more complex object (in this case the #Model) to the controller, like this (The #Model is type CaseFilterModel):
.DataBinding(dataBinding => dataBinding.Ajax().Select("_CasesAjaxBinding", "Home", new {filterSpec = #Model} ))
And then trying to handle the object, like this:
public ActionResult _CasesAjaxBinding(CaseFilterModel filterSpec)
The filterSpec parameter is always null.
Thanks in advance for any ideas!
As far as I can find on the Telerik forums this cannot be done this way. There was a similar question on there, which described exactly the same problem. When passing the model it was always null in the controller's action method.
However there is a workaround if you want to pass multiple parameters to the select method in order to filter the data, but it requires some client side coding.
I'll include a summary of that work around here, so that the answer is complete. A solemn link doesn't say much.
Assume we have a grid which displays orders items (articles) from all the orders. First make sure to hook up the client side onDataBinding event:
<%= Html.Telerik().Grid<Order>()
.Name("Grid")
.ClientEvents(events => events.OnDataBinding("onDataBinding"))
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_AjaxBinding", "Grid"))
%>
In the client side event handler you need to compose your select URL. Here I'll pass two parameters, an order id (int) and a description of an article (string).
<script type="text/javascript">
function onDataBinding(e) {
var orderId = 100;
var searchText = "test";
var params = { OrderId: orderId, ArticleDescription: searchText };
var paramsStr = $.param(params);
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Grid") %>"
+ "?" + paramsStr;
var grid = $('#Grid').data('tGrid');
grid.ajax.selectUrl = selectUrl;
}
</script>
Then in your controller you can declare the select method as follows:
[GridAction]
public ActionResult _AjaxFilterBinding(AjaxFilterBindingModel model)
{
// Retrieve data here and filter it based upon the data present in the model.
var data = ...;
return View(new GridModel<Order> { Data = data });
}
The model looks like:
public class AjaxFilterBindingModel
{
public int OrderId { get; set; }
public string ArticleDescription { get; set; }
}
Passing a collection via the URL (GET) is also possible. Let's assume you want a collection of order IDs instead of just one.
The model would look like this:
public class AjaxFilterBindingModel
{
public IEnumerable<int> OrderIds { get; set; }
public string ArticleDescription { get; set; }
}
And the JavaScript would look like this:
function onDataBinding(e) {
jQuery.ajaxSettings.traditional = true;
var intArray = [1, 2, 3, 4, 5];
var params = { OrderIds: intArray, ArticleDescription: "Test" };
var paramsStr = $.param(params);
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Home") %>" + "?"
+ paramsStr;
var grid = $('#Grid').data('tGrid');
grid.ajax.selectUrl = selectUrl;
}
Remark: Don't forget to set "jQuery.ajaxSettings.traditional = true;" or the parameters will be serialized incorrectly and the ASP.NET MVC model binder will not be able to bind the integer array.
And to be complete here is the Telerik forum thread I mentioned:
http://www.telerik.com/community/forums/aspnet-mvc/grid/getting-the-model-object-in-a-custom-binding-grid-ajax-controller.aspx
And the suggested work around:
http://www.telerik.com/community/forums/aspnet-mvc/grid/code-sample-sending-additional-filters-with-ajax-binding.aspx
Also as a note, this solution is not in Razor syntax... took me forever to spot it but the line
var selectUrl = "<%= #Url.Action("_AjaxFilterBinding", "Home") %>" + "?" + paramsStr;
should be changed to
var selectUrl = "#Url.Action("_AjaxFilterBinding", "Home")" + "?" + paramsStr;
For Razor... I copied the code but couldn't figure out why my _AjaxFilterBinding was not being called. Just thought I'd point it out in case anyone else has this problem.
Thanks for the solution, it NOW works great :)

Categories

Resources