How to pass the full Model from view to controller via jquery in a MVC c# application - asp.net-mvc-3

I have a list of checkbox's and textbox's. I want to let the user add items to the list via a partial view modal popup.
After the user adds an item to the list, if any items on the original view have values in them, I want them preserved, and the page refreshed with the added items.
I want to send the full model back to the controller from the original view, I can then just add the new items to that model and pass the model back to the original page and have all my values preserved.
I could grab all the values and pass them via loops and such in javascript (very tedious), but I think the full model would be the easiest way.
I sawe a link from Laviak on a post from here..., but I can't get it to work.
it states....
If you need to send the FULL model to the controller, you first need the model to be available to your javascript code.
In our app, we do this with an extension method:
public static class JsonExtensions
{
public static string ToJson(this Object obj)
{
return new JavaScriptSerializer().Serialize(obj);
}
}
On the view, we use it to render the model:
<script type="javascript">
var model = <%= Model.ToJson() %>
</script>
You can then pass the model variable into your $.ajax call.
Has anyone got this to work???
Thanks
Bill

you can do something like this:
<script type="text/javascript">
var dataViewModel = #Html.Raw(Json.Encode(Model)); //Make sure you send the proper model to your view
function MethodPost(param1, param2, etc...) {
dataviewModel.Param1 = param1; //Or load a value from jQuery
dataviewModel.Param2 = $("#param2").val();
}
//Pass it to a controller method
$.post("#Url.Action(MVC.Home.PostMethodInController())", { viewModel: JSON.stringify(dataViewModel)} , function(data) {
//Do something with the data returned.
}
</script>
In the controller you get your class/model using Json.Net which is available on nuget.
public virtual ActionResult Index()
{
return View(new MyModelToView());//Send at least an empty model
}
[HttpPost]
public virtual JsonResult PostMethodInController(string viewModel)
{
var entity = JsonConvert.DeserializeObject<MyObject>(viewModel);
//Do Something with your entity/class
return Json(entity, JsonRequestBehavior.AllowGet);
}
Hope this helps.

Related

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'print_ad_option_id'

So I have method in which I do some ajax:
function prodChange() {
console.log(this.value);
// ajax
var url = '#Url.Action("GetAdvProdList", "Contract")' + '?prod_id=' + this.value;
$.ajax({
url: url,
type: 'GET',
success: showAdvProd,
error: function() {
console.log("FAILSHAKUREHKGAHH");
}
});
}
The ajax returns successfully and calls this controller method.
public ActionResult GetAdvProdList(int prod_id) { // product id
// get advertising products for this product
OutlookMediaEntities1 db = new OutlookMediaEntities1();
var advsList = from printAdOption in db.print_ad_option
where printAdOption.print_product_id == prod_id
select printAdOption;
List<SelectListItem> adv_list = new List<SelectListItem>(); // list of ads
List<print_ad_option> advs = advsList.ToList(); // list from db
foreach (print_ad_option av in advs)
{
SelectListItem temp_item = new SelectListItem();
temp_item.Text = av.name;
temp_item.Value = av.print_ad_option_id.ToString();
adv_list.Add(temp_item);
}
ViewData["advproducts"] = new SelectList((IEnumerable<SelectListItem>)adv_list.ToList(), "Value", "Text");
return null;
}
I'm returning null because it doesn't work if I return View or PartialView, and I don't think I want it to return those things anyway. All I want is the viewdata stuff to be set.
The ajax worked well before I added code to use the view data. Unfortunately when I try to use the new ViewData in the ajax success method, I get this error:
"There is no ViewData item of type 'IEnumerable' that has the key 'print_ad_option_id'"
The thing is that I get this error when I first load the page, so it seems to be attempting to evaluate the "#Html.DropDownListFor..." before the showAdvProd function is called:
// called when the ajax returns a list of adv prods in a viewdata
function showAdvProd() {
// add drop down
$("#drdn1").append('#Html.DropDownListFor(m => m.print_ad_option_id, ViewData["advproducts"] as SelectList)');
}
The function that does ajax is called when an item is selected from a (different) dropdown menu, but with this error the page doesn't even load, so obviously the ajax function is never called and thus the controller method never called. So of course it won't recognize the viewdata...
I'm similarly creating a dropdown menu in another part of my form, using viewdata but without ajax, and it works fine. So I think something is wrong with my ajax or my controller method, not with how I'm using the viewdata.
Thank you in advance for any help!
The problem is that on your initial page load, the view engine will evaluate all the code blocks - anything with a '#' symbol in front of it. Thus it will try and look for a ViewData item called advproducts before it exists, even though that reference is within a javascript function that hasn't been called yet. Remember - code blocks (such as #Html.blahblahblah() are evaluated server side, while you are expecting your ajax to run on the client side, after the page has been sent to the client.
A couple of options: Returning a partial view should work. You could return a partialview that contains a dropdown and fill it from the ViewBag. That would look like this:
initial page:
...
<div id="advProductsSection"></div>
...
Your ajax would call the same function, but would return a partial view:
public ActionResult GetAdvProdList(int prod_id) { // product id
...
ViewData["advproducts"] = new SelectList((IEnumerable<SelectListItem>)adv_list.ToList(), "Value", "Text");
return View("_AdvProducts");
}
And then a partial view (Here named _AdvProducts.cshtml):
#Html.DropDownList("className", (IEnumerable<SelectListItem>) ViewBag["advproducts"])
Your ajax function would need to change to replace the div with the new partial:
success: function (result) {
$('#advProductsSection').html(result);
}
Or something like that..
A second alternative is to return JSON from the Action Method and populate a dropdown with that.
Your Controller:
...
return JSON(adv_list.ToList());
}
Then in your ajax success func:
success: function(list) {
// states is your JSON array
var $dropdown = $('#Dropdown');
$.each(list, function(i, product) {
$('<option>', {
value: list.Value
}).html(list.Text).appendTo($dropdown);
});
}
You'd need to cleat the dropdown again when prodchange is called.

