Thursday, August 31, 2017

Breadcrumbs

Keywords: Breadcrumbs, web design, diseño de navegación



Los breadcrumbs son un elemento de navegación ideal para reducir el número de pasos que debe realizar un usuario, especialmente cuando tenemos taxonomías jerárquicas. Los breadcrumbs mejoran en general la encontrabilidad de los elementos y le facilitan al usuario determinar su estado actual y rutas de regreso.

El término breadcrumb, o miga de pan, proviene del cuento de Hansel y Grettel, donde los niños utilizaban migas de pan para encontrar su camino a casa. Los breadcrumbs están compuestos normalmente por textos en secuencia clickeables que permiten ir directamente a la categoría padre del elemento.

Veamos algunos ejemplos de breadcrumbs:

1. Basados en la ubicación
Éstos se basan directamente en la taxonomía, utilizando los niveles de jerarquía como las categorías padre que se incluyen en los breadcrumbs.

2. Basados en atributos
Son muy útiles en las taxonomías facetadas, listando los atributos que se van filtrando y permitiendo eliminar los filtros aplicados.

3. Basados en ruta 
En este caso, los breadcrumbs no se fijan en la taxonomía, sino en el recorrido que ha realizado el usuario hasta llegar a la página actual. De esta forma, muestra las páginas a las que ingresó antes de llegar a la que se encuentra actualmente.

Wednesday, August 30, 2017

Conceptos utilizados en el entorno de proyecto para el desarrollo de aplicaciones web

Si estás empezando con el desarrollo de aplicaciones web, seguramente las herramientas utilizadas en la configuración del entorno de proyecto en la entrada anterior, son nuevas para ti. A continuación, te dejo algunos conceptos que tomé de la web, son claros para iniciar:

Node.js es un entorno Javascript del lado del servidor, basado en eventos. Node ejecuta javascript utilizando el motor V8, desarrollado por Google para uso de su navegador Chrome. Aprovechando el motor V8 permite a Node proporciona un entorno de ejecución del lado del servidor que compila y ejecuta javascript a velocidades increíbles. El aumento de velocidad es importante debido a que V8 compila Javascript en código de máquina nativo, en lugar de interpretarlo o ejecutarlo como bytecode. Node es de código abierto, y se ejecuta en Mac OS X, Windows y Linux.

NPM (node package manager) es el gestor de paquetes javascript de NODE.JS por excelencia. Gracias a él, tenemos casi cualquier librería disponible a tan solo una linea de comando de distancia, permitiéndonos utilizarla en cuestión de segundos. NPM nos ayudará a administrar nuestros módulos, distribuir paquetes y agregar dependencias de una manera sencilla.
NPM utiliza el archivo package.json para almacenar todos los datos relevantes a nuestra aplicación. 
Cuando instalamos nuevos paquetes lo que hace npm es instalarlo de manera local en nuestro proyecto dentro de la carpeta node_modules, pero nosotros podemos decirle que lo instale de manera global de ser necesario.

Gulp es un manejador de tareas (Task manager), una increíble herramienta para todo desarrollador que te permitirá de forma automática, gestionar toda clase de tareas comunes y a la vez tediosas en el desarrollo de cualquier aplicación.
Gulp sirve para automatizar tareas. Mediante Gulp se define cómo deben ejecutarse esas tareas, y únicamente ejecutamos un comando en la terminal para que se encargue de hacer todo el trabajo por nosotros.

¿Qué es Bower?
Es un gestor de dependencias desde el lado cliente desarrollado por Twitter, el cual es algo similar a lo que hace composer, npm o nugget. La finalidad de Bower es gestionar las dependencias de nuestros archivos del lado cliente como, bootstrap, jquery, entre otros. Es decir, que vamos a tener todo nuestras dependencias en una archivo json y mediante un comando se descargará todo a nuestro proyecto, si hay futuras versiones de las librerías estas podrán ser actualizadas dejando a un lado la tediosa tarea de ir a la web, descargar y agregarlo manualmente.

Yeoman es un conjunto de herramientas compuesto por Yo, Bower y Grunt , con el que podrás tener en menos de 5 minutos el esqueleto de tu proyecto web. Como ves,  nos ayudará a empezar cualquier proyecto nuevo en cuestión de minutos.

Configuración de entorno de proyecto para el desarrollo de aplicaciones web [UI/UX]

Configuración de entorno de proyecto para el desarrollo de aplicaciones web con una buena experiencia de usuario

Keywords: Node.js, NPM, Bower, Gulp, Yeoman

Para la creación del presente proyecto, necesitaremos Node.js, si aun no lo tienes, puedes descargarlo desde: https://nodejs.org/en/download/

Descargas e instalas Node.js. A continuación, abre tu ventana de comandos. (Si usas Windows, presionas las teclas windows + R y typeas cmd o inicio node command prompt.) En el siguiente paso instalaremos las herramientas que nos permitirán generar la estructura de nuestro proyecto, correr tareas automáticas y gestionar nuestros paquetes.

Te creas un folder en la unidad de tu disco duro que desees. Ingresas al folder y estando adentro ejecutas los siguientes comandos paso a paso:

npm install  --global yo gulp-cli bower

Luego, instalamos el generador de aplicaciones web:

npm install -global generator-webapp

Ahora, ya se puede crear la aplicación con el comando:

yo webapp

Aquí, te pedirá elegir opciones:

Seleccionar las siguientes opciones:
1. which additional features would you like to include?
Elije Sass
2. would you like to include JQuery?
Elije Y (de Yes)
3. choose your style of DSL
Elije BDD porque es orientado al usuario final.

