In my MVC app, I need to update the values in a DropDownList after a post.
My view:
#{
IEnumerable<MyModel.MySelectItem> mylist= ViewBag.MyList;
}
#using (Ajax.BeginForm(...........)
{
#Html.DropDownList("myselection", mylist.ToSelectList(p => p.Description, p => p.Description), "Select Item")
....Other controls and a submit button are here...
}
My controller:
//Populate list in Index()
ViewBag.MyList = myGeneratedList;
return View();
When the view is initially displayed, the DropDownList is populated with the correct values.
Here is the post method:
public ActionResult GetData()
{
ActionResult result = null;
//Query data...
ViewBag.MyList = myNEWGeneratedList;
//Need to display a table of results and update DropDownList
var myTableResults = GetSomeData();
result = PartialView("_MyTableResultsView", myTableResults);
return results;
}
When the form posts back, the partial view displays it's results fine, but the DropDownList does NOT get updated.
How can I get the DropDownList in the view to update after a post?
Ajax.beginform will update a specific control based on the value of the UpdateTargetId parameter. If your dropdownlist is not within the target control, it will not be changed. If it is in the targeted control, it will be updated or overwritten with whatever is returned in your partial view.
If you want your dropdownlist to update outside of the returned partial view or UpdateTarget, you would most likely use javascript or jquery to update it client side.
Another option is to use HTML.beginform which will issue a full page postback (refresh everything), but your controller would need to be updated to return a view that contains the updated dropdownlist as well as the code in your current partial view.
Related
I have an ItemsController with an Index action returning and rendering items on the client like
return View(itemViewModels);
Each itemViewModel has some bootstrap tabs. In each tab a partialView is rendered.
When the user edits a partialView and send the data to the Tab1Controller how can I return the View for the whole itemViewModel showing validation errors for the one partial view inside that tab1?
I have made the View work requesting/responsing with the sent itemViewModel but then Only the single item`s html is returned not the full items html.
AND when I return the Index View to the ItemsViewModels then I can NOT return my passed itemViewModels to show the validation errors.
[HttpGet]
public ActionResult Index(ItemViewModel viewModel)
{
return View("ItemsViewModels", viewModel);
}
This code does not work because the View does not match to the viewmodel type.
But I need to show the Html of the ItemsViewModels with the invalid (data-val attributes) html of the single edited itemViewModel.
I can NOT post and return the ItemsViewModels because it has many other properties which would make the modelstate invalid...
Do you have any idea?
UPDATE
I am NOT allowed to use ajax else the problem would be done quickly... this is for a common website and the customer wants a postback no ajax/SPA behavior.
At the moment I get the whole items from the service again and render it but then every ItemViewModel`s html has an invalid e.g. textbox. But I want that only a certain ItemViewModel is invalid.
[HttpPost]
public virtual async Task<ActionResult> SaveLanguage(ItemViewModel itemViewModel)
{
var viewModels = GetItemsViewModelsFromSErvice();
viewModels.Items.ElementAt(0).Settings = itemViewModel;
return View(MVC.Test.Items.Views.Index,viewModels );
}
If you are forced to do a full postback, but each partial view contains just a form with the elements for that 1 item then you'll have to reconstruct the other items in your controller before returning.
Your controller method would be something like
public ActionResult Index()
{
var vm = GetAllItems(); //method to get all your items for first load
return View(vm);
}
[HttpPost]
public ActionResult Index(ItemViewModel viewModel)
{
//get the full list of items, and then replace just the altered one
var vm = GetAllItems(); // assume returns a list
var index = vm.FindIndex(x => x.ID == viewModel.ID);
vm[index] = viewModel;
//might have to rename items in the ModelState.Errors dictionary
//so they are associated with the correct item index.
//first get the list of errors. As viewModel is not a list for this method
//they will have keys like "PropertyName".
//For a listItem need renaming to something like "[#].PropertyName" (# = index)
var errs = from ms in ModelState
where ms.Value.Errors.Any()
let fieldKey = ms.Key
let errors = ms.Value.Errors
from error in errors
select new {fieldKey, error.ErrorMessage};
//clear the ModelState, and then re-add any model-errors with the renamed key
ModelState.Clear();
foreach(var item in errs)
{
ModelState.AddModelError(
String.Format("[{0}].{1}", index, item.fieldKey), item.ErrorMessage);
}
return View("ItemsViewModels", vm);
}
In addition you might need to rename your form elements so that the model binder treats them as list items after postback. I'm not 100% sure this is necessary though.
If you can use ajax this becomes neater...
It looks like your index Model is a List<ItemViewModel>
Then your Main view (Index.cshtml) would be something like..
#model List<ItemViewModel>
...
#for(int i = 0; i < Model.Count; i++)
{
<div id="#String.Format("partialContainer{0}", Model[i].ID)">
#Html.Partial("Partial1", Model[i])
</div>
}
(notice that the ID of the container div is something that we can reference as the ajax update target)
And then have your partial Views use the relevant partial Models
Eg Partial1.cshtml:
#model ItemViewModel
... etc
#using (Ajax.BeginForm("RefreshPartial", "Home", null, new AjaxOptions() {
UpdateTargetId = String.Format("partialContainer{0}", Model.ID), HttpMethod = "Post" }, null))
{
#Html.TextBoxFor(m => m.Property1);
#* form controls... *#
<input type="submit" value="Submit" />
}
So I have a Edit view with a partial view. In this partial view I have a selectlist (or dropdownlist) which values come from a ViewBag. In the control I include the selected value but it just does'nt work.
public ActionResult Edit(int id = 0)
{
Customer c = db.Customer.Find(id);
ViewBag.CustomerGlobalQuality = new SelectList(db.GlobalQuality, "Id", "Quality", c.Skill.GlobalQuality);
return View(c);
}
and in the PARTIAL VIEW I have:
#Html.DropDownList("CustomerGlobalQuality")
#Html.ValidationMessageFor(model => model.Skill.GlobalQuality)
what did I miss? It usually works with normal views, so why not with a partial?
if your logic doesn't need partial view, do not include it in this case. Use those lines of code inside your edit chtml view. In your case debug it and see what are you realy sending to drop down list. I see that you included dropdown list dana in edit view. If you use partial view you must pass object to that view in controler.
public ActionResult PartialDDLData()
{
ViewBag.CustomerGlobalQuality = new SelectList(db.GlobalQuality, "Id", "Quality", c.Skill.GlobalQuality);
return Partial("_nameOfView",ViewBag.CustomerGlobalQuality);
}
and make sure your partial view is accessible in shared or controller view folder.
I have an MVC 4 View that contains a form and a partial view. The main view contains information about a product and the partial view contains images of the product.
What I would like to do is to have the partial view contain it's own form which the images would be displayed on. If I submit this form to a controller action method, modify the model it's based on and then have the action method refresh the partial view, will just the partial view section of my main view change and not the main view itself? If not, is there a better way to do what I'm trying to do. I just want to update the partial view section of the main view.
If you want to update just the Partial View, you should retrieve the data using an AJAX call. That way you call a Controller that will return you the View (in this case Partial View) that you need. Example:
CSHTML:
<div id="myPartialViewDiv">
#{Html.RenderPartial("myPartialView", Model);}
</div>
JS:
searchAdmin = function () {
var URL = "myController/myAction";
$.get(URL, { "optionalArg": optionalArg }, function (data) {
$("#myPartialViewDiv").html(data);
})
}
myController:
public JsonResult myAction(string optionalArg)
{
// do something...
return this.Json(whatIwantToReturn.ToList(), JsonRequestBehavior.AllowGet);
}
I use MVC 3 in ASP.Net my web app is design with ViewModel and ViewModel builder.
I use a Builder class to populate some data in a ViewModel. In my case I have a Create View a DropDownList, with this code work fine. My problem is when trying to create an Edit View, I receive this error:
{"The ViewData item that has the key 'CandidateId' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'."}
My idea it would be to populate a DropDownList with some value but have pre-selected one as for databse record.
So How to display a DropDownList in a Edit View with selected a value from a DataBase?
VIEW
<div class="editor-label">
#Html.LabelFor(model => model.CandidateId)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.CandidateId, Model.CandidatesList, "None")
</div>
VIEW MODEL
public Nullable<int> CandidateId { get; set; }
public IEnumerable<SelectListItem> CandidatesList;
VIEW MODEL BUILDER
// We are creating the SelectListItem to be added to the ViewModel
eventEditVM.CandidatesList = serviceCandidate.GetCandidates().Select(x => new SelectListItem
{
Text = x.Nominative,
Value = x.CandidateId.ToString()
});
The reason for this error is because in your [HttpPost] action you forgot to repopulate the CandidatesList property on your view model from the database.
[HttpPost]
public ActionResult Edit(EventEditVM model)
{
if (ModelState.IsValid)
{
// the model is valid => do some processing here and redirect
// you don't need to repopulate the CandidatesList property in
// this case because we are redirecting away
return RedirectToAction("Success");
}
// there was a validation error =>
// we need to repopulate the `CandidatesList` property on the view model
// the same way we did in the GET action before passing this model
// back to the view
model.CandidatesList = serviceCandidate
.GetCandidates()
.Select(x => new SelectListItem
{
Text = x.Nominative,
Value = x.CandidateId.ToString()
});
return View(model);
}
Don't forget that only the selected value of the dropdown list is sent to the server when you submit the form. The CandidatesList collection property will be null inside your POST controller action because its values were never sent. So if you intend to redisplay the same view you need to initialize this property because your view depends on it.
I'm very new to web app development and I thought I would start with recent technology and so I'm trying to learn asp.net as-well as the MVC framework at once. This is probably a very simple question for you, MVC professionals.
My question is should a partial view have an associated action, and if so, does this action get invoked whenever a normal page uses RenderPartial() on the partial view?
While you can have an action that returns a partial view, you don't need an action to render a partial view. RenderPartial takes the partial view and renders it, using the given model and view data if supplied, into the current (parent) view.
You might want an action that returns a partial view if you are using AJAX to load/reload part of a page. In that case, returning the full view is not desired since you only want to reload part of the page. In this case you can have the action just return the partial view that corresponds to that section of the page.
Standard mechanism
Making use of partial view within a normal view (no action needed)
...some html...
<% Html.RenderPartial( "Partial", Model.PartialModel ); %>
...more html..
Ajax mechanism
Reloading part of a page via AJAX (note partial is rendered inline in initial page load)
...some html...
<div id="partial">
<% Html.RenderPartial( "Partial", Model.PartialModel ); %>
</div>
...more html...
<script type="text/javascript">
$(function() {
$('#someButton').click( function() {
$.ajax({
url: '/controller/action',
data: ...some data for action...,
dataType: 'html',
success: function(data) {
$('#partial').html(data);
},
...
});
});
});
</script>
Controller for AJAX
public ActionResult Action(...)
{
var model = ...
...
if (Request.IsAjaxRequest())
{
return PartialView( "Partial", model.PartialModel );
}
else
{
return View( model );
}
}
The accepted answer is completely correct, but I want to add that you can load your partial view using jQuery load. Less configuration needed, if you don't want to consider concurrency.
$("#Your-Container").load("/controller/action/id");
I was able to achieve something similar with this logic.
Within the .cshtml
#Html.Action("ActionMethodName", "ControllerName");
Within the controller
[Route("some-action")]
public ActionResult ActionMethodName()
{
var someModel = new SomeModel();
...
return PartialView("SomeView.cshtml", someModel);
}
And that's it.
If you need to pass values from the .cshtml to the action method then that is possible to.
The answer is no. But sometimes you need some controller action behind a partial view. Then you can create an actionMethod wich returns a partial view. This actionMethod can be called within another view:
#Html.Action("StockWarningsPartial", "Stores")
The actionmethod can look like:
public ActionResult StockWarningsPartial()
{
....
return View("StockWarningsPartial", warnings);
}
and the view 'StockWarningsPartial.cshtml' starts with:
#{
Layout = null;
}
to make it not render your surrounding layout again.
public ActionResult GetStateList(int country_id)
{
List<stateDTO> stateList = new List<stateDTO>();
stateList = bll.GetState(country_id);
ViewBag.sList = new SelectList(stateList, "state_id", "State_Name");
return PartialView("DisplayStates");
}