post complex viewmodels using ajax.beginForm MVC4 only getting null - asp.net-mvc-4

im very new to mvc, lest say i have a viewmodel that cotains objects like
public class vm_set_rol
{
public IEnumerable<SelectListItem> roles { get; set; }
public Rol_User rol { get; set; }
}
rol is an object like:
public class Rol_User
{
public int idUser { get; set; }
public int Role { get; set; }
public int GrantedBy { get; set; }
public bool canGrant { get; set; }
public DateTime ExpirationDate { get; set; }
}
so i have a form on a view to let the user select 1 role from a roles dropdown and select a date and a checkbox somthing like:
<div class="ModalContainer">
#using (Ajax.BeginForm(new AjaxOptions
{
UpdateTargetId = "gestionRolContainer",
Url = "Permiso/Test",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
}
)
)
{
<fieldset>
<legend>#Res_String.RolLabel</legend>
<span>ROL:</span><br />#Html.DropDownListFor(m => m.rol, Model.roles, new {#id="AdmPermUserRolesDropDown" })
<br />
#Html.CheckBoxFor(m => m.rol.conceder ,Model.rol.conceder) <span>Delegate?</span>
<br />
<input type="submit" class="buttonClass" value="OK" />
</fieldset>
}
</div>
the problem is that i only get null values, if i create some other property on the model like a string or int, those are posted back ok,
i kind of understad why objects are not posted back, bust is there any workaround??? or put a object on the modes is just wrong and i shuld declare the propertis on the viewmodel instead of an object???

Your dropdown is incorrectly bound. It should be bound to a scalar property to hold the selected value:
#Html.DropDownListFor(
m => m.rol.Role,
Model.roles,
new { id = "AdmPermUserRolesDropDown" }
)
As far as the Roles collection property is concerned, it will always be null in your controller action because this list is never sent to the server when you submit a form. Only the selected value is sent. So if you need to redisplay this view once again you will have to populate the Roles collection property in your HttpPost action the same way you did in your GET action.
Also your checkbox is bound to some m => m.rol.conceder property which doesn't exist in the view model you have shown. I guess you meant using the canGrant boolean property. Also you don't need to provide as second parameter to the CheckBoxFor helper the value. It will be inferred from the lambda expression:
#Html.CheckBoxFor(m => m.rol.canGrant) <span>Delegate?</span>
Last but not least, since you are using an Ajax.BeginForm make sure that you have referenced the jquery.unobtrusive-ajax.js script in your view.

Related

Null viewmodel passed to controller