Chequeas tu directorio, si en tu directorio creado, no ves la carpeta bower_components, debes ejecutar el siguiente comando:

bower install

Finalmente, ejecuta el siguiente comando para que corra el servidor de desarrollo

gulp serve

Se te deberá abrir un sitio web base con el siguiente aspecto:



ESTRUCTURA INICIAL DEL PROYECTO.
bower.json: librerias del cliente
package.json: librerias del servidor.
gulpfile.js: donde se registran las tareas automáticas del proyecto incluyendo la recarga automática, mimificacion de archivos, concatenación, entre otros.
carpeta app: se encuentran los archivos del proyecto, incluyendo carpetas para fuentes, imágenes, scripts y estilos.
carpeta scripts: tenemos el main.js archivo de código de script principal.
carpeta styles: tenemos el main.scss, el archivo de  estilo principal.
carpeta bower_components: donde se descargan las librerías del cliente.
carpeta node_module: donde se descargan las librerías del servidor.
carpeta test, se incluyen pruebas automáticas para nuestro proyecto.

INSTALACION FRAMEWORK RESPONSIVE
Para este ejemplo, instalaremos el framework materialize css, puedes utilizar el de tu preferencia. Para ello, ejecutar el comando siguiente dentro del directorio anteriormente creado.

bower install materialize --save

VERIFICACIÓN FRAMEWORK RESPONSIVE

Para verificar que haya sido instalado correctamente el framework, editaremos el archivo index.html que se encuentra dentro del folder app. Agregaremos el siguiente código:


Ahora, tu sitio web tendrá el siguiente aspecto: (recuerda tener levantado tu servidor con el comando gulp serve)


Monday, August 28, 2017

CRUD - ASP.NET MVC 5 + ADO.NET C# - [Eliminar]

Bien, ahora para completar el proyecto nos queda pendiente la vista Eliminar.

En las partes anteriores de esta entrada, aquí vimos las vistas [Listado y Ver] y aquí las vistas [Crear y Editar]

La vista Eliminar luce como sigue:


ProductoController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Business;
using Entity;
using System.Data;

namespace Web.Controllers
{
    public class ProductoController : Controller
    {
        private ProductoBusiness _business;
        private ProveedorBusiness prov;
        private CategoriaBusiness cate;

        public ProductoController()
        {
            _business = new ProductoBusiness();
            prov = new ProveedorBusiness();
            cate = new CategoriaBusiness();
        }



        [HttpGet]
        public ActionResult Eliminar(int id)
        {
            Producto p = new Producto();
            p = _business.GetById(id);
            return View(p);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Eliminar(int id, Producto p)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    if (_business.Delete(id))
                    {
                        WriteMessage(Eoperacion.delete, Etipo.success);
                        ModelState.Clear();
                    }
                    else
                    {
                        WriteMessage(Eoperacion.delete, Etipo.danger);
                    }
                }
            }
            catch (DataException)
            {
                ModelState.AddModelError("", "Unable to delete the record.");
                WriteMessage(Eoperacion.delete, Etipo.danger);
            }
            return View(p);
        }

        #region Metodos Auxiliares      
        public enum Eoperacion : byte
        {
            insert = 1,
            update = 2,
            delete = 3,
            find = 4,
            reset = 5
        }

        public enum Etipo : byte
        {
            success = 1,
            danger = 2,
            info = 3
        }

        public void WriteMessage(Eoperacion Operacion, Etipo Tipo)
        {
            if (Tipo == Etipo.success)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeExito = "El producto ha sido correctamente creado.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeExito = "El producto ha sido correctamente editado.";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeExito = "El producto ha sido correctamente eliminado.";
                        break;

                }
            }
            else if(Tipo == Etipo.danger)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeError = "No se ha podido crear el producto.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeError = "No se ha podido editar el producto";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeError = "No se ha podido eliminar el producto";
                        break;

                }

            }
            else
            {
                switch (Operacion)
                {
                    case Eoperacion.find:
                        ViewBag.MensajeBusqueda = "No se encontraron resultados para su búsqueda.";
                        break;
                    case Eoperacion.reset:
                        ViewBag.MensajeBusqueda = "";
                        break;

                }
            }
        }
        #endregion

    }

}
De igual manera como los otros métodos, clic derecho y agregar vista.

Vista Eliminar Producto

@model Entity.Producto

@{
    ViewBag.Title = "Eliminar Producto";
}

Eliminar Producto

@using (Html.BeginForm()) { @Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Id, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Id, new { htmlAttributes = new { @class = "form-control", @readonly = "readonly" } })
@Html.LabelFor(model => model.Nombre, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Nombre, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Nombre, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Descripcion, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.TextAreaFor(model => model.Descripcion, new { @class = "form-control", @rows = "3", @cols = "50" }) @Html.ValidationMessageFor(model => model.Descripcion, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.PrecioUnitario, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.PrecioUnitario, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.PrecioUnitario, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Stock, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Stock, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Stock, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Proveedor, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Proveedor, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Proveedor, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Categoria, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Categoria, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Categoria, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Estado, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Nombre_Estado, new { htmlAttributes = new { @class = "form-control" } })

|@Html.ActionLink("Regresar al Listado", "Listado")
}
@ViewBag.MensajeExito
@ViewBag.MensajeError

CRUD - ASP.NET MVC 5 + ADO.NET C# - [Crear y Editar]

Ok, continuamos con la creación de nuestros controladores y vistas. Para esta entrada nos concentraremos en la vista Crear Productos y Editar Productos.

En la parte primera de esta entrada, nos enfocamos en las vistas [Listado y Ver]

