There are two one-to-many models

Bid model

public class Bid { public int Id { get; set; } public string Name { get; set; } public virtual ICollection Documents { get; set; } } 

Document Model

 public class Document { public int Id { get; set; } public string Name { get; set; } public string Url { get; set; } public int? BidId { get; set; } public virtual Bid Bid { get; set; } } 

I need to implement a page with the addition of the application (Bid), on which it is possible to add documents right away, for example, using a modal bootstrap (pop-up window).

Tell me, please, how can I implement a controller that could add documents to an application that has not yet been added.

PS I can implement this thing using jQuery, which generates the markup, and the model binder, but this approach is poorly followed and difficult to test. There must be another way :)

What I do:
I create EditorTemplates for Bid and Document

Bid

 @model Intrasite.Domain.Entities.Bid @Html.HiddenFor(model=>model.Id) @Html.LabelFor(model=>model.Name) @Html.EditorFor(model=>model.Name) @Html.EditorFor(model=>model.Documents) 
Document

 @model Intrasite.Domain.Entities.Document @Html.HiddenFor(model => model.Id) @Html.LabelFor(model => model.Name) @Html.EditorFor(model => model.Name) 

I make a View to create an application:

 @using (Html.BeginForm("Create", "Home")) { @Html.EditorForModel() a href="" data-toggle="modal" data-target="#myModal">Добавить документ /a div class="modal fade" id="myModal" ... @Html.EditorFor(model => model.Documents) ... /div input type="submit" value="Создать заявку" / } 

And then the problems begin. I cannot add separately, through separate methods, documents to the application, since it has not been added yet. I have a controller with a method

 public ActionResult Create() { var bid = new Bid() { Documents = new List() {new Document()} }; return View(bid); } [HttpPost] public ActionResult Create(Bid bid) { if (!ModelState.IsValid) return View(bid); repository.CreateBid(bid); return View(bid); } 

Since I submit the application with the created document Documents = new List() {new Document()} , accordingly, I can fill only one document. I also created a method

 [HttpPost] public ActionResult CreateDocument(Bid bid) { bid.Documents.Add(new Document()); return View("Create", bid); } 

Which allowed me to add a lot of documents, but everything is crooked.

I want to know how smart people still implement such connections

  • Specify at what point the difficulty arises. For example: "That's about when the application is already there and everything is added, but the application is only being created and therefore does not work. You can do it like this, but I don’t like it." Because Now the essence of the problem is not obvious, and the efforts made are not visible, and the question looks like "write me the necessary code." - Petr Abdulin

1 answer 1

In this particular case, you should not physically create an application until the necessary documents have been added to it. The pop-up window that is used to add a document should not send a request to attach the document to the database.

Instead, you should send it to the method that will build a piece of the form (HTML form ) for this question, and return it via AJAX to the main page.

In detail, this process looks like this:

  1. On the main form, which is the most common form, you have a button to add a document. When you click on this button, a pop-up window appears with a form for adding a document. As far as I can see, there will be just a text box where you can enter the URL. The appearance of a pop-up window is implemented by means of JavaScript.

  2. Sending the second form (in a pop-up window) must be intercepted. Instead of sending a regular request, you should send an AJAX request to one of your controller shares:

Let's look at the promotion code:

 public ActionResult AjaxCreateDocument(string url) { return PartialView("CreateDocumentView", url); } 

CreateDocumentView.cshtml

 @model string @Html.Hidden("documents", Model) 
  1. As a result, an AJAX request will return a piece of HTML code like <input type="hidden" name="documents" value="введённый пользователем URL" /> . You need to add it to the main form, for example, using jQuery tools.

  2. And only when the main form is filled, it can be sent to save.

The scheme will work at the expense of the correct name of the hidden-field, i.e. documents . When an ASP.NET MVC binder encounters fields with the same name, it can bind all of them to a collection. In your object there is just such a field ICollection<Document> .

For more manageability, I would advise explicitly separate models of different levels. You have the same classes used for displaying data, and for storing them in the database, and for processing them at the business logic level. Because of this, in particular, you need to manage the fields Id , BidId , etc. that you do not really need.

Secondly, if there are difficulties with binding, look at this (classic) post . It will help if the simple name of the documents does not work.

  • Thank you so much for the advice! Now try to implement this idea. Only, nevertheless, it turns out such connections are made by jquery, by generating the html form and, subsequently, transferring it to the controller through the binder of the models. For example, if there are a lot of such links in the project, then it will take a lot of time for that. - Alexander Sankov
  • All the same can be done without AJAX, it will just look "like in the old web." It is important here that the data is not sent immediately to the database, but first it’s spinning for a while between the client and the server. And of course, while they are spinning, they come from the server as generated HTML-form, and from the client as filled fields in this form. - Mark Shevchenko