Aplicación web móvil con jQuery Mobile y ASP.NET MVC 2 – parte 4

Nuestra aplicación de ejemplo tiene ya implementado el controlador mClientes,  trabajaremos ahora con un nuevo controlador al que llamaremos mAutos cuyas acciones serán muy similares a mClientes.

Por lo tanto, agreguemos el nuevo controlador (en Areas/m/Controllers) asignándole el nombre mAutosController al que codificaremos su acción Index la cual debe devolver la lista de autos registrados en la BD, para esto es necesario nuevamente agregar el using hacia el Modelo (using Agencia2012.Models) y crear una instancia de nuestro modelo de entidades que tiene el nombre Agencia2012Entities.

El controlador como tal y su acción Index quedará de esta forma:

using Agencia2012.Models;
namespace Agencia2012.Areas.m.Controllers
{
    public class mAutosController : Controller
    {
        //Instancia del modelo de entidades
        Agencia2012Entities BD = new Agencia2012Entities();
        //
        // GET: /m/mAutos/
        public ActionResult Index()
        {
            //Devolver la lista de autos
            return View(BD.Auto.ToList());
        }
    }
}

Realizaremos el mismo procedimiento seguido con el controlador mClientes.

Crear la vista Index para mAutos, tipada de la clase Auto, con contenido List y la página maestra Movil.Master.

Img. 1. Agregar vista Index.

Para la vista Index, escribiremos el código correspondiente para estructura jQuery Mobile donde se muestre la lista de Autos.

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" 
    Inherits="System.Web.Mvc.ViewPage<IEnumerable<Agencia2012.Models.Auto>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Lista de autos
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
        <div data-role="page" data-theme="c"  id="autos">
            <div data-role="header" data-theme="b">
                <a href="/m/inicio" data-icon="home" data-iconpos="notext" rel="external">Inicio</a>
                <h1>Autos</h1>
                <a href="/m/mautos/nuevo" data-icon="plus" rel="external">Agregar</a>
            </div>
            <div data-role="content">   
                <ul data-role="listview" data-inset="true" data-filter="true">
                <% foreach (var item in Model) { %>
                    <li>
                        <a href="/m/mautos/verauto/<%:item.IdAuto %>">
                            <p>
                                <% //Mostrar algunos datos del cada auto
                                   //el párrafo es miembro de <a> %>
                                <strong>No. Serie</strong>: <%: item.NoSerie %><br />
                                <strong>Marca</strong>: <%: item.Marca.Nombre %><br />
                                <strong>Modelo</strong>: <%: item.Modelo %><br />
                                <strong>Año</strong>: <%: item.Anio %><br />
                            </p>
                        </a>
                    </li>          
                <% } %>
                </ul>
            </div>
            <div data-role="footer" data-theme="a">
                <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
             </div>
        </div>
</asp:Content>

Al ejecutar nuestra aplicación móvil e ingresar en Autos o directamente en http://localhost:1877/m/mautos ya tenemos nuestro resultado.

Img. 2. Lista de autos.

Nuestra página ya incluye un botón (link) para llevarnos al form para dar de alta un nuevo Auto al igual que clientes, pero antes implementaremos la parte de Ver Detalles donde al hacer clic o tocar algún elemento de la lista de autos nos muestre todos sus datos.

La acción y vista VerAuto

Agreguemos la acción al controlador:

public ActionResult VerAuto(int id)
{
    //Recuperar el objeto Auto con el Id solicitado
    var auto = BD.Auto.Where(a => a.IdAuto == id).FirstOrDefault();
    //Si es encontrado, devolverlo en la vista
    //sino, mostrar la pág. de error.
    if (auto != null)
        return View(auto);
    else
        return View("Error");
}

Creemos la vista VerAuto, tipada de la clase Auto con contenido Details.

Img. 3. Agregar la vista VerAuto.