Las vistas Crear y Editar lucen como sigue (son muy similares, lo único que cambia que Editar lleva el Código en sólo lectura):




ProductoController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Business;
using Entity;
using System.Data;

namespace Web.Controllers
{
    public class ProductoController : Controller
    {
        private ProductoBusiness _business;
        private ProveedorBusiness prov;
        private CategoriaBusiness cate;

        public ProductoController()
        {
            _business = new ProductoBusiness();
            prov = new ProveedorBusiness();
            cate = new CategoriaBusiness();
        }

        [HttpGet]
        public ActionResult Crear()
        {
            ViewBag.ListaProveedores = new SelectList(prov.GetAll(), "Id", "Nombre", "Nombre");
            ViewBag.ListaCategorias = new SelectList(cate.GetAll(), "Id", "Nombre", "Nombre");
            ViewBag.ListaEstados = Utilities.Utilities.GetEstados();
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Crear(Producto p)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    ViewBag.ListaProveedores = new SelectList(prov.GetAll(), "Id", "Nombre", "Nombre");
                    ViewBag.ListaCategorias = new SelectList(cate.GetAll(), "Id", "Nombre", "Nombre");
                    ViewBag.ListaEstados = Utilities.Utilities.GetEstados();

                    if (_business.Insert(p))
                    {
                        WriteMessage(Eoperacion.insert, Etipo.success);
                        //ModelState.Clear();
                    }
                }
                return View();
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", "Error al registrar el producto - " + ex.Message);
                WriteMessage(Eoperacion.insert, Etipo.danger);
                return View();
            }
        }

        [HttpGet]
        public ActionResult Editar(int id)
        {
            ViewBag.ListaProveedores = new SelectList(prov.GetAll(), "Id", "Nombre", "Nombre");
            ViewBag.ListaCategorias = new SelectList(cate.GetAll(), "Id", "Nombre", "Nombre");
            ViewBag.ListaEstados = Utilities.Utilities.GetEstados();

            Producto p = new Producto();
            p = _business.GetById(id);
            return View(p);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Editar(int id, Producto p)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    ViewBag.ListaProveedores = new SelectList(prov.GetAll(), "Id", "Nombre", "Nombre");
                    ViewBag.ListaCategorias = new SelectList(cate.GetAll(), "Id", "Nombre", "Nombre");
                    ViewBag.ListaEstados = Utilities.Utilities.GetEstados();

                    if (_business.Update(p))
                    {
                        WriteMessage(Eoperacion.update, Etipo.success);
                    }
                    else
                    {
                        WriteMessage(Eoperacion.update, Etipo.danger);
                    }
                    ModelState.Clear();
                }
            }
            catch (DataException)
            {
                ModelState.AddModelError("", "Unable to edit employee record.");
                WriteMessage(Eoperacion.update, Etipo.danger);
            }
            return View(p);
        }


        #region Metodos Auxiliares      
        public enum Eoperacion : byte
        {
            insert = 1,
            update = 2,
            delete = 3,
            find = 4,
            reset = 5
        }

        public enum Etipo : byte
        {
            success = 1,
            danger = 2,
            info = 3
        }

        public void WriteMessage(Eoperacion Operacion, Etipo Tipo)
        {
            if (Tipo == Etipo.success)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeExito = "El producto ha sido correctamente creado.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeExito = "El producto ha sido correctamente editado.";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeExito = "El producto ha sido correctamente eliminado.";
                        break;

                }
            }
            else if(Tipo == Etipo.danger)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeError = "No se ha podido crear el producto.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeError = "No se ha podido editar el producto";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeError = "No se ha podido eliminar el producto";
                        break;

                }

            }
            else
            {
                switch (Operacion)
                {
                    case Eoperacion.find:
                        ViewBag.MensajeBusqueda = "No se encontraron resultados para su búsqueda.";
                        break;
                    case Eoperacion.reset:
                        ViewBag.MensajeBusqueda = "";
                        break;

                }
            }
        }
        #endregion

    }

}
Vista Crear Producto

@model Entity.Producto

@{
    ViewBag.Title = "Crear Producto";
}

Crear Nuevo Producto

@using (Html.BeginForm()) { @Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Nombre, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Nombre, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Nombre, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Descripcion, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.TextAreaFor(model => model.Descripcion, new { @class = "form-control", @rows = "3", @cols = "50" }) @Html.ValidationMessageFor(model => model.Descripcion, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.PrecioUnitario, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.PrecioUnitario, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.PrecioUnitario, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Stock, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Stock, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Stock, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Proveedor, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.IdProveedor, (SelectList)ViewBag.ListaProveedores, "", htmlAttributes: new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Proveedor, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Categoria, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.IdCategoria, (SelectList)ViewBag.ListaCategorias, "", htmlAttributes: new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Categoria, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Estado, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.Estado, (List)ViewBag.ListaEstados, htmlAttributes: new { @class = "form-control" })

|@Html.ActionLink("Regresar al Listado", "Listado")
}
@ViewBag.MensajeExito
@ViewBag.MensajeError
Vista Editar Producto
@model Entity.Producto

@{
    ViewBag.Title = "Editar Producto";
}

Editar Producto