I have a basic 'create' view scaffolded from a domain model, so it is typed to the model
#model TblProduct
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="Artist" class="form-control" />
...
I'm trying to add functionality and use a view model instead, and I'm starting with a very basic viewmodel with only that domain model within it:
public class ProductViewModel
{
public TblProduct P { get; set; }
}
Now I've changed the 'create' view to use the view model instead
#model ProductViewModel
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="P.Artist" class="form-control" />
...
So I expect the model to be valid given that (aside from editing the variable names) I'm populating all the same fields from the form, and effectively no other fields have been added to the model.
An error occurs when I post the form, I pass a ProductViewModel parameter to the create method but on inspection it is null. However ModelState.IsValid is true. So the code tries to write to the db and fails.
public async Task<IActionResult> Create([Bind("ID,Artist,ProductTitle... (long list removed)...] ProductViewModel productAndItems)
{
var prod = productAndItems.P;
if (ModelState.IsValid)
{
_context.Add(prod);
...FAIL
Any idea what I should be checking here - what am I missing?
How do I get the view (typed to a viewmodel) to pass the model data to the controller? And if it's null, how can ModelState.IsValid be true? In the example above I have debugged, the parameter passed in productAndItems is null.
Your current Bind attribute is looking for the following properties ID,Artist,ProductTitle... (White-list) and it's not finding them so therefore it's ignore everything and treating it is as a (Black-list) item.
You can either decorate your ProductViewModel with the Bind attribute as follows:
[Bind(Include = "P")]
public class ProductViewModel
{
public TblProduct P { get; set; }
}
This will of course mean all the properties in the TblProduct will be bound when submitting
If you do not want all of the properties to be bound on submit for TblProduct then you can decorate the TblProduct with the Bind attribute as follows
public class ProductViewModel
{
public TblProduct P { get; set; }
}
[Bind(Include = "ID,Artist,ProductTitle")]
public class TblProduct
{
public int ID { get; set; }
public string Artist { get; set; }
public string ProductTitle { get; set; }
public string ProductSubTitle { get; set; } //we will not include this in our (White-list)
//more props
}
More reading at MSDN
you need to add a name attribute to your form so the controller will pick it up.

why checkbox wont check on page load?

i have the following database table for the Compounds table (chemical compounds/elements in the periodic table) there are typos in table data so ignore them
the data is :
the controller :
public class CheckboxController : Controller
{
//
// GET: /Checkbox/
testdbEntities db = new testdbEntities();
[HttpGet]
public ActionResult Index()
{
var comps = db.Compounds.Select(c => new CompoundModel { Id=c.Id, CompoundName=c.Name, IsSelected=c.IsSelected}).ToList();
CheckboxVM cvm = new CheckboxVM { checkboxData=comps};
return View(cvm);
}
[HttpPost]
public string Index(IEnumerable<CheckboxVM> collection)
{
return "";
}
}
Model class CompoundModel is:
public class CompoundModel
{
public int Id { get; set; }
public string Code { get; set; }
public string CompoundName { get; set; }
public bool IsSelected { get; set; }
}
and the ViewModel CheckBoxVM:
public class CheckboxVM
{
public string Id { get; set; }
public string CompoundNmae { get; set; }
public bool IsSelected { get; set; }
public IEnumerable<CompoundModel> checkboxData { get; set; }
}
When the page loads it should display check boxes with names and if db table has checked on them (IsSelected=1) then they should be checked.In the post back i need to receive the id, of the user checked checkboxes. At the moment my code does meet the first requirement to check the checked checkboxes based on IsSelected on page load. Is there a way to fix this?
If you need a video with debugging please ask i will be happy to post : )
THE VIEW: (UPDATE)
#model recitejs1.Models.CheckboxVM
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
foreach (var item in Model.checkboxData)
{
#Html.CheckBoxFor(x=>x.IsSelected, (item.IsSelected)?new{#checked="check"}:null)#item.CompoundName
#Html.HiddenFor(x=>x.Id, item.Id)
#Html.HiddenFor(x=>x.CompoundNmae, item.CompoundName)
}
<br><br>
<input type="submit" name="name" value="Send" />
}
You cannot use a foreach loop to generate form controls. It generates duplicate name attributes (that have no relationship to your model) and duplicate id attributes (invalid html).
Create a custom `EditorTemplate for your model
In /Views/Shared/EditorTemplates/CompoundModel.cshtml
#model recitejs1.Models.CompoundModel
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.CompoundName)
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.CompoundName)
Then in the main view
#model recitejs1.Models.CheckboxVM
....
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.checkboxData)
<input type="submit" name="name" value="Send" />
}
The EditorFor() method will generate the correct html for each item in your collection
Note: You should inspect the html before and after you make this change to better understand how model binding works.
Note also that your POST method parameter needs to be
public string Index(CheckboxVM model)
since that's what the view is based on. However the only property of CheckboxVM that you use in the view is IEnumerable<CompoundModel> checkboxData in which case your view should be
#model IEnumerable<CompoundModel>
...
#Html.EditorFor(m => m)
and keep the POST method as it is (but change the GET method)

Passing Model via dropDownListFor() from view to controller

I am trying to pass a Model from my view to my controller using dropDownListFor.
After choosing something from the list it sends the model to my controller but it's content NULL.
This is what i have for the Model
public class Model
{
public int ModelId { get; set; }
[Required]
public string Name { get; set; }
}
This is what my ViewModel looks like
public class ModelVM
{
public List<Model> Models;
public Model SelectModel { get; set; }
public IEnumerable<SelectListItem> ModelItems
{
get { return new SelectList(Models, "ModelId", "Name"); }
}
}
The controller when i put data in the ViewModel looks like this
public ActionResult Index()
{
ModelVM modelVM= new ModelVM()
{
Models = manager.GetAllModels().ToList()
};
return View(modelVM);
}
Finally this is what i have in the View for the dropDownList
#using (Html.BeginForm("Home", "Home", FormMethod.Post))
{
#Html.DropDownListFor(model => model.SelectModel, Model.ModelItems)
<input type="submit" value="Go" />
}
So this is supposed to send Model to my controller.
But when i check the content of the passed Model in the controller, everything is NULL which isn't supposed to be because when i debug the view and check for the content of ModelItems, everything in the ModelItems is there.
Here is when i check the content of the passed Model
public ActionResult Home(Model model) <<<<<<<<<< Content == NULL
{
return View();
}
A <select> element on posts back a single value. Html has no concept of what your c# Model class is so you cannot bind to a complex object. You need to bind to the ModelId property of Model. Your view model should be
public class ModelVM
{
[Display(Name = "Model")]
public int SelectedModel { get; set; }
public SelectList ModelItems { get; set ;}
}
and in the controller
public ActionResult Index()
{
ModelVM modelVM = new ModelVM()
{
ModelItems = new SelectList(manager.GetAllModels(), "ModelId", "Name")
// SelectedModel = ? if you want to preselect an item
};
return View(modelVM);
}
and in the view
#Html.LabelFor(m => m.SelectedModel)
#Html.DropDownListFor(m => m.SelectedModel, Model.ModelItems)
and in the post method your model will be correctly bound with the ID of the selected model
Trying passing in type ModelVM to the Home action as that is the type being passed to Index view.
Instead of model.SelectModel in the drop-down declaration, you only need an int ID to capture which ID is selected.

MVC4 pass model values from view (for-each) to controller

Hello dear i am trying to pass my model values from view to controller. when i am trying to do that i receive only null value in controller my view is given below
I also have used #html.hadden but still received only null value in contoller after button click. please someone help me and tell me the solution of this problem.
My VIEW IS THIS WHERE I AM TRYING TO PASS VALUE TO CONTOLLER:
#model IEnumerable<onlinebookstore.entityframwork.book>
#using (Html.BeginForm("AddToCart", "ShopingCart", FormMethod.Post))
{
foreach (var item in Model)
{
<img src="#Url.Content(item.book_img)" alt="Image" width="170px"; height="230px" />
<br />
#Html.DisplayFor(modelItem => item.book_name)
<br />
<label>Author:</label>
#Html.DisplayFor(modelItem => item.author)
<br />
<label>Pk.</label>#Html.DisplayFor(modelItem => item.price, new { id = "price" })&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
#Html.ActionLink("For Detail..", "Detail", "Book", new { id = item.inventory_id }, null)
<br />
<input type="submit" class="btnreg" id="btnaddcart" value="Add to Basket"/>
<br />
}
}
My Controller where i am trying to receive values from view and here i received only null values
[HttpPost]
public ActionResult AddToCart(book model)
{
var cartitem =
db.shoping_cart_item
.FirstOrDefault(u => u.inventory_id == model.inventory_id);
if (cartitem == null && Session["userid"] != null)
{
cartitem = new shoping_cart_item();
cartitem.inventory_id = model.inventory_id;
cartitem.price = Convert.ToDecimal(model.price.ToString());
db.shoping_cart_item.Add(cartitem);
}
return View();
}
My Model (BOOK)
public string inventory_id { get; set; }
public string book_name { get; set; }
public string author { get; set; }
public string book_type { get; set; }
public Nullable<int> no_book { get; set; }
public Nullable<decimal> price { get; set; }
public string book_img { get; set; }
public string book_description { get; set; }
Please help me how we will pass model value using for-each loop in view to Controller
You have no inputs for your form. You aren't submitting anything. Try #Html.TexboxFor() or some other helper.
So what will be the remedy of this problem where i can display my all values in view and on button click all the values of model will be pass on to controller.
I think you are doing it wrong.
Your view is looping through the list of onlinebookstore.entityframwork.book to display the details AND one button for EACH of them inside one big form. When you submit that, all inputs that can be submitted will be posted (a collection of books at best). But on your controller, you are just receiving one instance of onlinebookstore.entityframwork.book
You might want to rethink your view by creating the loop first then inside it, have the #using (Html.BeginForm("AddToCart", "ShopingCart", FormMethod.Post)) markup inside. This creates a form for each onlinebookstore.entityframwork.book. Just make the necessary adjustments for multi-form on a single page to work.

Passing extra data to a post controller - MVC

Let's say I have the model
public class ViewModel
{
[Required]
[Display(Name = "Email address")]
public string Email { get; set; }
public string ExtraData{ get; set; }
}
Where ExtraData is simply some extra text that gets added in the GET action:
[HttpGet]
public ActionResult ActionMethod()
{
ViewModel modelWithExtraData= new ViewModel{ ExtraData = "Some extra data." };
return PartialView("MyView", modelWithExtraData);
}
And gets rendered inside the view as such:
<form>
#Html.TextBoxFor(m => m.Email)
<div>#Model.ExtraData</div>
<form>
When this form gets posted the the controller, I'd like the extra data "Some extra data" to be intact and posted with the model but this does not happen.
[HttpPost]
public ActionResult ActionMethod(ViewModel model)
{
//model.ExtraData = null ... Not good
}
I looked through all the #Html helper methods but can't seem to find the right one to simply display the text (non-editable) and send it back.
You could include the extra data as a hidden field inside the form alongside with the textbox:
<form>
#Html.TextBoxFor(m => m.Email)
#Html.HiddenFor(m => m.ExtraData)
<div>#Model.ExtraData</div>
<form>