I was wondering what the best way to approach this problem in ASP.NET MVC would be. The following is a trivial example of what I'd like to be able to do:
I have a webpage with textbox and a submit button. When the submit button is pressed the I would like the contents to be displayed on the same webpage. When it is pressed again I would like what was already displayed from the first submission to be displayed as well as the new data that was just submitted.
I have tried saving this data to a model, but the model is wiped clean every time the form posts. How could I do this and keep the data from the post before the last one (and the post before that)?
If you want data to persist between requests, as a starting point I would use 'TempData'. The TempData property value is stored in session state and exists until it is read or until the Session expires.
Example ViewModel:
public class SomeClass
{
public string Something { get; set; }
public List<string> RetainedValues { get; set; }
}
Example Controller:
[HttpGet]
public ActionResult Index()
{
return View("Index");
}
[HttpPost]
public ActionResult Index(SomeClass postedValues)
{
// retrieve retained values
var retained = (List<string>) TempData["RetainedValues"] ?? new List<string>();
retained.Add(postedValues.Something);
// save for next post
TempData["RetainedValues"] = retained;
// setup viewmodel
var model = new SomeClass
{
RetainedValues = retained
};
return View("Index", model);
}
Example View (strongly typed):
<div>
#foreach(var item in Model.RetainedValues)
{
<div>#item</div>
}
</div>
#using(Html.BeginForm())
{
#Html.EditorFor(m=>m.Something)
<input type="submit"/>
}
Just put an hidden field for your model property then your previews value will be loaded on it and passed it back to the next post.
Ex.: #Html.HiddenFor(model => model.YourProperty)
So knowing that you could have two properties ex.: one named newValue and other called allValues.
the allValues you use it with an hidden field and your newValue you use to insert the new ones. So on post you just add the newValue to the allValues.
Something like that:
model.allValues += newValue;
--UPDATE
Or you can use session or tempdata as mentioned by #Jesse
For this case I would prefer to use hidden fields as it has a lower complexity and its data didnt need be secure as it will be shown to the user anyway.
Related
I want to show a Review page after the user fill sout all the information but on the review page I need to have a "Back" button which should take the user back tot he previous page with the same state and all the changes that they have made. What is the best way of achieving this ? I coded the review page on a different view but then the back is clicked the state of the page with all the changes I lost.
I have tried trying to code the view in another view page
The best way is that you support the parameters in your View:
For example:
public class MyClass
{
public IEnumerable<string> MyProperty { get; set; }
}
And in the Controller:
public ActionResult Index()
{
// Create your model and set the values
var myModel = new MyClass
{
MyProperty = new List<string> { "First Value", "Second Value" }
};
// Return the model back to your view for access using #Model
return View(myModel);
}
You have to generate your own model for the form completion, and then pass all the objects (Ideally just one with many attributes) from the review page to the form filling page and vice-versa.
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.
I've got a view that needs some checkboxes describing days of the week. I want to return back to the controller which boxes are selected. I found some help from various Google searches, etc, and here's what I have.
Section 1:
In the Controller:
vm.AllDays = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>().ToList();
In the View:
#foreach (var day in Model.AllDays) {
<input type="checkbox" name="days" value="#day"/> <label>#day</label><br />
}
And then in my post handler:
[HttpPost]
public ActionResult _AddEdit(SubscriptionDetailsModel vm, IEnumerable<string> days) {
//etc, etc, etc
}
This works, I get the selected days coming in on the days parameter. However, I got curious and changed the 'name' attribute on the checkboxes to the field in my model that I ultimately want these values to go to. In summary, my model for the view contains an object that has a List in it. That List is the collection I would like the selected values to end up in. But here's the relevant code.
Section 2
Model snippets:
public class SubscriptionDetailsModel {
public CronTabModel CrontabModel { get; set; }
//...
}
public class CronTabModel {
public List<DayOfWeek> On { get; set; }
}
Controller:
[HttpPost]
public ActionResult _AddEdit(SubscriptionDetailsModel vm) {
//etc, etc, etc
}
And, of course, view:
#foreach (var day in Model.AllDays) {
<input type="checkbox" name="On" value="#day"/> <label>#day</label><br />
}
Edit: The reason I was seeing confusing results is due to a typo elsewhere in my View.
Technically, the code in Section 1 is usable for my situation, I can do some string parsing, etc. Is it possible to make the Checkbox group populate from one collection (Model.AllDays) and return in a second collection (Model.CronTabModel.On)?
Model binding to a list is a bit awkward, but possible. MVC has an issue with gaps in indices, they did build in a helper to fix it. The hidden field lets MVC know about the gaps so it can bind effectively.
#for (var index =; index < Model.AllDays.Count(); index++) {
#Html.Hidden("AllDays.Index", index)
#Html.CheckBoxFor(m => Model.AllDays[Index], new { Value= Model.AllDays[index] }
<label>#Model.AllDays[Index]</label><br />
}
That should let you bind to a list of checkboxes that post their values back and are bound to a property called AllDays on your model.
If you want to change the name, you will need to change the name on your Model that you pass out. Doing it this way allows MVC to add the indexers onto the name, so you end up with
AllDays[0] = "Sunday"
AllDays[1] = "Monday"
Etc etc.
Some more reading about array binding http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Every example that I can find of an MVC4 app has the edit working on one row of data at a time. It displays all the rows of data with each row having an edit which takes you to another page and allows you to edit that one row.
What I would like to do is display all the data elements in rows and instead of having the user have to click EDIT on each row, all the rows' data points would already be in text boxes which the user can directly update. And there is just one SAVE on the page that would just save all the updates/edits at once.
How can I setup my MVC app to support that?
You can use an EditorTemplates for this. The below example shows the normal form posting example. You can ajaxify it if you need by using the serialize method and sending form values.
Assuming You need to Edit the List of Student Names for a course. So Let's create some viewmodels for that
public class Course
{
public int ID { set;get;}
public string CourseName { set;get;}
public List<Student> Students { set;get;}
public Course()
{
Students=new List<Student>();
}
}
public class Student
{
public int ID { set;get;}
public string FirstName { set;get;}
}
Now in your GET action method, you create an object of our view model, initialize the Students collection and send it to our strongly typed view.
public ActionResult StudentList()
{
Course courseVM=new Course();
courseVM.CourseName="Some course from your DB here";
//Hard coded for demo. You may replace this with DB data.
courseVM.Students.Add(new Student { ID=1, FirstName="Jon" });
courseVM.Students.Add(new Student { ID=2, FirstName="Scott" });
return View(courseVM);
}
Now Create a folder called EditorTemplates under Views/YourControllerName. Then create a new view under that called Student.cshtml with below content
#model Student
#{
Layout = null;
}
<tr>
<td>
#Html.HiddenFor(x => x.ID)
#Html.TextBoxFor(x => x.FirstName ) </td>
</tr>
Now in our main view (StudentList.cshtml), Use EditorTemplate HTML helper method to bring this view.
#model Course
<h2>#Model.CourseName</h2>
#using(Html.BeginForm())
{
<table>
#Html.EditorFor(x=>x.Students)
</table>
<input type="submit" id="btnSave" />
}
This will bring all the UI with each of your student name in a text box contained in a table row. Now when the form is posted, MVC model binding will have all text box value in the Students property of our viewmodel.
[HttpPost]
public ActionResult StudentList(Course model)
{
//check for model.Students collection for each student name.
//Save and redirect. (PRG pattern)
}
Ajaxified solution
If you want to Ajaxify this, you can listen for the submit button click, get the form and serialize it and send to the same post action method. Instead of redirecting after saving, you can return some JSON which indicates the status of the operation.
$(function(){
$("#btnSave").click(function(e){
e.preventDefault(); //prevent default form submit behaviour
$.post("#Url.Action("StudentList",YourcontrollerName")",
$(this).closest("form").serialize(),function(response){
//do something with the response from the action method
});
});
});
You just need to specify the right model, list of example, and send the ajax with have information on each row (element of the array), read it on the server side and update each element accordingly. For this goal you use Post request. Just pass the list of elements as a parameters into the controller and pass it using the ajax.
For example you controller could be defined as:
public ActionResult Update(List<MyEntity> list)
{
...
}
public class MyEntity
{
public string Name {get; set;}
public int Count {get; set;}
}
and JavaScript could be as:
var myList = new Array();
// fill the list up or do something with it.
$.ajax(
{
url: "/Update/",
type: "POST",
data: {list: myList}
}
);
And of course your "Save" button has click event handler that will call that functionality with the ajax call.
For your convenience you can consider using KnockoutJS or other MVVM frameworks to bind the data with the DOM on the client side.
How to write a code for displaying the alert message: "Successfully registered", after user data is stored in database, using MVC
I am using Asp.Net MVC3, C#, Entity Model.
Try using TempData:
public ActionResult Create(FormCollection collection) {
...
TempData["notice"] = "Successfully registered";
return RedirectToAction("Index");
...
}
Then, in your Index view, or master page, etc., you can do this:
<% if (TempData["notice"] != null) { %>
<p><%= Html.Encode(TempData["notice"]) %></p>
<% } %>
Or, in a Razor view:
#if (TempData["notice"] != null) {
<p>#TempData["notice"]</p>
}
Quote from MSDN (page no longer exists as of 2014, archived copy here):
An action method can store data in the controller's TempDataDictionary object before it calls the controller's RedirectToAction method to invoke the next action. The TempData property value is stored in session state. Any action method that is called after the TempDataDictionary value is set can get values from the object and then process or display them. The value of TempData persists until it is read or until the session times out. Persisting TempData in this way enables scenarios such as redirection, because the values in TempData are available beyond a single request.
The 'best' way to do this would be to set a property on a view object once the update is successful. You can then access this property in the view and inform the user accordingly.
Having said that it would be possible to trigger an alert from the controller code by doing something like this -
public ActionResult ActionName(PostBackData postbackdata)
{
//your DB code
return new JavascriptResult { Script = "alert('Successfully registered');" };
}
You can find further info in this question - How to display "Message box" using MVC3 controller
Personally I'd go with AJAX.
If you cannot switch to #Ajax... helpers, I suggest you to add a couple of properties in your model
public bool TriggerOnLoad { get; set; }
public string TriggerOnLoadMessage { get; set: }
Change your view to a strongly typed Model via
#using MyModel
Before returning the View, in case of successfull creation do something like
MyModel model = new MyModel();
model.TriggerOnLoad = true;
model.TriggerOnLoadMessage = "Object successfully created!";
return View ("Add", model);
then in your view, add this
#{
if (model.TriggerOnLoad) {
<text>
<script type="text/javascript">
alert('#Model.TriggerOnLoadMessage');
</script>
</text>
}
}
Of course inside the tag you can choose to do anything you want, event declare a jQuery ready function:
$(document).ready(function () {
alert('#Model.TriggerOnLoadMessage');
});
Please remember to reset the Model properties upon successfully alert emission.
Another nice thing about MVC is that you can actually define an EditorTemplate for all this, and then use it in your view via:
#Html.EditorFor (m => m.TriggerOnLoadMessage)
But in case you want to build up such a thing, maybe it's better to define your own C# class:
class ClientMessageNotification {
public bool TriggerOnLoad { get; set; }
public string TriggerOnLoadMessage { get; set: }
}
and add a ClientMessageNotification property in your model. Then write EditorTemplate / DisplayTemplate for the ClientMessageNotification class and you're done. Nice, clean, and reusable.
Little Edit
Try adding
return new JavascriptResult() { Script = "alert('Successfully registered');" };
in place of
return RedirectToAction("Index");