@using (Html.BeginForm()) { @Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Id, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Id, new { htmlAttributes = new { @class = "form-control", @readonly = "readonly" } })
@Html.LabelFor(model => model.Nombre, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Nombre, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Nombre, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Descripcion, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.TextAreaFor(model => model.Descripcion, new { @class = "form-control", @rows = "3", @cols = "50" }) @Html.ValidationMessageFor(model => model.Descripcion, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.PrecioUnitario, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.PrecioUnitario, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.PrecioUnitario, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Stock, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.EditorFor(model => model.Stock, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Stock, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Proveedor, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.IdProveedor, (SelectList)ViewBag.ListaProveedores, "", htmlAttributes: new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Proveedor, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Categoria, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.IdCategoria, (SelectList)ViewBag.ListaCategorias, "", htmlAttributes: new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Categoria, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Estado, htmlAttributes: new { @class = "col-sm-3 col-md-4 col-lg-2 col-form-label font-weight-bold text-md-right" })
@Html.DropDownListFor(model => model.Estado, (List)ViewBag.ListaEstados, htmlAttributes: new { @class = "form-control" })

|@Html.ActionLink("Regresar al Listado", "Listado")
}
@ViewBag.MensajeExito
@ViewBag.MensajeError

Sunday, August 27, 2017

CRUD - ASP.NET MVC 5 + ADO.NET C# - [Listado y Ver]

Hola! Continuamos con el proyecto MVC5 + ADO.NET Hasta aquí ya tienes las capas Entity, Data, Buesiness y Web creadas.

Nota. No utilizaré el folder Models que trae consigo MVC5 ya que estoy utilizando la capa Entity la cual viene a ser lo mismo con la diferencia que está separado en un proyecto a parte.

En esta parte de esta entrada únicamente veremos el tema de Listado de Productos y Ver Productos. En las siguientes veremos el de Crear Producto, Editar Producto y Eliminar Producto.

C. Create /Crear Producto
R. Read / Listado de Productos
U. Update /Editar Productos
D. Delete / Eliminar Producto

Empecemos.

Clic derecho Controllers Agregar Controlador Yo lo llamé ProductoController. (ojo, la palabra Controller al final es por convención)

El controlador lo crearemos de cero, sin usar plantillas ni nada de lo que Visual Studio provee. Entonces creemos el constructor, en el constructor iniciaremos nuestro objeto Business que es el que hemos dicho en entradas anteriores que será el encargado de comunicar al proyecto web con la data.

Creamos también los métodos Listado y Ver que devuelven un ActionResult. El método listado tendrá su versión HttpGet y HttpPost.

¿Por qué Listado lleva HttpGet y HttpPost? Get lo ocuparemos cuando cargue la página, será una consulta de todos los productos (hacer un select sin where en caso de que sepas que pueden haber muchas tuplas de resultado sin dudas no es buena práctica) y ocuparemos el Post cuando el usuario interactúe con nuestro sistema de filtrado para listar los productos.

Se tendría que ver algo así:




Nota: Estoy usando un Layout (o master page si vienes de WebForms) En el Layout hago mis llamadas tanto a las hojas de estilos como a los códigos JS. Estoy usando Bootstrap4

El código es como sigue a continuación:

ProdutoController


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Business;
using Entity;
using System.Data;

namespace Web.Controllers
{
    public class ProductoController : Controller
    {
        private ProductoBusiness _business;
        private ProveedorBusiness prov;
        private CategoriaBusiness cate;

        public ProductoController()
        {
            _business = new ProductoBusiness();
            prov = new ProveedorBusiness();
            cate = new CategoriaBusiness();
        }

        public ActionResult Listado()
        {
            ModelState.Clear();
            return View(_business.GetAll());
        }

        [HttpPost]
        public ActionResult Listado(int? txtCodigoProducto, string txtNombreProducto)
        {
            if (txtCodigoProducto == null) {
                txtCodigoProducto = 0;
            }
            //if (txtNombreProducto == string.Empty){
            //    txtNombreProducto = "-1";
            //}

            Producto p = new Producto();
            p.Id = (int)txtCodigoProducto;
            p.Nombre = txtNombreProducto;

            List resultado = (List)_business.SearchBy(p);

            WriteMessage(Eoperacion.reset, Etipo.info);
            if (resultado.Count == 0)
            {
                WriteMessage(Eoperacion.find, Etipo.info);
            }

            return View(resultado);
        }

        public ActionResult Ver(int id)
        {
            Producto p = new Producto();
            p = _business.GetById(id);
            return View(p);
        }

     

        #region Metodos Auxiliares      
        public enum Eoperacion : byte
        {
            insert = 1,
            update = 2,
            delete = 3,
            find = 4,
            reset = 5
        }

        public enum Etipo : byte
        {
            success = 1,
            danger = 2,
            info = 3
        }

        public void WriteMessage(Eoperacion Operacion, Etipo Tipo)
        {
            if (Tipo == Etipo.success)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeExito = "El producto ha sido correctamente creado.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeExito = "El producto ha sido correctamente editado.";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeExito = "El producto ha sido correctamente eliminado.";
                        break;

                }
            }
            else if(Tipo == Etipo.danger)
            {
                switch (Operacion)
                {
                    case Eoperacion.insert:
                        ViewBag.MensajeError = "No se ha podido crear el producto.";
                        break;
                    case Eoperacion.update:
                        ViewBag.MensajeError = "No se ha podido editar el producto";
                        break;
                    case Eoperacion.delete:
                        ViewBag.MensajeError = "No se ha podido eliminar el producto";
                        break;

                }

            }
            else
            {
                switch (Operacion)
                {
                    case Eoperacion.find:
                        ViewBag.MensajeBusqueda = "No se encontraron resultados para su búsqueda.";
                        break;
                    case Eoperacion.reset:
                        ViewBag.MensajeBusqueda = "";
                        break;

                }
            }
        }
        #endregion

    }

}

Perfecto, ya estaría nuestro controlador, ahora hacemos clic derecho sobre el nombre de nuestro método Listado y agregamos la Vista. Visual Studio automáticamente agregará la Vista dentro de la carpeta Views. Aprovechamos para agrega la Vista Ver también.