La estructura de la página con jQuery Mobile queda como:

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" 
    Inherits="System.Web.Mvc.ViewPage<Agencia2012.Models.Auto>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Ver Auto
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
        <div data-role="page" data-theme="b"  id="verauto">
            <div data-role="header" data-theme="b">
                <a data-rel="back" data-icon="arrow-l">Regresar</a>
                <h1>Auto</h1>
                <a href="/m/mautos/nuevo" data-icon="plus" rel="external">Agregar</a>
            </div>
            <div data-role="content">
        <div><strong>No. Serie</strong>:</div>
        <div><%: Model.NoSerie %></div>
        <div><strong>Marca</strong>:</div>
        <div><%: Model.Marca.Nombre %></div>
        <div><strong>Modelo</strong>:</div>
        <div><%: Model.Modelo %></div>
        <div><strong>Año</strong>:</div>
        <div><%: Model.Anio %></div>
        <div><strong>Color</strong>:</div>
        <div><%: Model.Color %></div>
        <div><strong>Precio</strong>:</div>
        <div><%: String.Format("{0:c}", Model.Precio) %></div>
        <div><strong>Vendido</strong>:</div>
        <div>
        <% //Comprobar si el auto ha sido vendido %>
             <% if ((bool) Model.Vendido)
                   {
                    //Si ya está vendido, mostrar el nombre del cliente y el botón para ver los detalles de la venta
                    %>
                       Si
                       <br />
                       <strong>Cliente</strong>: <%: Model.Venta.FirstOrDefault().Cliente.Nombre + " " + Model.Venta.FirstOrDefault().Cliente.Apellidos%>  <a href="/m/mventas/verventa/<%: Model.Venta.FirstOrDefault().IdVenta %>" data-role="button" data-theme="c" data-icon="arrow-r" data-iconpos="right" data-inline="true" rel="external">Ver detalles de la venta</a>
                   <%}
                   else
                   {
                    //Si no se ha vendido, mostrar un botón para realizar la venta del auto 
                    %>
                    No
                    <br />
                        <a href="/m/mventas/venderauto/<%: Model.IdAuto %>" data-role="button" data-inline="true" data-icon="arrow-r" data-iconpos="right" data-theme="c" rel="external">Vender este auto</a>
              <%} %>
        </div>
        <a href="/m/mautos/editar/<%: Model.IdAuto %>" data-role="button" data-inline="true" rel="external">Editar</a>       
        </div>
            <div data-role="footer" data-theme="a">
                <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
             </div>
        </div>
</asp:Content>

Entonces al ejecutar nuestra aplicación y elegir un auto, tenemos el resultado.

Img. 4. Ver Auto en versión móvil.

La acción y vista Editar

Igual que en clientes, podemos editar los datos del auto (puede agregar el código correspondiente para no mostrar el botón editar si ya se ha vendido el auto).

Agreguemos la acción Editar (con respuesta GET y POST):

public ActionResult Editar(int id)
{
    //recuperar el objeto Auto con el Id solicitado
    var auto = BD.Auto.Where(a => a.IdAuto == id).FirstOrDefault();
    if (auto != null)
    {        
        if (!(bool)auto.Vendido)
        {
            //si el auto no se ha vendido
            //mostrar la vista Editar con los datos del auto
            //le pasamos la lista de marcas (para que pueda cambiar la marca)
            ViewData["Marcas"] = Listas.Marcas();
            return View(auto);
        }
        else
        {
            //si ya se ha vendido
            //mostraremos la vista VerVenta (se creará en mVentasController más adelante)
            //A esa vista pasaremos un mensaje que deberá mostrarse al usuario
            ViewData["MensajeVenta"] = "El auto ya ha sido vendido y no se puede editar.";
            //entonces a esa vista VerVenta pasamos los datos del auto ya vendido
            return View("../mventas/verventa", auto.Venta.FirstOrDefault());
        }
    }
    else
        return View("Error");
}
[HttpPost]
public ActionResult Editar(int id, FormCollection datos)
{
    //recuperar el objeto Auto que se está editando
    var auto = BD.Auto.Where(a => a.IdAuto == id).FirstOrDefault();
    if (ModelState.IsValid)
    {
        //si ya se han validado los datos
        //actualizar el modelo y guardar los cambios
        UpdateModel(auto, datos);
        BD.SaveChanges();
        //redireccionar a ver nuevamente los detalles del auto
        return Redirect("../../../m/mautos/verauto/" + auto.IdAuto);
    }
    else
        return View("Error");
}

