2015-04-17 13 views
5

Qual è il modo migliore per aggiungere/eliminare righe a una tabella quando si fa clic su un pulsante? Ho bisogno di righe create da proprietà ChildClass (la classe figlio è una lista all'interno della classe/modello principale).MVC 5 Dynamic Rows con BeginCollectionItem

Attualmente dispone di una vista (il modello è MyMain) che fa riferimento a una vista parziale utilizzando RenderPartial.

La vista parziale mostra le proprietà del modello, una classe denominata MyChild che è un elenco di oggetti in MyMain.

Desidero aggiungere ed eliminare pulsanti per aggiungere dinamicamente le righe trattenute nella vista parziale.

Quindi aggiungere MyChild ripetutamente per più righe nell'elenco. È possibile? O non dovrei usare viste parziali per questo?

codice aggiornato

Qui di seguito sono le classi attuali e punti di vista con cui sto lavorando, sto cercando di implementare l'helper BeginCollectionItem ma sto ottenendo rif nullo dove sto cercando di caricare il vista parziale nonostante la dichiarazione if che dice di creare una nuova istanza della classe figlia se non esiste - perché viene ignorata?

Vista principale

@using (Html.BeginForm()) 
{ 
    <table> 
     <tr> 
      <th>MyMain First</th> 
      <th>MyChild First</th> 
     </tr> 
     <tr> 
      <td> 
       @Html.EditorFor(m => m.First) 
      </td> 
      <td> 
       @if (Model.child != null) 
       { 
        for (int i = 0; i < Model.child.Count; i++) 
        { 
         Html.RenderPartial("MyChildView"); 
        } 
       }   
       else 
       { 
        Html.RenderPartial("MyChildView", new MvcTest.Models.MyChild()); 
       }  
      </td> 
     </tr> 
     @Html.ActionLink("Add another", "Add", null, new { id = "addItem" }) 
    </table> 
} 

Vista parziale

@model MvcTest.Models.MyChild 

@using (Html.BeginCollectionItem("myChildren")) 
{ 
    Html.EditorFor(m => m.Second); 
} 

modelle

public class MyMain 
{ 
    [Key] 
    public int Id { get; set; } 
    public string First { get; set; } 
    public List<MyChild> child { get; set; } 
} 

public class MyChild 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Second { get; set; } 
} 

regolatore

public class MyMainsController : Controller 
{ 
    // GET: MyMains 
    public ActionResult MyMainView() 
    { 
     return View(); 
    } 

    [HttpPost] 
    public ActionResult MyMainView(IEnumerable<MyChild> myChildren) 
    { 
     return View("MyMainView", myChildren); 
    } 

    public ViewResult Add() 
    { 
     return View("MyChildView", new MyChild()); 
    } 
} 
+3

Alcune opzioni per aggiungere in modo dinamico e l'eliminazione di oggetti [qui] (http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796# 29161796) e [qui] (http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) –

+0

Qualcosa del genere: https: //github.com/danludwig/BeginCollectionItem –

+0