Cambio importante, dentro del proyecto web, ubicar la carpeta App_Start y dentro del archivo RouteConfig.cs, realizar el siguiente cambio:


 public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Producto", action = "Listado", id = UrlParameter.Optional }
            );
        }


Ok, a continuación expongo el código de la Vista Listado y Vista Ver:

Vista LISTADO DE PRODUCTOS

@model IEnumerable

@{
    ViewBag.Title = "Listado de Productos";
}

Listado de Productos

@using (Html.BeginForm()) {
@Html.Label("Código:", new { @class = "pr-3 control-label text-white d-none d-lg-block" }) @Html.TextBox("txtCodigoProducto", null, new { @class = "form-control", @placeholder = "Código" })
@Html.Label("Nombre:", new { @class = "pr-3 control-label text-white d-none d-lg-block" }) @Html.TextBox("txtNombreProducto", null, new { @class = "form-control", @placeholder = "Nombre" })
}
@foreach (var item in Model) { }
@Html.DisplayNameFor(model => model.Id) @Html.DisplayNameFor(model => model.Nombre) @Html.DisplayNameFor(model => model.Descripcion) @Html.DisplayNameFor(model => model.PrecioUnitario) @Html.DisplayNameFor(model => model.Stock) @Html.DisplayNameFor(model => model.Proveedor) @Html.DisplayNameFor(model => model.Categoria) @Html.DisplayNameFor(model => model.Estado) Acciones
@Html.DisplayFor(mitem => item.Id) @Html.DisplayFor(mitem => item.Nombre) @Html.DisplayFor(mitem => item.Descripcion) @Html.DisplayFor(mitem => item.PrecioUnitario) @Html.DisplayFor(mitem => item.Stock) @Html.DisplayFor(mitem => item.Proveedor) @Html.DisplayFor(mitem => item.Categoria) @{ if (item.Estado == true) { Activo } else { Inactivo } }
@ViewBag.MensajeBusqueda


Vista VER PRODUCTOS

@model Entity.Producto

@{
    ViewBag.Title = "Ver Producto";
}

Información del Producto @(Model.Nombre)

Código:
@Model.Id
Nombre:
@Model.Nombre
Descripción:
@Model.Descripcion
Precio Unitario:
@Model.PrecioUnitario
Stock:
@Model.Stock
Proveedor:
@Model.Proveedor
Categoría:
@Model.Categoria
Estado:
@{ if (Model.Estado == true) { Activo } else { Inactivo } }
@Html.ActionLink("Regresar al Listado", "Listado")

Saturday, August 26, 2017

Web Responsive: por qué a veces preferir el uso de una columna


Aunque el desarrollo de grillas multi-columna ha sido adoptado por muchos diseñadores y desarrolladores en el mundo, en muchas ocasiones es mejor utilizar un diseño simple a una sola columna, especialmente cuando se está ofreciendo un producto o se está generando un contenido extenso para la lectura del usuario.


Según GoodUi, la principal ventaja del uso de una sola columna es el control que se puede generar sobre la narrativa en la pantalla, ésta permite guiar a los lectores de una forma predecible de inicio a final. Cuando se usan varias columnas, se corre el riesgo de distraer al usuario con información adicional que tal vez no requería en este momento, esto es especialmente cierto cuando estamos buscando una llamada a la acción por parte del usuario



Bootstrap 4 Beta. Clase hidden-*-up / hidden-*-down desaparecida.

Hace una semana me descargué la versión Beta y lo implementé en mi proyecto. Tuve la necesidad de ocultar un elemento div para los tamaños md hacia abajo. Intenté con la clase hidden-md-down pero no me funcionó! El tema es que a partir de la versión beta, ésta clase hidden fue eliminada por temas de interferencia con el jquery.

Entonces qué?
Para mi caso bastó lo siguiente:

para desaparecer el bloque:

d-none d-*-block (la d es de display y el * es la medida sm, md, lg, xl)

El siguiente bloque desaparecerá a partir de dispositivos md hacia abajo.

<div class = "d-none d-md-block">
</div>

Bootstrap 4 alpha

<div class="hidden-md-up"></div>

<div class="hidden-md-down"></div>

<span class="hidden-sm-down"></span>

Bootstrap 4 beta

<div class="d-md-none">
<!--
  - Equivalent to: .hidden-md-up
  - display:none md and up.
  - Default mobile-first behaviour is display:block at xs and up
-->

<div class="d-none d-lg-block">
<!--
  - Equivalent to: .hidden-md-down
  - display:none md and down
  - lg is one breakpoint higher than md
-->

<span class="d-none d-md-inline">
<!--
  - Equivalent to: .hidden-sm-down
  - display:none below md (sm-down)
  - display:inline md and up (equivalent to hidden-sm-down)
-->


ASP.NET MVC 5 + ADO.NET C# Parte 2

En la primera parte de esta entrada, vimos las dos primeras capas para nuestro proyecto MVC5. En esta segunda parte, veremos las capas Business y Web.

Parte 1

Tercera Capa: Business

Esta capa encapsula la lógica de la Data y la retorna. En esta capa puedes definir las reglas de negocio.

Agregamos el proyecto tipo Librería de Clases o Class Library a nuestra solución Ventas. Agregamos una nueva clase. A la clase yo la llamé ProductoBusiness Esta capa cobra sentido cuando precisamente le agregas las reglas de negocio. En la mayoría de ejemplos notaras que esta capa la omiten y directamente el proyecto web se comunica con la capa Datos.
El código es como sigue a continuación:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Data;
using Entity;

