How do I checkbox Asp.net mvc database

Asked

Viewed 730 times

1

Hello. I’m doing a project for a music player. In the Playlist register, I would like the songs to appear in checkbox so I can mark the desired ones and save in the database.

This is my controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using XMusic.MVC.Models;
using XMusic.MVC.Persistencia;

namespace XMusic.MVC.Controllers
{
    public class PlayListController : Controller
    {

        private PersistenciaPlayList persistenciaPlayList;
        private ContextoEF contextoEF;

        private PersistenciaUsuario persistenciaUsuario;
        private PersistenciaMusica persistenciaMusica;

        public PlayListController()
        {
            persistenciaPlayList = new PersistenciaPlayList();
            persistenciaMusica = new PersistenciaMusica();
            contextoEF = new ContextoEF();

            persistenciaUsuario = new Persistencia.PersistenciaUsuario();

            ViewData["usuarios"] = persistenciaUsuario.ObterTodos();
            ViewData["musicas"] = persistenciaMusica.ObterTodas();
        }

        public ActionResult Index()
        {
            var playlists = persistenciaPlayList.ObterTodos();
            return View(playlists);
        }

        [HttpGet]
        public ActionResult Adicionar()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Adicionar(PlayList playlist)
        {


            var msgAlerta = string.Empty;
            var tipoAlerta = string.Empty;

            try
            {
                if (!ModelState.IsValid)
                    throw new Exception();

                persistenciaPlayList.Adicionar(playlist);

                ModelState.Clear();

                msgAlerta = "PlayList cadastrada com sucesso";
                tipoAlerta = "alert-success";

            }
            catch (Exception e)
            {
                msgAlerta = "Ocorreu um erro ao cadastrar a PlayList" + e;
                tipoAlerta = "alert-danger";
            }

            TempData.Add("MsgAlerta", msgAlerta);
            TempData.Add("TipoAlerta", tipoAlerta);

            return View();
        }


        [HttpGet]
        public ViewResult Alterar(int playlistId)
        {
            var playlist = persistenciaPlayList.Obter(playlistId);
            return View(playlist);
        }

        [HttpPost]
        public ActionResult Alterar(PlayList playlist)
        {
            var msgAlerta = string.Empty;
            var tipoAlerta = string.Empty;

            var alterado = false;

            try
            {
                if (!ModelState.IsValid)
                    throw new Exception();

                alterado = persistenciaPlayList.Alterar(playlist);

                if (alterado)
                {
                    msgAlerta = "PlayList alterada com sucesso";
                    tipoAlerta = "alert-success";
                }
                else
                {
                    msgAlerta = "Ocorreu um erro ao alterar a PlayList";
                    tipoAlerta = "alert-danger";
                }

            }
            catch (Exception e)
            {
                alterado = false;
                msgAlerta = "Ocorreu um erro ao alterar a PlayList: " + e;
                tipoAlerta = "alert-danger";

            }


            TempData.Add("MsgAlerta", msgAlerta);
            TempData.Add("TipoAlerta", tipoAlerta);

            if (alterado)
                return RedirectToAction("Index");
            else
                return View();
        }