How to return a partial view in a controller in ASP.NET MVC3?

I have a controller and one of its methods (actions) access my database of items. That method checks the item type. How do I show my partial view only if the item retrieved from my database is of a specific type?
Controller Action example:
public ActionResult CheckItem(Koko model)
{
var item = db.Items.Where(item => item.Number == model.Number).First();
if(item.Type=="EXPENSIVE")
{
//show partial view (enable my partial view in one of my Views)
}
}
You could return a PartialView action result:
public ActionResult CheckItem(Koko model)
{
var item = db.Items.Where(item => item.Number == model.Number).First();
if (item.Type=="EXPENSIVE")
{
return PartialView("name of the partial", someViewModel);
}
...
}
Now the controller action will return partial HTML. This obviously means that you might need to use AJAX in order to invoke this controller action otherwise you will get the partial view replace the current browser window. In the AJAX success callback you could reinject the partial HTML in the DOM to see the update.

ASP.NET MVC 3 - How to execute code after leaving a View?

I'm in some trouble here. I have a view in which a company can thumbs up or down users of our site. The users are listed in a table and a column has the little hand images for the company to vote for or against the user. I had programmed an ActionLink there, however, I don't want a postback to happen every time a company vote on a user.
I decided to fill a list with the user IDs the company votes on and then, when leaving the page, a filter would intercept the request, get the list and process the votes. In this post I was taught how to initialize Filter parameters when calling the Action, but as you can see, I need a way for the Filter to get the Lists when the user exits the View, not in an Action.
I wouldn't want to use code-behind because, paired with MVC, it is not a best practice, but postbacks are not an option either.
Here's what I have so far:
public ActionResult ListUsers()
{
// Create a List with user models and send it to a View,
// which generates a WebGrid
return View(userList);
}
public class PromoteUsersFilter : ActionFilterAttribute
{
public int[] UsersToPromote { get; set; }
public int[] UsersToScrewWith { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
//insert promoting logic here
}
}
I believe there is a simple way of doing it, since most websites have this funcionality. Can anyone guide me with this?
Why not use AJAX that calls into your controller methods? If you set up your json properly, it will still be deserialized into an object, too.
This is OK even in the MVC mindset as far as I know. If you need to persist your data, but not update the entire page it is the only way that I know. You can even swap out entire partial views using AJAX.
I think the common misconception here is that the View portion (of MVC) is not just one page, but actually made up of a number of views smashed into the one page. So, updating one of those views separately does not really break the pattern.
Definitely go for the AJAX solution:
It could look like this in your view:
<div>
<span class="cssUpvote" id="upvote"><span>
<span class="cssDownvote" id="downvote"></span>
</div>
with some Jquery
<script>
$(document).on('click', 'upvote', function (event) {
$.ajax({
type: 'POST',
url: '/Votes/Upvote',
data: { id: companyId }
});
});
$(document).on('click', 'downvote', function (event) {
$.ajax({
type: 'POST',
url: '/Votes/Downvote',
data: { id: companyId }
});
});
</script>
And then your actions on the Controller
[HttpPost]
public ActionResult Upvote(int id)
{
//Do something with the (company)id
return Json();
}
[HttpPost]
public ActionResult Downvote(int id)
{
//Do something with the (company)id
return Json();
}

MVC3/Razor to Controller Ajax call