namespace Business
{
    public class ProductoBusiness
    {
        private ProductoRepository _repository;
       public ProductoBusiness()
        {
            _repository = new ProductoRepository();
        }

        public bool Insert(Producto objProducto)
        {
            return _repository.Insert(objProducto);
        }
        public bool Update(Producto objProducto)
        {
            return _repository.Update(objProducto);
        }

        public bool Delete(int Id)
        {
            return _repository.Delete(Id);
        }

        public IEnumerable GetAll()
        {
            return _repository.GetAll();
        }

        public Producto GetById(int id)
        {
            return _repository.GetById(id);
        }
        public IEnumerable SearchBy(Producto objProducto)
        {
            return _repository.SearchBy(objProducto);
        }
    }
}

Cuarta Capa: Presentación (WEB)

Perfecto, lo que sigue es agregar nuestro proyecto web que será MVC5 (Aplicacion Web ASP.NET)




Le agregamos las referencias Entity y Business.

Lo que sigue a continuación, es trabajar con las interfaces de usuario, haremos un CRUD. Esto le dejaré para una próxima entrada.

Friday, August 25, 2017

ASP.NET MVC 5 + ADO.NET C# Parte 1

Sin mas preámbulos, empecemos a definir la estructura del proyecto.

Me gusta trabajar de la siguiente manera:

1. Entity: Entidades que representan las tablas de base de datos.
2. Data: Hace referencia a la capa Entity. En esta capa suelo usar una clase Connection y una interfaz IRepository, este viene a ser Repository Pattern. Aquí hace uso de nuestro querido amigo ADO.NET
3. Business: Hace referencia a la capa Entity y Data. Encapsula la logica de la Data y la retorna. Es nuestra tradicional capa de negocio. Es la que se comunicara directamente con nuestro proyecto web.
4. Web. Proyecto MVC5, hace referencia a la capa Entity y Business.


Para esta entrada, nos concentraremos en las dos primeras capas, vale decir, en la ENTITY y en la DATA.
Empecemos a crear una solución haciendo uso del Visual Studio 2017 (para mi caso) llamada Ventas. Vamos agregando proyectos a nuestra solución del tipo Class Library o Biblioteca de Clases por cada capa.

Primera Capa: Entity


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Entity
{
    public class Producto   
    {
        [Display(Name = "Código")]
        public int Id { get; set; }

        [Required(ErrorMessage = "El campo {0} es requerido.")]
        [Display(Name = "Nombre")]
        public string Nombre { get; set; }

        [Display(Name = "Descripción")]
        public String Descripcion { get; set; }

        [Required(ErrorMessage = "El campo {0} es requerido.")]
        [Display(Name ="Precio Unitario")]
        public double PrecioUnitario { get; set; }

        [Required(ErrorMessage = "El campo {0} es requerido.")]
        [Display(Name = "Stock")]
        public int Stock { get; set; }

        [Display(Name = "Estado")]
        public bool Estado { get; set; }

        public int IdCategoria { get; set; }
        public int IdProveedor { get; set; }

        public String Categoria { get; set; }
        public String Proveedor { get; set; }

        public String Nombre_Estado { get; set; }

    }
}

Ahora creamos el siguiente proyecto llamado Data.
Segunda Capa: Data


Nuestra capa Data tal como ya se dijo en las primeras lineas de esta entrada, tendremos una clase Connection, la estructura de la misma es como sigue:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace Data
{
    public class Connection
    {
        private static Connection _objCnn = null;
        private SqlConnection _cnn;
        private SqlTransaction _trn;

        private Connection()
        {
            _cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["cnnString"].ToString());
        }

        public static Connection InitializeConnection()
        {
            if (_objCnn == null)
            {
                _objCnn = new Connection();
            }
            return _objCnn;
        }

        public SqlConnection getCnn()
        {
            return _cnn;
        }
        public SqlConnection OpenConnection()
        {
            try
            {
                if (_cnn.State == ConnectionState.Open)
                {
                    return _cnn;
                }
                else
                {
                    _cnn.Open();
                }
                return _cnn;
            }
            catch (Exception)
            {

                throw;
            }
           
        }

        public void CloseConnection()
        {
            try
            {
                if (_cnn != null && _cnn.State != ConnectionState.Closed)
                {
                    _cnn.Close();
                }
            }
            catch (Exception)
            {

                throw;
            }
            
        }
    }
}

Ahora, construimos nuestro REPOSITORY PATTERN

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Data
{
    public interface IRepository where T : class
    {
        bool Insert(T model);
        bool Update(T model);
        bool Delete(int id);
        T GetById(int id);
        IEnumerable GetAll();
        IEnumerable SearchBy(T model);
    }
}


Ok, para finalizar la capa DATA, necesitamos crear una clase la cual implementará la interfaz creada anteriormente. Yo la llamé: ProductoRepository

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Entity;
using System.Data;
using System.Data.SqlClient;

namespace Data
{
    public class ProductoRepository : IRepository
    {
        private Connection _objCnn;
        //private SqlCommand _cmd;
        private SqlDataReader _reader;
        private string _tabla = string.Empty;

        public ProductoRepository()
        {
            _objCnn = Connection.InitializeConnection();
            _tabla = "Productos";
        }
        public bool Delete(int id)
        {
            int i;
            StringBuilder query = new StringBuilder();
            query.AppendFormat("DELETE FROM {0} WHERE id=@Id ", _tabla);

            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    cmd.Parameters.AddWithValue("@id", id);
                    _objCnn.OpenConnection();
                    i = cmd.ExecuteNonQuery();
                }
            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            return i >= 1;
        }