La vista Editar

Para esta vista volveremos a crear una vista parcial (plantilla) que contendrá el formulario para editar o registrar un auto, agregaremos también los archivos JS correspondientes de forma muy similar a como se hizo en la parte 2 :: http://afelipelc.mx/2012/01/19/aplicacion-web-movil-con-jquery-mobile-y-asp-net-mvc-2-parte-2/ (en la vista Editar).

Agregamos dos nuevos archivo de JavaScript (nuevo elemento Archivo JScript) a la carpeta Areas/m/scripts llamados autovars.js  y autovalidador.js en los que declararemos todas las variables  y funciones a utilizar en nuestro validador.

Img. 5. Archivos JS agregados para validar Auto.

El código para autovars.js es:

//autovars.JS
// Declaración de variables globales - deben hacerce antes de $(document).ready()
var hdrMainvar = null;
var contentMainVar = null;
var ftrMainVar = null;
var contentTransitionVar = null;
var marcalblVar = null;
var marcaVar = null;
var formvar = null;
var contentDialogVar = null;
var inputMapVar = null;
// constantes
var MISSING = "obligatorio"; //el css ya existe
var EMPTY = "";
var noselect = "0";

El código para autovalidador.js es:

//autovalidador.js
$(document).ready(function () {
    // asignar valores a variables globales
    hdrMainVar = $('#hdrMain');
    contentMainVar = $('#contentMain');
    ftrMainVar = $('#ftrMain');
    marcalblVar = $('#marcalbl');
    marcaVar = $('#IdMarca');
    formVar = $('#datosauto');
    contentDialogVar = $('#contentDialog');
    inputMapVar = $('.obligatorio');
    hideContentDialog();
});
$('#buttonOK').click(function () {
    hideContentDialog();
    showMain();
    return false;
});
$('#datosauto').submit(function () {
    var err = false;
    // Oculatar el contenido de la página
    hideMain();
    // Quitar las clases que posiblemente se agregaron al
    //realizar una validación previa
    marcalblVar.removeClass(MISSING);
    inputMapVar.each(function (index) {
        $(this).prev().removeClass(MISSING);
    });
    // Verificar todos los campos marcados con la
    inputMapVar.each(function (index) {
        if ($(this).val() == null || $(this).val() == EMPTY) {
            $(this).prev().addClass(MISSING);
            err = true;
        }
    });
    //validar el combo Marca
    if (marcaVar.val() == noselect) {
        marcalblVar.addClass(MISSING);
        err = true;
    }
    // Si la validación falla, mostrar la ventana de dialogo
    if (err == true) {
        showContentDialog();
        return false;
    }
    // Enviar el formulario
    formVar.submit();
});
function hideMain() {
    hdrMainVar.hide();
    contentMainVar.hide();
    ftrMainVar.hide();
}
function showMain() {
    hdrMainVar.show();
    contentMainVar.show();
    ftrMainVar.show();
}
function hideContentDialog() {
    contentDialogVar.hide();
}
function showContentDialog() {
    contentDialogVar.show();
}

La vista parcial Auto.

Creados y codificados los archivos JS procedemos a crear la vista parcial Auto.ascx, para ello nuevamente agregar una nueva carpeta llamada EditorTemplates en Areas/m/Views/mAutos y en esta carpeta agregamos la vista parcial Auto tipada para la clase Auto con contenido Edit:

Img. 6. Agregando la vista parcial Auto.ascx

La carpeta y el archivo Auto.ascx queda como:

Img. 7. Vista parcial Auto.ascx agregada.