Grazie @StephenMuecke Ho appena esaminato ciò che mi hai suggerito: BeginCollectionItem è menzionato in una delle risposte nel modo migliore? Sembra estremamente confuso. Fondamentalmente voglio essere in grado di aggiungere le proprietà del modello figlio come una riga come vuole il cliente (ci sarà un'ulteriore convalida, ma in un secondo momento). Utilizzare una vista parziale è il modo migliore per raggiungere questo obiettivo? Saluti per il tuo aiuto. – PurpleSmurph

risposta

8

risposta Aggiornato - Il codice originale è NON fedele alla 'dinamica', ma permette per tutto quello che ho bisogno di fare entro i parametri di domanda.

Inizialmente, non riuscivo a ottenere il suggerimento di Stephen's BCI nei commenti delle domande di lavoro, da allora ho ed è geniale.Il codice riportato di seguito nella sezione aggiornata funzionerà se si copia + incolla, ma sarà necessario scaricare manualmente BCI da GIT o utilizzare PM> Install-Package BeginCollectionItem con la console di Gestione pacchetti in Visual Studio.

Ho avuto qualche problema con vari punti utilizzando BCI a causa della complessità e non avendo fatto prima MVC - here è più informazioni su come affrontare l'accesso class.property(type class).property(type class).property.

Risposta originale - Ho seguito un esempio più chiaro di quello della domanda, che è diventato troppo confuso.

Utilizzo di due viste parziali, una per l'elenco di dipendenti e un'altra per la creazione di un nuovo dipendente, tutte contenute nel viewmodel di companyemployee che contiene un oggetto di società e un elenco di oggetti dipendenti. In questo modo è possibile aggiungere, modificare o eliminare più dipendenti dall'elenco.

Speriamo che questa risposta possa aiutare chiunque cerchi qualcosa di simile, questo dovrebbe fornire abbastanza codice per farlo funzionare e per lo meno spingerti nella giusta direzione.

Ho omesso le mie classi di contesto e inizializzazione perché sono solo true per il codice prima, se necessario posso aggiungerle.

Grazie a tutti coloro che hanno aiutato.

modelle - CompanyEmployee essere il modello di vista

public class Company 
{ 
    [Key] 
    public int id { get; set; } 
    [Required] 
    public string name { get; set; } 
} 

public class Employee 
{ 
    [Key] 
    public int id { get; set; } 
    [Required] 
    public string name { get; set; } 
    [Required] 
    public string jobtitle { get; set; } 
    [Required] 
    public string number { get; set; } 
    [Required] 
    public string address { get; set; } 
} 

public class CompanyEmployee 
{ 
    public Company company { get; set; } 
    public List<Employee> employees { get; set; } 
} 

Indice

@model MMV.Models.CompanyEmployee 
@{ 
    ViewBag.Title = "Index"; 
    Layout = "~/Views/Shared/_Layout.cshtml"; 
} 

<h2>Index</h2> 
<fieldset> 
    <legend>Company</legend> 
    <table class="table"> 
     <tr> 
      <th>@Html.LabelFor(m => m.company.name)</th> 
     </tr> 
     <tr> 
      <td>@Html.EditorFor(m => m.company.name)</td> 
     </tr> 
    </table> 
</fieldset> 
<fieldset> 
    <legend>Employees</legend> 

     @{Html.RenderPartial("_employeeList", Model.employees);} 

</fieldset> 
<fieldset> 
    @{Html.RenderPartial("_employee", new MMV.Models.Employee());} 
</fieldset> 
<div class="form-group"> 
    <div class="col-md-offset-2 col-md-10"> 
     <input type="submit" value="Submit" class="btn btn-default" /> 
    </div> 
</div> 

PartialView di elenco di dipendenti

@model IEnumerable<MMV.Models.Employee> 
@using (Html.BeginForm("Employees")) 
{ 
    <table class="table"> 
     <tr> 
      <th> 
       Name 
      </th> 
      <th> 
       Job Title 
      </th> 
      <th> 
       Number 
      </th> 
      <th> 
       Address 
      </th> 
      <th></th> 
     </tr> 
     @foreach (var emp in Model) 
     { 
      <tr> 
       <td> 
        @Html.DisplayFor(modelItem => emp.name) 
       </td> 
       <td> 
        @Html.DisplayFor(modelItem => emp.jobtitle) 
       </td> 
       <td> 
        @Html.DisplayFor(modelItem => emp.number) 
       </td> 
       <td> 
        @Html.DisplayFor(modelItem => emp.address) 
       </td> 
       <td> 
        <input type="submit" formaction="/Employees/Edit/@emp.id" value="Edit"/> 
        <input type="submit"formaction="/Employees/Delete/@emp.id" value="Remove"/> 
       </td> 
      </tr> 
     } 
    </table> 
} 

Vista parziale Creare dipendenti

@model MMV.Models.Employee 

@using (Html.BeginForm("Create","Employees")) 
{ 
    <table class="table"> 

     @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 

     <tr> 
      <td> 
       @Html.EditorFor(model => model.name) 
       @Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" }) 
      </td> 
      <td> 
       @Html.EditorFor(model => model.jobtitle) 
       @Html.ValidationMessageFor(model => model.jobtitle) 
      </td> 
      <td> 
       @Html.EditorFor(model => model.number) 
       @Html.ValidationMessageFor(model => model.number, "", new { @class = "text-danger" }) 
      </td> 
      <td> 
       @Html.EditorFor(model => model.address) 
       @Html.ValidationMessageFor(model => model.address, "", new { @class = "text-danger" }) 
      </td> 
     </tr> 
    </table> 
    <div class="form-group"> 
     <div class="col-md-offset-2 col-md-10"> 
      <input type="submit" value="Create" class="btn btn-default" /> 
     </div> 
    </div> 
} 

controller - ho usato multipla ma li si può mettere in una

public class CompanyEmployeeController : Controller 
{ 
    private MyContext db = new MyContext(); 

    // GET: CompanyEmployee 
    public ActionResult Index() 
    { 
     var newCompanyEmployee = new CompanyEmployee(); 
     newCompanyEmployee.employees = db.EmployeeContext.ToList(); 
     return View(newCompanyEmployee); 
    } 

    [HttpPost, ActionName("Delete")] 
    public ActionResult DeleteConfirmed(int id) 
    { 
     Employee employee = db.EmployeeContext.Find(id); 
     db.EmployeeContext.Remove(employee); 
     db.SaveChanges(); 
     return RedirectToAction("Index", "CompanyEmployee"); 
    } 

    [HttpPost] 
    public ActionResult Create([Bind(Include = "id,name,jobtitle,number,address")] Employee employee) 
    { 
     if (ModelState.IsValid) 
     { 
      db.EmployeeContext.Add(employee); 
      db.SaveChanges(); 
      return RedirectToAction("Index", "CompanyEmployee"); 
     } 

     return View(employee); 
    } 
} 

codice aggiornato - con BeginCollectionItem - dinamica add/delete

Studente parziale

@model UsefulCode.Models.Person 
<div class="editorRow"> 
    @using (Html.BeginCollectionItem("students")) 
    { 
     <div class="ui-grid-c ui-responsive"> 
      <div class="ui-block-a"> 
       <span> 
        @Html.TextBoxFor(m => m.firstName) 
       </span> 
      </div> 
      <div class="ui-block-b"> 
       <span> 
        @Html.TextBoxFor(m => m.lastName) 
       </span> 
      </div> 
      <div class="ui-block-c"> 
       <span> 
        <span class="dltBtn"> 
         <a href="#" class="deleteRow">X</a> 
        </span> 
       </span> 
      </div> 
     </div> 
    } 
</div> 

Maestro parziale

@model UsefulCode.Models.Person 
<div class="editorRow"> 
    @using (Html.BeginCollectionItem("teachers")) 
    { 
     <div class="ui-grid-c ui-responsive"> 
      <div class="ui-block-a"> 
       <span> 
        @Html.TextBoxFor(m => m.firstName) 
       </span> 
      </div> 
      <div class="ui-block-b"> 
       <span> 
        @Html.TextBoxFor(m => m.lastName) 
       </span> 
      </div> 
      <div class="ui-block-c"> 
       <span> 
        <span class="dltBtn"> 
         <a href="#" class="deleteRow">X</a> 
        </span> 
       </span> 
      </div> 
     </div> 
    } 
</div> 

Registrati controller

public ActionResult Index() 
{ 
    var register = new Register 
    { 
     students = new List<Person> 
     { 
      new Person { firstName = "", lastName = "" } 
     }, 
     teachers = new List<Person> 
     { 
      new Person { lastName = "", firstName = "" } 
     } 
    }; 

    return View(register); 
} 

Register e persona Modello

public class Register 
{ 
    public int id { get; set; } 
    public List<Person> teachers { get; set; } 
    public List<Person> students { get; set; } 
} 

public class Person 
{ 
    public int id { get; set; } 
    public string firstName { get; set; } 
    public string lastName { get; set; } 
} 

Indice

@{ 
    Layout = "~/Views/Shared/_Layout.cshtml"; 
} 
@model UsefulCode.Models.Register 
<div id="studentList"> 
@using (Html.BeginForm()) 
{ 
    <div id="editorRowsStudents"> 
     @foreach (var item in Model.students) 
     { 
      @Html.Partial("StudentView", item) 
     } 
    </div> 
    @Html.ActionLink("Add", "StudentManager", null, new { id = "addItemStudents", @class = "button" }); 
} 
</div> 

<div id="teacherList"> 
@using (Html.BeginForm()) 
{ 
    <div id="editorRowsTeachers"> 
     @foreach (var item in Model.teachers) 
     { 
      @Html.Partial("TeacherView", item) 
     } 
    </div> 
    @Html.ActionLink("Add", "TeacherManager", null, new { id = "addItemTeachers", @class = "button" }); 
} 
</div> 


@section scripts { 
    <script type="text/javascript"> 
    $(function() { 
     $('#addItemStudents').on('click', function() { 
      $.ajax({ 
       url: '@Url.Action("StudentManager")', 
        cache: false, 
        success: function (html) { $("#editorRowsStudents").append(html); } 
       }); 
       return false; 
      }); 
      $('#editorRowsStudents').on('click', '.deleteRow', function() { 
       $(this).closest('.editorRow').remove(); 
      }); 
      $('#addItemTeachers').on('click', function() { 
       $.ajax({ 
        url: '@Url.Action("TeacherManager")', 
        cache: false, 
        success: function (html) { $("#editorRowsTeachers").append(html); } 
       }); 
       return false; 
      }); 
      $('#editorRowsTeachers').on('click', '.deleteRow', function() { 
       $(this).closest('.editorRow').remove(); 
      }); 
     }); 
    </script> 
} 

StudentManager Azione:

public PartialViewResult StudentManager() 
{ 
    return PartialView(new Person()); 
} 
+0

Come dovrebbe essere il metodo post? –

+0

Scusa, non so cosa intendi? – PurpleSmurph

+0

Quando pubblico la vista principale Index con il modello Register, On post restituisce gli 0 elementi nelle proprietà degli insegnanti e degli studenti. –

1

È possibile utilizzare viste parziali per esso, ma il problema è che genera le proprietà Id con EditorFor provoca duplicati venire generato.

Ciò che ha funzionato per me era usare var view = @Html.Raw(@Html.Partial('Your partial')) all'interno di javascript nella mia vista principale.

Questa variabile avrà quindi la vista con gli ID di default, ho quindi sostituire la proprietà id e la proprietà nome con id=YourMainListName_{ItemNumber}__Property e name=YourMainListName_{ItemNumber}__Property

Dove {ItemNumber} sarà l'indice della nuova voce nella lista.

Ci vuole un sacco di javascript per farlo funzionare correttamente, ma fino a quando le proprietà Id e Name sono impostate correttamente, tutto dovrebbe funzionare correttamente, inclusa l'associazione del modello quando si esegue il postback della pagina.