        public IEnumerable GetAll()
        {
            StringBuilder query = new StringBuilder();
            query.AppendLine("SELECT p.id, p.nombre, p.descripcion, p.precio_unitario, p.stock, p.id_proveedor, p.id_categoria, p.estado, c.nombre as categoria, v.nombre as proveedor");
            query.AppendFormat("FROM {0} p ", _tabla);
            query.AppendLine("INNER JOIN Categorias c on c.id=p.id_categoria");
            query.AppendLine("INNER JOIN Proveedores v on v.id=p.id_proveedor");
            List listaProductos = new List();
            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    _objCnn.OpenConnection();
                    _reader = cmd.ExecuteReader();
                    while (_reader.Read())
                    {   
                        Producto objProducto = new Producto();
                        objProducto.Id = Convert.ToInt32(_reader["id"].ToString());
                        objProducto.Nombre = _reader["nombre"].ToString();
                        objProducto.Descripcion = _reader["descripcion"].ToString();
                        objProducto.PrecioUnitario = Convert.ToDouble(_reader["precio_unitario"].ToString());
                        objProducto.Stock = Convert.ToInt32(_reader["stock"].ToString());
                        objProducto.IdProveedor = Convert.ToInt32(_reader["id_proveedor"].ToString());
                        objProducto.IdCategoria = Convert.ToInt32(_reader["id_categoria"].ToString());
                        objProducto.Estado = Convert.ToBoolean(_reader["estado"].ToString());
                        objProducto.Categoria = _reader["categoria"].ToString();
                        objProducto.Proveedor = _reader["proveedor"].ToString();
                        listaProductos.Add(objProducto);
                    }
                }
            }
            catch (Exception)
            {

                //throw;
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            return listaProductos;
        }

        public Producto GetById(int id)
        {
            StringBuilder query = new StringBuilder();

            query.AppendLine("SELECT p.id, p.nombre, p.descripcion, p.precio_unitario, p.stock, p.id_proveedor, p.id_categoria, p.estado, c.nombre as categoria, v.nombre as proveedor, case p.estado when 1 then 'Activo' else 'Inactivo' end as nombre_estado");
            query.AppendFormat("FROM {0} p ", _tabla);
            query.AppendLine("INNER JOIN Categorias c on c.id=p.id_categoria");
            query.AppendLine("INNER JOIN Proveedores v on v.id=p.id_proveedor");
            query.AppendLine("WHERE p.id = @id");

            Producto p = new Producto();
            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    _objCnn.OpenConnection();
                    cmd.Parameters.AddWithValue("@id", id);
                    _reader = cmd.ExecuteReader();
                    if (_reader != null && _reader.HasRows)
                    {
                        _reader.Read();
                        p.Id = Convert.ToInt32(_reader["id"].ToString());
                        p.Nombre = _reader["nombre"].ToString();
                        p.Descripcion = _reader["descripcion"].ToString();
                        p.PrecioUnitario = Convert.ToDouble(_reader["precio_unitario"].ToString());
                        p.Stock = Convert.ToInt32(_reader["stock"].ToString());
                        p.IdProveedor = Convert.ToInt32(_reader["id_proveedor"].ToString());
                        p.IdCategoria = Convert.ToInt32(_reader["id_categoria"].ToString());
                        p.Estado = Convert.ToBoolean(_reader["estado"].ToString());
                        p.Categoria = _reader["categoria"].ToString();
                        p.Proveedor = _reader["proveedor"].ToString();
                        p.Nombre_Estado = _reader["nombre_estado"].ToString();
                    }
                }
            }
            catch (Exception)
            {

                //throw;
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            return p;
        }

        public bool Insert(Producto model)
        {
            //string msg = string.Empty;
            int i;
            StringBuilder query = new StringBuilder();
            query.AppendFormat("INSERT INTO {0} ", _tabla);
            query.AppendLine("(nombre, descripcion, precio_unitario, stock, id_proveedor, id_categoria, estado)");
            query.AppendLine("VALUES");
            query.AppendLine("(@nombre, @descripcion, @precio_unitario, @stock, @id_proveedor, @id_categoria, @estado)");
            //query.AppendLine(";SELECT SCOPE_IDENTITY()");

            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    cmd.Parameters.AddWithValue("@nombre", model.Nombre);
                    cmd.Parameters.AddWithValue("@descripcion", model.Descripcion);
                    cmd.Parameters.AddWithValue("@precio_unitario", model.PrecioUnitario);
                    cmd.Parameters.AddWithValue("@stock", model.Stock);
                    cmd.Parameters.AddWithValue("@id_proveedor", model.IdProveedor);
                    cmd.Parameters.AddWithValue("@id_categoria", model.IdCategoria);
                    cmd.Parameters.AddWithValue("@estado", model.Estado);
                    _objCnn.OpenConnection();
                    //i = (int)cmd.ExecuteScalar();
                    i =cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                //msg= ex.Message;
                throw new Exception(ex.Message);
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            //return i;
            return i >= 1;
        }

        public IEnumerable SearchBy(Producto model)
        {
            StringBuilder query = new StringBuilder();
            query.AppendLine("SELECT p.id, p.nombre, p.descripcion, p.precio_unitario, p.stock, ");
            query.AppendLine("p.id_proveedor, p.id_categoria, p.estado, c.nombre as categoria, v.nombre as proveedor");
            query.AppendFormat("FROM {0} p ", _tabla);
            query.AppendLine("INNER JOIN Categorias c on c.id=p.id_categoria");
            query.AppendLine("INNER JOIN Proveedores v on v.id=p.id_proveedor");
            query.AppendLine("WHERE 1=1 and");

            if (model.Id > 0)
            {
                query.AppendLine("p.id=@id");
            }
            if (model.Nombre != string.Empty)
            {
                string condicion;
                condicion = (model.Id > 0) ? " and p.nombre like '%' + @nombre + '%' " : " p.nombre like '%' + @nombre + '%' ";
                query.AppendLine(condicion);
                //query.AppendLine("p.nombre like '%' + @nombre + '%' ");
            }
            
           List  listaProductos = new List();
            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    _objCnn.OpenConnection();
                    cmd.Parameters.AddWithValue("@id", model.Id);
                    cmd.Parameters.AddWithValue("@nombre", model.Nombre);

                    _reader = cmd.ExecuteReader();
                    while (_reader.Read())
                    {
                        Producto objProducto = new Producto();
                        objProducto.Id = Convert.ToInt32(_reader["id"].ToString());
                        objProducto.Nombre = _reader["nombre"].ToString();
                        objProducto.Descripcion = _reader["descripcion"].ToString();
                        objProducto.PrecioUnitario = Convert.ToDouble(_reader["precio_unitario"].ToString());
                        objProducto.Stock = Convert.ToInt32(_reader["stock"].ToString());
                        objProducto.IdProveedor = Convert.ToInt32(_reader["id_proveedor"].ToString());
                        objProducto.IdCategoria = Convert.ToInt32(_reader["id_categoria"].ToString());
                        objProducto.Estado = Convert.ToBoolean(_reader["estado"].ToString());
                        objProducto.Categoria = _reader["categoria"].ToString();
                        objProducto.Proveedor = _reader["proveedor"].ToString();
                        listaProductos.Add(objProducto);
                    }
                }
            }
            catch (Exception)
            {

                //throw;
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            return listaProductos;
        }

        public bool Update(Producto model)
        {
            int i;
            StringBuilder query = new StringBuilder();
            query.AppendFormat("UPDATE {0} ", _tabla);
            query.AppendLine("SET nombre = @nombre, descripcion = @descripcion, precio_unitario = @precio_unitario, stock = @stock,");
            query.AppendLine("id_proveedor = @id_proveedor, id_categoria = @id_categoria, estado = @estado");
            query.AppendLine("WHERE id = @id");

            try
            {
                using (SqlCommand cmd = new SqlCommand(query.ToString(), _objCnn.getCnn()))
                {
                    cmd.Parameters.AddWithValue("@nombre", model.Nombre);
                    cmd.Parameters.AddWithValue("@descripcion", model.Descripcion);
                    cmd.Parameters.AddWithValue("@precio_unitario", model.PrecioUnitario);
                    cmd.Parameters.AddWithValue("@stock", model.Stock);
                    cmd.Parameters.AddWithValue("@id_proveedor", model.IdProveedor);
                    cmd.Parameters.AddWithValue("@id_categoria", model.IdCategoria);
                    cmd.Parameters.AddWithValue("@estado", model.Estado);
                    cmd.Parameters.AddWithValue("@id", model.Id);
                    _objCnn.OpenConnection();
                    i = cmd.ExecuteNonQuery();
                }
            }
            catch (Exception)
            {
                return false;
                //throw;
            }
            finally
            {
                _objCnn.CloseConnection();
            }
            return i >= 1;
        }
    }
}

