1

I'm new to MVC, but have done a bunch of reading the past few months, and I've ran into some issues when trying to build a one to many relationship dynamically on the page. I've set up the logic and can accomplish this through multiple page loads but I want the form to be more dynamic, so I'm not sure what the correct way to do this is.

For testing purposes I scaled down my Model just to see if I can get the interaction I want before I apply it to my project and what I have is this:

Generated models from my Db.

public partial class TestIncident
{
    public TestIncident()
    {
        this.TestGeoKits = new HashSet<TestGeoKit>();
    }

    public int RecordID { get; set; }
    public string OwnerFirstName { get; set; }
    public string OwnerMiddleName { get; set; }
    public string OwnerLastName { get; set; }

    public virtual ICollection<TestGeoKit> TestGeoKits { get; set; }
}

RecordID being my PKey for the class above

public partial class TestGeoKit
{
    public int RecordID { get; set; }
    public int KitID { get; set; }
    public bool SubmittedForTestingFlag { get; set; }
    public string SubmissionLocation{ get; set; }
    public Nullable<System.DateTime> SubmissionDate { get; set; }
    public bool Processed { get; set; }
    public Nullable<System.DateTime> ReturnDate { get; set; }
    public string Notes { get; set; }

    public virtual TestIncident TestIncident { get; set; }
}

RecordID being my FKey relation and KitID being my PKey for this table.

Now, I need to set up a Create View that contains both the Form associated with the TestIncident AND has the option to click an ADD button to populate Partials containing the Form associated with my TestGeoKits on the same page, instead of after a postback and redirect (How I currently have it). I've tried piecing information from multiple posts together to accomplish this, but one way or another I run into a bunch of problems and even more questions..

Logically I would think, being MVC uses Entity Framework, that on my Create TestIncident page I can have an ADD button that adds a TestGeoKit form to the page each time its clicked, in the form of a partial view. Then, when you click submit the entire Object is passed back to the Controller, binds all the values for the incident to the database, gets the next Identity Value (set up in sql) for RecordID, and uses that information for the ICollection of TestGeoKits in TestIncident to build the foreign key relationship and save all the data.

How do I go about building a dynamic page like this? I've read MANY articles, played with Ajax commands and jQuery form controls, played with LazyLoading / EagerLoading, and I think I'm over complicating it for myself at this point.

I have a, not so dynamic, version currently running that when you submit your TestIncident form, the Method called in the Controller checks for a GeoKitCollectedFlag after its saved and if its true redirects you to a separate page to fill out the GeoKit Form. But, again my ideal form is to combine these two actions on one page.

Any help would be greatly appreciated!

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Scarlety
  • 93
  • 7
  • 4
    You're thinking about the server side aspect of this too much, and ignoring Separation of Concerns (don't worry about your data layer and Entity Framework or lazy loading). You need to think about the client. What does the client need in order to accomplish this? Think about that, and the rest should fall into place. – mason Feb 06 '15 at 16:03

1 Answers1

1

Really the key thing is in the naming of your fields. MVC is actually pretty competent at constructing complex object graphs based on posted data from a form, as long as you simply follow its conventions. For enumerable properties, that's making sure your field names follow the form ListProperty[N].Property. So, for your TestGeoKit instances you should have input names like TestGeoKits[0].RecordID.

To ensure this happens with Razor generated inputs, you need to iterate over existing instances using for rather than foreach:

@for (var i = 0; i < Model.TestGeoKits.Count(); i++)
{
    @Html.HiddenFor(m => m.TestGeoKits[i].RecordID)
    <!-- etc -->
}

Adding new records dynamically obviously requires a JavaScript solution. There's many different ways you could handle this. You could construct the HTML manually via JavaScript. You could submit an AJAX request to some action that would return HTML for all the fields. You could use JavaScript to copy the HTML for an existing record on the page and replace the id and name attributes with the proper index.

Personally, I would recommend going with a framework geared towards handling data binding and templating. Knockout is a popular choice, but your could use things like Backbone, Angular, etc. The idea, simply, is that you want something where you can merely add a data object to an array in JavaScript, and have it generate the appropriate HTML for you with the right index for the names and such. Trying to keep up with this manually is kind of a nightmare.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Taking that approach I get a "Cannot apply indexing with [] to an expression of type "System.collections.Generic.ICollection" , which of course TestGeoKit is auto generated as an ICollection and the type ICollection interface does not declare an indexer. – Scarlety Feb 06 '15 at 21:04
  • @Scarlety. You need to change `TestGeoKits` to `IList` or use a custom `EditorTemplate` for typeof `TestGeoKit` (in any case you should be using a view model, not the data model for editing). In addition to Chris's suggestions, [this question/answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) gives some other solutions for dynamically adding/deleting items –  Feb 07 '15 at 01:43
  • It is autogenerated as an ICollection, any changes would be overwritten if I ever have to update my Model from the database. I'll look into Templating thought, thanks! – Scarlety Feb 09 '15 at 14:52