A esta plantilla agregamos también el código para el formulario de datos de Auto con algunos elementos de  jQuery Mobile:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Agencia2012.Models.Auto>" %>
<div id="Div1" data-role="fieldcontain">
    <%: Html.HiddenFor(model => model.IdAuto) %>
    <label for="NoSerie">Número de serie*</label>
    <%: Html.TextBoxFor(model => model.NoSerie, new { @class = "obligatorio" })%>
</div>
<div id="Div2" data-role="fieldcontain">
    <label id="marcalbl" for="Marca">Marca*</label>
        <% if (Model!=null && Model.Marca != null)
    {
        //Si se está editando, mostramos la marca seleccionada
        %>
        <%: Html.DropDownList("IdMarca", new SelectList(ViewData["Marcas"] as IEnumerable, "IdMarca", "Nombre", Model.IdMarca), new Dictionary<string, object>() { { "data-native-menu", "true" } })%>
    <%
    }
    else
    { 
     %>
    <%: Html.DropDownList("IdMarca", new SelectList(ViewData["Marcas"] as IEnumerable, "IdMarca", "Nombre"), new Dictionary<string, object>() { { "data-native-menu", "true" } })%>
    <% } %>
</div>
<div id="Div3" data-role="fieldcontain">
    <label for="Modelo">Modelo*</label>
    <%: Html.TextBoxFor(model => model.Modelo, new { @class = "obligatorio" })%>
</div>
<div id="Div4" data-role="fieldcontain">
    <label for="Anio">Año*</label>
        <%if (Model != null)
        {
         //Si se está editando, mostramos el año 
         %>
    <input type="number" name="Anio" id="Anio" min="1990" pattern="[0-9]*" required="required" value="<%: Model.Anio %>" />
    <%}
        else
        { %>
        <input type="number" name="Anio" id="Anio" min="1990" pattern="[0-9]*" required="required" />
    <%} %>
</div>
<div id="Div5" data-role="fieldcontain">
    <label for="Color">Color*</label>
    <%: Html.TextBoxFor(model => model.Color, new { @class = "obligatorio" })%>
</div>
<div id="Div6" data-role="fieldcontain">
    <label for="Precio">Precio*</label>
        <%if (Model != null)
        { 
          //si se está editando, mostramos el precio
          %>
    <input type="number" name="Precio" id="Precio" min="10000" pattern="[0-9]*" required="required" value="<%: Model.Precio %>" />
    <%}
        else
        { %>
        <input type="number" name="Precio" id="Precio" min="10000" pattern="[0-9]*" required="required" />
    <%} %>
</div>

Creada la plantilla ahora creamos la vista Editar, esta será tipada de la clase Auto con contenido Edit.

Img. 8. Agregando la vista Editar.

A esta vista agregamos la estructura jQuery Mobile y hacemos uso de la plantilla Auto.ascx:

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/m/Views/Shared/Movil.Master" Inherits="System.Web.Mvc.ViewPage<Agencia2012.Models.Auto>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Editar
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script src="../../../Areas/m/Content/scripts/autovars.js" type="text/javascript"></script>
    <link href="../../../Areas/m/Content/css/estilos.css" rel="stylesheet" type="text/css" />
<!-- Inicio de la página -->
<div data-role="page" data-theme="c" id="editarauto">
<div data-role="header" data-theme="b" id="hdrMain" name="hdrMain" data-nobackbtn="true">
<a href="/m/mautos" data-icon="arrow-l">Autos</a>
           <h1>Editar</h1>
        <a href="/m/mautos/nuevo" data-icon="plus" rel="external">Agregar</a>
</div>
<div data-role="content" id="contentMain" name="contentMain">
        <form id="datosauto" action="" method="post">
            <!-- Invocar al EditorTemplate Auto.ascx que es nuestra plantilla -->
            <%: Html.EditorFor(model => Model) %>
            <p>
                <button type="submit" data-theme="b" data-inline="true">Guardar</button> <a data-rel="back" data-role="button" data-theme="d" data-inline="true">Cancelar</a>
            </p>
   </form>