Agregar icono a un input

Keywords: input with icon, input font awesome, how to add icon into input



Ok, la idea es agregar un icono a un elemento html input tal como lo hacemos a un elemento anchor el cual es sencillo puesto que tiene etiqueta de cierre. Sin embargo, en el caso del input, que lo necesitamos por el submit, NO tiene un etiqueta de cierre. Podemos hacer lo siguiente:




La solución que yo empleo es el caso 2.
Para el caso 3, el link para sacar el UNICODE es el siguiente: http://fontawesome.io/cheatsheet/
Nota.- Los ejemplos mostrados han sido realizados utilizando Bootstrap 3 ó 4

Sunday, August 6, 2017

Preguntas respuestas Android 4

1. If we add the following property to a Widget:

 


The result is:
The Widget will show a splash screen using the preview image
The preview image will be set as a background on the Widget
The user will be able to see a preview of what the app widget looks like 

2. What is a Keystore on Android?
Contains keys to decompile different builds of an Android application
Contain access to private files on the project
Is a binary file that contains one or more private keys

3. Is it possible that Google Play Store accept an APK signed with a debug certificate for publishing?
No 
Yes

4. If we create a Widget with the following specifications:

minWidth: 110dp

minHeight: 250dp

How many columns and rows the widget is going to take on the screen (https://developer.android.com/guide/practices/ui_guidelines/widget_design.html)?
2 columns, 4 rows 
3 columns, 4 rows
2 columns, 2 rows

5. If we are creating a Widget which of the following class we need to declare on the AndroidManifest.xml?
AppWidgetProvider 
AppWidgetProviderInfo
WidgetContentProvider

6. What is a AppWidgetProvider class
A class that defines the basic widget information
Defines the basic methods that allow you to programmatically interact with the widget 
Is a class that extends from an Activity

7. Which property we need to set on the AppWidgetProviderInfo to defines how often the App Widget should update?
android:update
android:updatePeriod
android:updatePeriodMillis 

8. Is it possible to use Android Studio to generate signed APKs ?
Yes 
No
It is only possible on the command line

9. What is a Widget?
A View that displays a constraint layout
Widgets are miniature application views that can be embedded in other applications 
Widgets are a complete application views that can be merged in other applications

10. Is it necessary to define a initial layout for a widget?
No
Yes