        [HttpGet]
        public JsonResult ObterUsuarios()
        {
            var msg = string.Empty;

            var listaUsuarios = persistenciaUsuario.ObterTodos();


            if (!listaUsuarios.Any())
            {
                msg = "Nenhum usuário encontrado";
            }

            var usuarios = from u in listaUsuarios
                           select new { UsuarioId = u.UsuarioId, Nome = u.Nome };

            return Json(new { ret = usuarios, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }


        [HttpGet]
        public JsonResult ObterMusicas()
        {
            var msg = string.Empty;

            var listaMusicas = persistenciaMusica.ObterTodas();


            if (!listaMusicas.Any())
            {
                msg = "Nenhum música encontrada";
            }

            var musicas = from m in listaMusicas
                           select new { MusicaId = m.MusicaId, Titulo = m.Titulo };

            return Json(new { ret = musicas, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }



        [HttpGet]
        public JsonResult ObterTodos()
        {
            var msg = string.Empty;

            var listaPlayList = persistenciaPlayList.ObterTodos().ToList();



            if (!listaPlayList.Any())
            {
                msg = "Nenhuma PlayList encontrada";
            }


            var playlist = from p in listaPlayList
                           select new { PlayListId = p.PlayListId, Titulo = p.Titulo };



            return Json(new { ret = playlist, mensagem = msg },
                JsonRequestBehavior.AllowGet);
        }
    }


}

This is the persistent Playlist:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using XMusic.MVC.Models;

namespace XMusic.MVC.Persistencia
{
    public class PersistenciaPlayList
    {
        private ContextoEF _contexto = new ContextoEF();


        public PlayList Adicionar(PlayList playlist)
        {
            _contexto.Playlists.Add(playlist);
            _contexto.SaveChanges();

            return playlist;
        }


        public IEnumerable<PlayList> ObterTodos()
        {
            return _contexto.Playlists.ToList();
        }

        public PlayList Obter(int id)
        {
            try
            {
                return _contexto.Playlists.FirstOrDefault(p => p.PlayListId == id);
            }
            catch
            {
                throw;
            }

        }


        public bool Alterar(PlayList playlist)
        {
            try
            {
                var p = _contexto.Playlists.Find(playlist.PlayListId);

                if (p != null)
                {

                    p.Titulo = playlist.Titulo;
                    p.UsuarioId = playlist.UsuarioId;
                    p.Musicas = playlist.Musicas;

                    _contexto.Entry(p).State = EntityState.Modified;
                    _contexto.SaveChanges();

                    return true;
                }
                else
                    return false;
            }
            catch
            {
                throw;
            }
        }




        public PlayList Remover(int id)
        {
            try
            {
                var p = _contexto.Playlists.Find(id);

                _contexto.Playlists.Remove(p);
                _contexto.SaveChanges();

                return p;
            }
            catch
            {
                return null;
            }
        }

    }
}

And here where I am showing the data. I would like the music data, to be shown in checkbox:

@model XMusic.MVC.Models.PlayList

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Titulo, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Titulo, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Titulo, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownList(
               "UsuarioId",
               new SelectList(
                   (IEnumerable<XMusic.MVC.Models.Usuario>)ViewData["usuarios"],
                   "UsuarioId",
                   "Nome"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Musicas, "Músicas", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">


                @Html.DropDownList(
               "MusicaId",
               new SelectList(
                   (IEnumerable<XMusic.MVC.Models.Musica>)ViewData["musicas"],
                   "MusicaId",
                   "Titulo"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.Musicas, "", new { @class = "text-danger" })
            </div>
        </div>


        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Salvar" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>

2 answers

1

The best way is defined Viewmodels:

public class PlaylistViewModel
{
    public Playlist Playlist { get; set; }
    public ICollection<UsuarioViewModel> Usuarios { get; set; }
}

public class MusicaViewModel
{
    public bool Selecionado { get; set; }
    public Musica Musica { get; set; }
}

That is to say lousy to do. It is good to remove.

    public PlayListController()
    {
        persistenciaPlayList = new PersistenciaPlayList();
        persistenciaMusica = new PersistenciaMusica();
        contextoEF = new ContextoEF();

        persistenciaUsuario = new Persistencia.PersistenciaUsuario();

        ViewData["usuarios"] = persistenciaUsuario.ObterTodos();
        ViewData["musicas"] = persistenciaMusica.ObterTodas();
    }

Same thing for your persistence layer. Here are the reasons

Controller

To Index gets like this:

    public ActionResult Alterar(int playlistId)
    {
        var playlist = contexto.Playlists
                                .Include(p => p.Musicas)
                                .Include(p => p.Usuarios)
                                .FirstOrDefault(p => p.PlaylistId == playlistId);

        var viewModel = new PlaylistViewModel
        {
            Playlist = playlist, 
            Musicas = playlist.Musicas.Select(m => new MusicaViewModel
            {
                Selecionado = false,
                Musica = m
            })
        };

        ViewModel.Usuarios = contexto.Usuarios.ToList();
        return View(playlist);
    }

View

@model XMusic.MVC.ViewModels.PlayListViewModel

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Playlist.Titulo, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Playlist.Titulo, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Playlist.Titulo, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownListFor(model => model.Playlist.UsuarioId,
                    ((IEnumerable<XMusic.MVC.Models.Usuario>)ViewBag.Usuarios).Select(u => new SelectListItem {
                        Text = u.Nome,
                        Value = u.UsuarioId.ToString(),
                        Selected = (Model != null) && (Model.Playlist != null) && (Model.Playlist.UsuarioId == u.UsuarioId)
                    }), "Selecione...", new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Playlist.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>

        @foreach (var musica in Model.Musicas)
        {
            <div>
                @Html.Partial("_Musicas", musica)
            </div>
        }

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Salvar" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>

Partial _LinhaMusica.cshtml

For this to work well, you will need to use the package Begincollectionitem:

@model XMusic.MVC.ViewModels.MusicaViewModel

@using (Html.BeginCollectionItem("Musicas"))
{
    @Html.HiddenFor(model => model.Musica.MusicaId)
    @Html.CheckBoxFor(model => model.Selecionado) @Model.Musica.Nome
}

Controller (POST)

    [HttpPost]
    public ActionResult Alterar(PlayListViewModel playlist)
    {
        var msgAlerta = string.Empty;
        var tipoAlerta = string.Empty;

        var alterado = false;

        // Não use try ... catch dentro de Actions.
        // Use o método OnException do Controller ancestral para tratar exceções.
        // try
        // {
            if (ModelState.IsValid)
            {
                // Se o Model é inválido, não levante exceção.
                // A maneira correta é enviando ao Form mensagens de validação de cada campo que falhou.
                // throw new Exception();

                // Monte uma nova lógica aqui. 
                // Aqui você pode conferir as CheckBoxes que foram marcadas, etc.
                // alterado = persistenciaPlayList.Alterar(playlist);

                if (alterado)
                {
                    TempData.Add("MsgAlerta", "PlayList alterada com sucesso");
                    TempData.Add("TipoAlerta", "alert-success");
                    return RedirectToAction("Index");
                }

                TempData.Add("MsgAlerta", "Ocorreu um erro ao alterar a PlayList");
                TempData.Add("TipoAlerta", "alert-danger");
            }
        // catch (Exception e)
        // {
        //    alterado = false;
        //    msgAlerta = "Ocorreu um erro ao alterar a PlayList: " + e;
        //    tipoAlerta = "alert-danger";
        // }

        ViewModel.Usuarios = contexto.Usuarios.ToList();            
        return View(alterado);
    }

0

Posting lists of objects usually takes a little work. In these cases I usually use Custom Model Binders. By default Asp.NET binds the data of our Views to the parameters in our Actions, so when we use the approach I mentioned above, we give up part of Automatic Binding. To do this just implement some interfaces provided by Asp.NET itself, but depending on the version rather than interfaces, you will have to extend some classes. In the following example, I implemented its functionality using Asp.NET.Core. Look at the code:

class ListaMusicaModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (context.Metadata.PropertyName != null && context.Metadata.ContainerType.GetProperty(context.Metadata.PropertyName).PropertyType.IsConstructedGenericType)
            return new ListaMusicaModelBinder(context.Metadata.ModelType);
        return null;
    }
}

First we create the Provider, which will provide the correct instance responsible for performing the Bind of the data for you. Note that for all metadata that should not be handled by Listamusicamodelbinder we return null, I do this because I want all property other than the music collection to remain under the responsibility of Asp.NET to perform Bind.

Now our Modelbinder

class ListaMusicaModelBinder : IModelBinder
{
    public ListaMusicaModelBinder(Type type)
    {
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        return Task.Run(() =>
        {
            List<Musica> musicas = new List<Musica>();
            var request = bindingContext.HttpContext.Request.Form.Keys.Where(w => w.Contains(bindingContext.FieldName)).ToArray();
            var grupos = request.GroupBy(s => s.Split('.').First());
            foreach (var item in grupos)
            {                    
                var value = bindingContext.ValueProvider.GetValue(item.First()).FirstValue;
                musicas.Add(new Musica
                {
                    MusicaId = int.Parse(value)
                });
            }
            bindingContext.Result = ModelBindingResult.Success(musicas);
        });
    }
}

This class is responsible for mapping the data from requests to the respective properties of its parameters, when not the parameter itself.

But I want to draw attention to the method Bindmodelasync, there are other approaches to implementing this method, as in the article Custom Model Binding in ASP.Net Core.

I tried to implement in the simplest way possible, because I didn’t see the need to treat (N) types of collections and map (N) types of properties, this would only serve to inflate the code with unnecessary complexity, because we know that you only need to handle collections of songs, and that your view will only return the Ids of selected songs.

Done that in the file Startup, in the method Configureservices, we have configured our

public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddMvc().AddMvcOptions((opt) =>
        {
            // aqui configuramos nosso provider.
            opt.ModelBinderProviders.Insert(0, new XMusic.Controllers.ListaMusicaModelBinderProvider());
        });
    }

Asp.NET allows us to add (N) providers for various things, ranging from Validation Customizators to Mapping the data from requests.

You will find the Add method in Modelbinderproviders, but recommend the use of Insert, this ensures that our custom preview is the first to be analyzed.

Finally the View is as follows

@*
    For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*@
@{
}
@model XMusic.Models.PlayList

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Titulo, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Titulo, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Titulo, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.UsuarioId, "Usuário", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.DropDownList(
               "UsuarioId",
               new SelectList(
                   (IEnumerable<XMusic.Models.Usuario>)ViewData["usuarios"],
                   "UsuarioId",
                   "Nome"),

               htmlAttributes: new { @class = "form-control" })


                @Html.ValidationMessageFor(model => model.UsuarioId, "", new { @class = "text-danger" })
            </div>
        </div>
        @{ 
            var Musicas = (IList<XMusic.Models.Musica>)ViewData["musicas"];
        }
        <table>
            <thead>
                <tr>
                    <td>#</td>
                    <td>Música</td>
                </tr>
                @for (int i = 0; i < Musicas.Count; i++)
                {
                    <tr>
                        <td>
                            <input type="checkbox" value="@Musicas[i].MusicaId" 
                                   id="@Html.IdFor(m =>m.Musicas[i].MusicaId)"
                                   name="@Html.NameFor(m =>m.Musicas[i].MusicaId)"/>
                        </td>
                        <td>
                            @Musicas[i].Titulo
                        </td>
                    </tr>
                }
            </thead>

        </table>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Salvar" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Lista de PlayLists", "Index")
</div>

Your songs are displayed in a table, and in this table we have the checkbox that will allow the user to choose the songs that will be saved.

When you run the Post, you’ll see that your list inside the Playlist is only filled with selected songs.

But why this approach?

There are other ways to accomplish this, one of them is by using ajax requests, but not to run away from what you have done so far, I recommend this approach.

If you want an example of Modelbind in other versions of Asp.NET recommend the links below:

How to bind to custom Objects in action Signatures in MVC/Webapi

Custom Model Binder for ASP.NET MVC on GET request

Browser other questions tagged

You are not signed in. Login or sign up in order to post.