In my MVC controller I have two action methods.
The first one is Index method:
public IActionResult Index()
{
return PopulateViewModel();
}
The "PopulateViewModel" Action Method is used for updating of the view model and then showing these updated values on the Index view.
public IActionResult PopulateViewModel()
{
ViewModel viewModel = new ViewModel()
{
//updating values in the view model
//the values are received when the form in the view is submitted
};
return View("Index", viewModel);
}
The problem that I have is that on my Index view the updated values are not shown immediately after submitting the form in the view. When I submit the form I must then once again refresh the page to see the updated values.
What could be the reason for such behavior and how can I correct that?
You misunderstand the conceptual notion. The index is supposed to represent the initial page state. Other actions within the controller will modify the output by rendering the page with the adjusted model. Or handling server side model binding, but the concept is fundamentally achieving the same result.
Your controller logic should be within the following constraints.
public class SampleController : Controller
{
public IActionResult Index() => new View("...", ...);
public IActionResult SubmitSample(string location)
{
var model = service.GetLabLocations(location);
return View("...", model);
}
}
The index is simulating a GET request, returning the initial page in the required state. The form portion, should POST data, outlined in the SubmitSample portion of the code. This will change the state of the page, but the server will need to render with those changes. So the page will load with the attached model, for you to display.
This would represent Razor more than likely on the server side.
#if(Model != null)
foreach(var sample in Model)
{
// Markup, with the data
}
Related
I have a ASP.NET MVC wrapper class that wraps a List containing ViewModels:
public class ScheduleContainerViewModel
{
public List<SchedulingViewModel> Items { get; set; }
public ScheduleContainerViewModel(List<SchedulingViewModel> items)
{
Items = items;
}
SchedulingViewModel contains a Schedule object and a Users object.
ScheduleContainerViewModel is eventually passed to a PartialView.
This PartialView is called via Ajax to populate a div in a View. The model for the PartialView is ScheduleContainerViewModel and the model for the View is a Job.
The Ajax GET works great. When I try to POST it, however, the Controller is never called and I get a 500 server error. Here's some of the XHR data being POSTed:
It seems to me that List Items is being passed, containing SchedulingViewModels and their associated Schedules, but maybe not?
There are three relevant methods in the Controller. Here are their signatures:
public ActionResult ScheduleJob(int? id)
public ActionResult Table(int? id)
[HttpPost]
public ActionResult Table(ScheduleContainerViewModel cvm)
ScheduleJob loads the initial View, which calls [HttpGet] Table to load the PartialView. Finally, [HttpPost] Table is the one being POSTed to. Why can't the model binder see the data being passed to it in the XHR?
My only guess it that it's because the model used by the View called by ScheduleJob is different than the one being POSTed to [HttpPost] Table.
I tried to do a CRUD operation. Operation is that if user clicks on Edit from grid then data should be shown in the control that is below the grid. It means, grid and controls are in same page. Below is the code:
#Html.ActionLink("Edit", "Update", new { id=item.Country_ID })
In controller :
[HttpPost]
public ActionResult Update(TBL_Country tbl_country)
{
if (ModelState.IsValid)
{
db.Entry(tbl_country).State = EntityState.Modified;
db.SaveChanges();
}
return View(tbl_country);
}
But, it shows an error stating :
It does not have any views. How can I give the same page view again?
I am a beginner in MVC4.
You only need to write the View path, something like this
return View( "~/Views/ControllerName/ViewName.cshtml", tbl_country);
I have models (POCO entities) like Student, Course, Standard etc. I have corresponding controllers such as StudentController etc. I have a view Index for each model which displays the list of all the corresponding entities in DB. For example, StudentController.Index() returns the /Student/Index view. However, if there are no Student records in the DB, instead of returning the Index view , I redirect to the Empty action method of the Navigation controller, i.e. NavigationController.Empty(), which returns the /Navigation/Empty view. This is done for all model entity classes.
Now, on the empty page, I wish to have a hyperlink to go back to the previous page. So I created an action method called GoBack() in the NavigationController class, in which I redirect to the previous view. But how can I access the information about what the previous page was in this action method? Or is there a better way to do this? I do not want to use the back button.
As far as I'm concerned there are a couple of routes to take here. You could use sessions or the application cache to store a las visited page, and then get that page (by storing a route for instance) in the GoBack() action using a RedirectToAction.
But maybe a nicer and stateless aproach would be to render the hyperlink by having a view model having two properties for last used controller & action. Then you could pass these from the action result calling the /Navigation/Empty action (when there aren't any records).
ViewModel
public class NavigationVM
{
public string LastAction {get;set;}
public string LastController {get;set;}
}
Navigation Controller Action
public ActionResult Empty(string lastAction, string lastController)
{
var vm = new NavigationVM()
{
LastAction = lastAction,
LastController = lastController
}
return View(vm);
}
View
#model = Namespace.NavigationVM
#Html.ActionLink("LinkName", Model.LastAction, Model.LastController)
EDIT
If you then need to find out from where the students controller was called (in your example) you can go about this the same way. I.e.: Render the link to the StudentsController with extra route values.
StudentController:
public ActionResult Index(string lastAction, string lastController)
{
.... // no students
return RedirectToAction("Empty", "Navigation", new RouteValueDictionary(new { lastAction = "Index", lastController= "Student"}));
}
View with hyperlink to students controller (use the action and controller that rendered this view as lastAction and lastController respectively):
#Html.ActionLink("Get students", "Index", "Student", new { lastAction= "Index", lastController = "CallingController" }, null)
In an MVC4 project I need to "refresh" the page depending on some messages that can be present, otherwise I just redirect to a page, and if presenting again the page if them messages are present I would like to avoid just returning the View as it will cause then the double submission when the user tries to refresh it.
What I'm trying to do is this
[HttpGet]
public ActionResult SampleMethod()
{
viewModel = _builder.Build();
return View(viewModel);
}
[HttpPost]
public void SampleMethod(SampleViewModel viewModel)
{
if (ModelState.IsValid)
{
var response = serviceCall;
var errorMessages = response.ErrorMessages;
if (!errorMessages.Any())
{
//Redirect to proper view
}
else
vm = _builder.Build();
}
else vm = _builder.Build(); //There is some validation error I rebuild
CashbackOffersConfirmation(vm);
}
public ActionResult SampleMethodConfirmation(SampleViewModel viewModel)
{
return View("SampleMethod", viewModel);
}
It goes through the process
but the final page is .../SampleMethod instead of .../SampleMethodConfirmation and is blank,
Is this something to do with the routing (quite lost in this)? Is this a correct approach?
Thanks
In order to pass the object model from the view to the controller, you need to make a post request. Make sure you use a form that will generate the post request.
Also make the SampleMethodConfirmation method a post.
E.g.: add [HttpPost] on top of the method in the controller
In MVC4:
I have the following property in my model used for a dropdown list:
public SelectList Subjects { get; set; }
I set the Subjects property in my Index() Action on page load and return the model.
The dropdown gets populated just fine with the SelectListItems.
#Html.DropDownListFor(x => x.Subject, new SelectList(Model.Subjects, "Text", "Text", "Other"))
When I submit the form the Subjects SelectList in the model has changed to null. There has to be a simple way to persist this on HttpPost. I assume I want to submit and post this SelectList as well, along with all the form fields? How would I do this?
It is commonly accepted that you re-populate a SelectList after the Post action. Just extract it inside a method and call it in the Get and Post action.
Posting it back again to the controller is not the way to go. You can cache the items in the SelectList so you won't have to make a query to the data store twice.
Example:
public ActionResult Create()
{
var model = new SubjectModel();
PopulateSubjectList(model);
return View(model);
}
[HttpPost]
public ActionResult Create(SubjectModel model)
{
if (ModelState.IsValid)
{
// Save item..
}
// Something went wrong.
PopulateSubjectList(model);
return View(model);
}
private void PopulateSubjectList(SubjectModel model)
{
if (MemoryCache.Default.Contains("SubjectList"))
{
// The SubjectList already exists in the cache,
model.Subjects = (List<Subject>)MemoryCache.Default.Get("SubjectList");
}
else
{
// The select list does not yet exists in the cache, fetch items from the data store.
List<Subject> selectList = _db.Subjects.ToList();
// Cache the list in memory for 15 minutes.
MemoryCache.Default.Add("SubjectList", selectList, DateTime.Now.AddMinutes(15));
model.Subjects = selectList;
}
}
Note: MemoryCache uses the System.Runtime.Caching namespace. See: System.Runtime.Caching namespace.
Also, caching should be in a seperate layer between your controller (or business layer) and the data access layer, this is just for clarity.
Browsers only post back the selected values on the form elements. Also, its not a good idea to post back the values which can be retrieved from the data store. You would have to pull the items in the list just like you did while populating the list.
Also, MVC does not maintain the state of the page like .NET webpages as it does not have a view state. Developers are fully responsible for managing states of pages between the post backs, which is the essence of MVC design pattern.