Master-detail in MVC C # with Razor

10

Personal I need to set up a master-detail master record. I've already researched and seen a few examples on the internet, however as I do for when the master is not registered, the details are stored temporarily for the general save save the master record and detail records.

I thought about doing it in Session, but I think this can be a problem. Is there a solution for this?

public class Cliente
{
    [Key]
    public int ClienteID { get; set; }
    public string Nome { get; set; }
    public virtual ICollection<Telefone> Telefones { get; set; }
}

public class Telefone
{
    [Key]
    public int TelefoneID { get; set; }                
    public string Telefone { get; set; }
    public virtual Cliente Cliente { get; set; }
}

The idea is to do something similar to the image below, but without the need for the Client registered in the database. Do everything in single post.

The big question, is how can I "record" the phone data and return to the screen this information without refresh ...

    
asked by anonymous 10.03.2014 / 18:17

3 answers

9

Since it is to save everything at once, I would use a client approach, without postback until the moment everything has to be saved.

You could argue about the possible loss of data in complex registers where the user unwittingly closes the browser, but using the HTML 5 features (if this is an option) you can save the data in localstorage or using a DB client (websql) which prevents such data loss.

Functional example:

View code:

@model MvcApplication2.Controllers.MasterDetailObj
<html>
<head>
    <title>Postar master/detail</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script><scripttype="text/javascript">
        $(function () {
            $("#add").on("click", function () {
                var tel = $("#numero").val();
                var ddd = $("#ddd").val();
                var idx = $("#telefones > tbody > tr").length;
                $("#telefones > tbody").append("<tr><td><input type='hidden' name='Telefones[" + idx + "].Numero' value='" + tel + "' />" + tel
                    + "</td><td><input type='hidden' name='Telefones[" + idx + "].Ddd' value='" + ddd + "' />" + ddd + "</td></tr>");
            });
        });
    </script>
</head>
<body>
    <h2>Postar master/detail</h2>

    <div>
        <div>
            <label for="numero">Número</label>
            <input type="text" id="numero" />
        </div>
        <div>
            <label for="ddd">DDD</label>
            <input type="text" id="ddd" />
        </div>
        <button id="add">Add</button>
    </div>

    @using (this.Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Nome)
            @Html.TextBoxFor(x => x.Nome)
        </div>
        <table id="telefones">
            <thead>
                <tr>
                    <th>Número</th>
                    <th>Ddd</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>

        <button type="submit">submit</button>
    }
</body>
</html>

Controller code:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult MultiDetailSubmit()
    {
        return this.View();
    }

    [HttpPost]
    public ActionResult MultiDetailSubmit(MasterDetailObj obj)
    {
        if (this.ModelState.IsValid)
        {
            // aqui estarão disponíveis todos os itens adicionados na lista de telefones no client

            return this.RedirectToAction("Index", "Home");
        }

        return this.View(obj);
    }

}

Models:

public class MasterDetailObj
{
    public string Nome { get; set; }
    public List<Telefone> Telefones { get; set; }
}

public class Telefone
{
    public string Numero { get; set; }
    public string Ddd { get; set; }
}
    
10.03.2014 / 18:51
5
  

This code was written at the time that the Entity Framework was in version 5, so possibly the handling logic of the phone collection may have changed. Please notify me for comments if any error condition is found for me to correct.

Use the package BeginCollectionItem , available in NuGet:

  

link

The following tutorial documents how to implement master-detail:

link

It would look like this:

_CreateOrEdit.cshtml (Client)

@model SeuProjeto.Models.Cliente

@* Demais campos do seu model *@

@if (Model != null && Model.Telefones != null)
{
    foreach (var telefone in Model.Telefones)
    {
        Html.RenderPartial("_TelefonesEditor", telefone);
    }
}

@* Botões de submit, fechamento de <fieldset>, etc. *@

_TelefonesEditor.cshtml

@model SeuProjeto.Models.Telefone

@using (Html.BeginCollectionItem("Telefones"))
{ 
    @Html.HiddenFor(model => model.TelefoneID)
    @Html.HiddenFor(model => model.ClienteID)
    @Html.EditorFor(model => model.Telefone)
}

CustomersController.cs

namespace SeuProjeto.Controllers
{
    public class ClientesController : Controller
    {
        [HttpPost]
        public ActionResult Create(Cliente cliente)
        {
            if (ModelState.IsValid)
            {
                if (shop.Telefones != null)
                {
                    foreach (var telefone in cliente.Telefones)
                    {
                        telefone.ClienteID = cliente.ClienteID;
                        context.Entry(telefone).State = System.Data.Entity.EntityState.Modified;
                        context.SaveChanges();
                    }
                }
            }

            // Lógica adicional, caso Model não seja válido
        }

        [HttpPost]
        public ActionResult Edit(Cliente cliente)
        {
            if (ModelState.IsValid)
            {
                // Telefones Originais
                List<Telefones> telefonesOriginais = context.Telefones.AsNoTracking().Where(t => t.ClienteID == cliente.ClienteID).ToList();

                if (cliente.Telefones != null)
                {
                    // Telefones Excluídos
                    foreach (var telefone in telefonesOriginais)
                    {
                        if (!cliente.Telefones.Where(t => t.TelefoneID == telefone.telefoneID).Any())
                        {
                            var telefoneExcluido = context.Telefones.Single(t => t.TelefoneID == telefone.TelefoneID);
                            context.Telefones.Remove(telefoneExcluido);
                            context.SaveChanges();
                        }
                    }

                    // Telefones Novos ou Editados
                    foreach (var telefone in cliente.Telefones)
                    {
                        if (telefone.ClienteID == 0)
                        {
                            telefone.ClienteID = cliente.ClienteID;
                            context.Telefones.Add(telefone);
                        }
                        else
                        {
                            context.Entry(telefone).State = System.Data.Entity.EntityState.Modified;
                        }

                        context.SaveChanges();
                    }
                }

                context.Entry(cliente).State = System.Data.Entity.EntityState.Modified;
                context.SaveChanges();
            }

            // Lógica adicional, caso Model não seja válido
        }
    }
}
    
10.03.2014 / 18:34
2

I recommend you use the MVVM standard, especially KnockoutJS, it is a great package where you bring your model to the view and based on it you can have this implementation of the list. The cool thing about working with it is that you can choose to transact the final object or part of it.

Below are some extremely useful links to knowledge and see the implementation of KnockoutJS with MVC

MVC-4-Knockout-CRUD

Building with MVVM

KnockoutJS and bootstrap

Introduction to KnockoutJS

Of course, KnockoutJS's own link

KnockoutJS Official

(Available in the Install-Package KnockoutJS Nugget)

    
11.03.2014 / 13:00