I have a Razor view with a couple of dropdown lists. If the value of one of the dropdown's is changed I want to clear the values in the other drop down and put new ones in. What values I put in depends on the values in the model that the view uses and that is why I need to send the model back from the view to the controller. The controller will then also need to be able to modify the dropdown by sending back data to the view. Note, that I am not saying that I want to go back to the controller from a form submit using Ajax. I am going back to the controller using a form submit, but not using Ajax.
Please can someone give me some simple code or some pointers to show how this might be done.
Thanks
I personally use ViewBag & ViewData to solve this condition.
Controller:
public ActionResult Index()
{
ViewBag.dd1value = default_value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
Return View();
}
View:
In the first dropdownlist add an onchange javascript.
<select onchange="javascript:this.form.submit();">
#foreach (var item in ViewBag.dd1) {
if (ViewBag.dd1value = item.dd1value)
{
<option selected value="#item.dd1value">#item.dd1text</option>
}
else
{
<option value="#item.dd1value">#item.dd1text</option>
}
}
Then, on submit button give it a name.
<input type="submit" name="Genereate" value="Generate" />
In the controller, create 2 ActionResult to receive data.
For dropdownlist:
[HttpPost]
public ActionResult Index(int dd1value)
{
ViewBag.dd1value = dd1value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
Return View();
}
For submit button:
[HttpPost]
public ActionResult Index(int dd1value, int dd2value, FormCollection collection)
{
ViewBag.dd1value = dd1value;
ViewBag.dd2value = dd2value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
ViewBag.result = Result(dd1value, dd2value);
Return View();
}
If you don't need button:
[HttpPost]
public ActionResult Index(int dd1value, int dd2value)
{
ViewBag.dd1value = dd1value;
ViewBag.dd2value = dd2value;
ViewBag.dd1 = DropDownlist1();
ViewBag.dd2 = DropDownlist2(dd1value);
ViewBag.result = Result(dd1value, dd2value);
Return View();
}
Please note that if you use ViewBag / ViewData, all the help you get from the compiler is disabled and runtime errors/bugs will occur more likely than if the property has been on a "normal" object and typos would be catched by the compiler.
I would implement a different solution from DragonZelda.
I would create a ViewModel object containing the data that you need on the page, that the View binds to.
Then, I would create controls that bind to that Model, like:
#Html.DropDownListFor(x => x.SomeDDLSelected, ......)
x.SomeDDLSelected would be a property in your ViewModel object that would automatically get the selected value in the dropdownlist when the automatic model binder gets in action.
Then, to finalize it, the Controller action would receive your ViewModel object as a parameter:
public ActionResult MyAction(MyViewModelObject obj)
{...}
And you get all your data nice and tidy, all strong typing.

Refreshing parent view when a partial view's form is submitted

I'm looking into using partial views in MVC3 using Razor, and I get my partial view to render and it works fine.
What I'd like to do, though, is refresh the parent view when the partial view is submitted.
Code in my parent view to render partial view
<div id="mydiv">
#{ Html.RenderAction("Add", "Request"); }
</div>
Action for parent view is simple,
public ActionResult Index()
{
List<obj> reqs = //some query
return View(reqs);
}
In my partial view's get action I have:
public ActionResult Add()
{
AddRequestViewModel vm = new AddRequestViewModel();
//set some stuff on the VM here
return PartialView(vm);
}
In the post action called by the partial view, if modelstate isn't valid, return PartialView(vm)
If it is valid, I'd like the parent and partial views to refresh.
I tried RedirectToAction, but this can't be called in an action called by a partial, apparently, and I tried return Index();, but this causes an issue with the code used to render the partial view,
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[DatRequests.Models.ReqRequest]', but this dictionary requires a model item of type 'DatRequests.ViewModels.AddRequestViewModel'.
Any suggestions on how to do this would be appreciated. The purpose of the page is to show a list of elements, and the partial contains a form to add a new element to the list.
Edit: The partial's model is different, as it contains data for selection, which is from a db, which is why I tried RenderAction, but I'm not sure if there are other ways of doing this.
When the partial view is submitted normally you submit it to some controller action. You could either submit it using a normal request or an AJAX request. If you use a normal request you could perform a standard redirect to the Index inside the POST controller action that will handle the form submission. If you use AJAX, you could return a JSON result pointing to the url that you want to redirect:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
return Json(new { url = Url.Action("Index") });
}
and inside your AJAX success callback:
success: function(result) {
if (result.url) {
// we have a success
window.location.href = result.url;
} else {
// invalid modelstate => refresh the partial
$('#mydiv').html(result);
}
}
Probably RenderAction should not be used this way.
When using Html.RenderAction, a new/seperate request would be sent to the server. And you got another chance to load some data from db or somewhere else to display to the client. Also, you could apply OutputCache to this action. this is usually the way doing global cache.
Here you are doing a POST to the server. Either directly put a element here or using a partial view to do the Post. And in the corresponding action, do a RedirectToAction.
Do it with ajax or not isn't the point. my opinion is more about the right way using RenderAction

Resources