0

Building a user input form that can take many objects (viewmodel contains lists of other objects) that can be added and deleted (those added will be displayed in a list coming from a partial page), but I don't want the objects to be sent to the database until the final submit where all objects will be sent to the database at once.

This is to stop unnecessary additions to the database if a user leaves the form after adding some objects.

What's the best way of achieving this? I was thinking storing the model in a session updated by the controller - is this advisable?

Example Model:

public class ViewModel
{
    public string SchoolName {get;set;}
    public List<Student> student {get;set;}
    public List<Course> courses {get;set;}
}

public class Student
{
    public string Name {get;set;}
}

public class Course
{
    public string Code {get;set;}
}
SelrekJohn
  • 476
  • 2
  • 6
  • 21
  • What do you mean _"until the final submit"_? –  Apr 27 '15 at 11:02
  • I've updated the question, sorry about that, wasn't clear. So the user can add as many objects as they want before submitting to the database. – SelrekJohn Apr 27 '15 at 11:05
  • Do you mean dynamically add new `Student` and `Course` objects? Why not just create then in the one view and submit in one action? –  Apr 27 '15 at 11:07
  • Tried that before but couldn't get it to work, I've got the different Models in PartialViews - so in this instance, School has PVs of Course and Student which are controlled in their respective PV so that it appears as one page to the user. – SelrekJohn Apr 27 '15 at 11:10
  • It will only work with partials if you are using the `BeginCollectionItem` helper, or you can do it all client side as shown in [this answer](http://stackoverflow.com/questions/29837547/set-class-validation-for-dynamic-textbox-in-a-table/29838689#29838689) –  Apr 27 '15 at 11:11
  • I used a similar method to [this](http://stackoverflow.com/questions/29700557/mvc-5-dynamic-rows-with-beginitemcollection/29849988#29849988) answer - which works well if I want to store in the DB as I go, but I don't, can I not use the controller to store/manage the data until the final submit? I couldn't get BeginCollectionItem to work so used partials when I asked about it [here](http://stackoverflow.com/questions/29774822/mvc-5-begincollectionitem-with-partial-crud) – SelrekJohn Apr 27 '15 at 11:19
  • If `BeginCollectionItem` did not work for you then your making a mistake in your code. I will have a look at the linked question a bit later. –  Apr 27 '15 at 11:36
  • Yes I was making a mistake in the code for BCI but couldn't solve it so reverted to MVC, VM, and PV. I'll have another look at it. Which is was the second link is there, if you can show me where I'm going wrong on that then that'll answer both my q's, 2 in 1. Thanks for your help. – SelrekJohn Apr 27 '15 at 11:38
  • the latest thinking would be to persist on the client, probably in javascript json object, which is then submitted via ajax. this gives you the most responsive solution, although implementing the builing logic in javascript can be a pain – Ewan Apr 27 '15 at 13:11
  • Thanks @Ewan I'll try a more JS related solution next time, I'm not used to JS as such but there's a bit in the BeginCollectionItem I'm struggling through with - just the delete function to work left. – SelrekJohn Apr 27 '15 at 13:13

2 Answers2

2

I have used Sessions to do this and support the models between pages, reducing the need to go back to the database each time, also helps with adding lists.

With your student model in mind, I would suggest something like the below controller, you could potentially have one session, but it could get complex; I'd be more tempted to have two sessions, one for student and one for course.

    public ActionResult Index()
            {
                if (Session["StudentSession"] != null)
                {
                    List<Student> Students = (List<Person>)Session["StudentSession"];
                    // do something
                }
                else // create a new session so you can do w/e
                {
                    List<Student> Students = new List<Student>();
                    Session["StudentSession"] = Students;
                }
                return View();
            }
PurpleSmurph
  • 2,055
  • 3
  • 32
  • 52
0

Its not advisable to store an object graph in memory using the MVC pattern. Your mention of storing data in session is one possible way of achieving what you need however you can probably simplify your model and directly persist your data on the client and in post data. This will negate the issue of multiple open tabs, exiting mid way through a workflow etc...

[HttpGet]
public ActionResult MyForm()
{
    var model = new MyFormModel();
    [...]
    return View(model);
}

[HttpPost]
public ActionResult MyForm(MyFormModel model)
{
    if (model.AddStudent)
    {
        // do some add student logic
    }

    if (model.AddCourse)
    {
        // add course logic
    }

    if (model.Submit)
    {
        // perform save
        return RedirectToAction("SomewhereElse");
    }

    return View(model);
}

View

<form method="post">

    [...]

    <button type="submit" name="AddStudent" value="True">Add Student</button>

    <button type="submit" name="AddCourse" value="True">Add Course</button>

    <button type="submit" name="Submit" value="True">Save</button>

</form>

Model

public class ViewModel
{
    [...]
    public bool AddStudent { get; set; }
    public bool AddCourse { get; set; }
    public bool Submit { get; set; }
}

This technique should continue to re-render the view and persist your data in-page until you click Save.

Dan
  • 968
  • 1
  • 8
  • 21