</div><!-- Fin del contenído de la página -->
<div data-role="footer" data-theme="a">
                <h4><a href="http://www.afelipelc.mx" rel="external">www.afelipelc.mx</a></h4>
     </div>
<!-- Contenedor que utilizaremos como cuadro de diálogo al momento de validar -->
  <div align="center" data-role="content" id="contentDialog" name="contentDialog">
<div>Llene todos los campos marcados con * antes de enviar el formulario.</div>
<a id="buttonOK" name="buttonOK" href="#editarauto" data-role="button" data-inline="true">OK</a>
</div> <!-- fin contenedor de Diálogo -->
    <!-- Enlazar al script de validación para que pueda ser ejecutado correctamente -->
    <script src="../../../Areas/m/Content/scripts/autovalidador.js" type="text/javascript"></script>
</div> <!-- fin de toda la página jQuery M -->
</asp:Content>

Ahora al ejecutar la app, ir a autos, seleccionar uno de la lista y al seleccionar la opción Editar, ya tenemos el formulario:

Img. 9. Vista Editar ya funcionando.

La validación también funciona:

Img. 10. Diálogo de notificación.
Img. 11. Campo obligatorio resaltado.

Al realizar y guardar los cambios, nos redirecciona correctamente a los detalles mostrando los datos actualizados.

Img. 12. Datos actualizados.

Hasta aquí se queda el controlador mAutos, ahora por su parte, implemente la acción y vista Nuevo que le permita registrar nuevos autos (es parte del proyecto y no debe dejarse de lado).

Lo que se tendrá como resultado sera como se muestra en estas imágenes.

Al dar clic en el botón Agregar (en autos) debe mostrar el mismo formulario que en Editar.

Img. 13. Vista Nuevo (auto).

Funciona también la validación:

Img. 14. Comprobando validación.

Y finalmente al llenar el formulario y registrar el auto, debe redireccionarnos a ver sus datos.

Img. 15. Auto registrado.

Hasta aquí el controlador mAutosController ya estará terminado.

Falta solamente el controlador mVentas que es un poco más sencillo.

Créditos: La validación de formularios fue tomada de http://mobile.tutsplus.com/tutorials/mobile-web-apps/jquery-mobile-forms/

3 comentarios en “Aplicación web móvil con jQuery Mobile y ASP.NET MVC 2 – parte 4

  1. Hola Felipe, primeramente agradecerle por el post, y de paso quiero hacerle unas consultas propios de jquery mobile.
    yo tengo mi index.htm que es la pagina 1.
    en el head de la primera pagina.

    //$( ‘#dvlogin’ ).live( ‘pageinit’,function(event){
    $(‘#dvlogin’).live( ‘pageinit’,function(event){
    $(‘#btninicio’).bind(‘click’, function(event, ui){
    $.mobile.changePage(“ui/Usuario.html”, {reloadPage : true,});
    });
    });

    y tambien tengo en la segunda pagina Usuario.html, en el head tengo este codigo
    $(‘#dvUsuario’).live( ‘pageinit’,function(event){
    $(‘#btnn’).bind(‘click’, function(event, ui){
    alert(‘Ingreso a la pagina 2’);
    });
    });

    En la pagina index.html el pageinit funciona de maravillas pero mi duda es de por que no se lanza el pageinit de la segunda pagina ya que no puedo enlazar los eventos de los botonos de la pagina 2 asi como lo hize en la primera pagina.
    Aclarando estoy trabajando con eclipse+phonegap jquery mobile +android
    por favor espero sus comentarios espero que me puedan ayudar, los agradecere.

    1. Lo que sucede es que las pginas se que se cargan con ajax por alguna razn pierden el enlace a los funciones de javascript, actualmente ya no he trabajado con jqm, pero tambin me tope con este error, la solucin rpida es cargar los contenidos pero no con ajax, sino directamente como links externos.

      1. Bueno entiendo en mi caso como lo trabajaria con links externos??, me puedes explicar con un ejemplo, si es mucho pedir muchas gracias.,

Escribe tu comentario:

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s