Formulario A-B-C con SQL Server, ASP.NET y AngularJS

Hola tyros, yo de nuevo.

En días pasados les compartí un tutorial homónimo a este, pero en PHP. Hoy vamos a tratar de obtener exactamente el mismo resultado pero utilizando ASP.NET.
El proceso es realmente muy fácil. Del lado del HTML los cambios son mínimos y del lado del C# el código que hay que escribir es menos y mas organizado que PHP.
Como en el ejercicio anterior, voy a evitar explicaciones innecesarias. Lo que aquí voy a exponer no lo aprendí en la universidad, tampoco en algún curso de certificación, de hecho, como la mayoría de mis lectores, lo aprendo todo de otros que lo intentaron primero, lo lograron y lo publicaron en Internet.
Las ideas que voy a presentar son muy básicas. No se requieren conocimientos avanzados. Me atrevo a decir que la tradición copy-paste es suficiente para resulverlo; sin embargo, se requerirá un poquito mas para escalarlo y adaptarlo a necesidades particulares.
Si eres de aquellos que les gusta comparar y elegir la mejor opción de hacer las cosas, te recomiento leer la versión PHP de este tutorial.



Requisitos Previos

1. Microsoft SQL Server 2008 Express o superior
2. Microsoft SQL Server Management Studio
3. Microsoft Visual Studio 2017 Comunity o Superior

Para obviar la parte de SQL Server: Abrir SQL Server Management Studio, crear una base de datos llamada "ejemplos". Ejecutar el siguiente script:

use ejemplos
go
if exists(select 1 from sys.objects where name='productos')
 drop table dbo.productos
go
create table dbo.productos
(
 id_producto int not null identity(1,1),
    codigo_barras nvarchar(50) not null,
    nombre_producto nvarchar(100) not null,
    stock float not null default 0,
    precio_venta float not null default 0,
    constraint pk_productos primary key(id_producto),
 constraint uq_productos_codigo_barras unique(codigo_barras)
)
go
/*insertamos datos de prueba*/
insert into dbo.productos(codigo_barras, nombre_producto, stock, precio_venta) values('0000000001','COCA-COLA 600 ML', 10, 15)
insert into dbo.productos(codigo_barras, nombre_producto, stock, precio_venta) values('0000000002','COCA-COLA LIGHT 600 ML', 10, 15)
insert into dbo.productos(codigo_barras, nombre_producto, stock, precio_venta) values('0000000003','SPRITE 600 ML', 10, 15)
/*mostrar datos*/
select * from dbo.productos
/*procedimientos almacenados*/
if exists(select 1 from sys.objects where name='proc_ProductoBuscar')
 drop procedure dbo.proc_ProductoBuscar
go
create procedure dbo.proc_ProductoBuscar
 @texto_buscar nvarchar(50)
as
begin
 select p.id_producto, p.codigo_barras, p.nombre_producto, p.stock, p.precio_venta
    from dbo.productos p
    where concat(p.codigo_barras,' ', p.nombre_producto) like concat('%', @texto_buscar, '%')
end
go
if exists(select 1 from sys.objects where name='proc_ProductoGrabar')
 drop procedure dbo.proc_ProductoGrabar
go
create procedure dbo.proc_ProductoGrabar
 @id_producto int,
    @codigo_barras nvarchar(50),
    @nombre_producto nvarchar(100),
    @stock float,
    @precio_venta float
as
begin
 if (@id_producto = 0) 
  begin
   if exists(select 1 from dbo.productos where codigo_barras = @codigo_barras) 
    goto error_repetido
   /*Insertar registro*/
   insert into dbo.productos(codigo_barras, nombre_producto, stock, precio_venta) 
            values(@codigo_barras,@nombre_producto, @stock, @precio_venta)
            /*Obtener Id generado*/
            set @id_producto = @@IDENTITY

  end
 else
  begin
   if exists(select 1 from dbo.productos where codigo_barras = @codigo_barras and id_producto <> @id_producto) 
    goto error_repetido
   /*Actualizar registro*/
   update dbo.productos set
            codigo_barras = @codigo_barras,
            nombre_producto = @nombre_producto,
            stock = @stock,
            precio_venta = @precio_venta
            where id_producto = @id_producto
        end
    select @id_producto
 return
error_repetido:
 raiserror('Ya existe otro producto con el mismo codigo de barras.',16,1)
 return
end
go
if exists(select 1 from sys.objects where name='proc_ProductoEliminar')
 drop procedure dbo.proc_ProductoEliminar
go
create procedure dbo.proc_ProductoEliminar
 @id_producto int
as
begin
 delete
    from dbo.productos 
    where id_producto=@id_producto
    select @id_producto
end
go

Para comenzar, vamos a agregar una página ASP.NET con C# como lenguaje, con el siguiente código base:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Formulario A-B-C</title>
    <!--Referencias CSS-->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="frmFormularioABC" runat="server">
        <div>
            <!--PONER AQUÍ EL CÓDIGO HTML-->
        </div>
    </form>
    <!--Referencias Javascript-->
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js" type="text/javascript"></script>
</body>
</html>

Si lo ejecutamos, se vería así:
Ahora hagamos algunos cambios para darle algo de forma. Modifiquemos el código para que quede así:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="formulario-abc.aspx.cs" Inherits="examples_formulario_abc" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Formulario A-B-C</title>
    <!--Referencias CSS-->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="frmFormularioABC" runat="server">
        <!--PONER AQUÍ EL CÓDIGO HTML-->
        <div class="container-fluid">
            <h1>Formulario A-B-C</h1>
            <hr />
            <a href="#" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Agregar nuevo producto</a>
            <br />
            <br />
            <div class="row">
                <div class="col-sm-6">
                    <div class="form-group input-group">
                        <input type="text" class="form-control" placeholder="Código de barras, Nombre del producto" />
                        <span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-sm-12">
                    <table class="table table-bordered table-striped table-hover">
                        <thead>
                            <tr>
                                <th colspan="6" class="text-center">Listado de Productos</th>
                            </tr>
                            <tr>
                                <th class="text-center"><i class="glyphicon glyphicon-pencil"></i></th>
                                <th class="text-center"><i class="glyphicon glyphicon-trash"></i></th>
                                <th>Código de barras</th>
                                <th>Nombre Producto</th>
                                <th class="text-right">Stock</th>
                                <th class="text-right">Precio Venta</th>
                            </tr>
                        </thead>
                    </table>
                </div>
            </div>
            <div class="row text-center">
                <small><a href="http://www.tyrodeveloper.com">www.tyrodeveloper.com</a></small>
            </div>
        </div>
    </form>
    <!--Referencias Javascript-->
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" type="text/javascript"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        //Código Javascript
    </script>
</body>
</html>

Con estos cambios, debe tener la siguiente apariencia:
Hagamos una pausa en este punto. Revisemos que todo vaya bien. Es buena práctica abrir la consola de desarrollador que ofrecen la mayoría de los navegadores de internet para detectar posibles errores.

A continuación vamos a habilitar AngularJS en nuestro proyecto. Anteriormente ya agregamos las referencias CSS y Javascript que necesitabamos, sin embargo, se requieren unos pequeños ajustes adicionales.
Con mucho cuidado, ubicamos la etiqueta "body" de nuestro documento ASP.NET y la modificamos así:

<body id="ng-producto-lista" ng-app="appCatalogos" ng-controller="cProductos">

Además, en el código Javascript, agregamos lo siguiente:

var myApp = angular.module('appCatalogos', []);


Antes de continuar, agregamos una clase llamada "Productos" con el siguiente código:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

/// <summary>
/// Programador: tyrodeveloper
/// </summary>
public class Productos
{
    public int id_producto = 0;
    public string codigo_barras = "";
    public string nombre_producto = "";
    public double stock = 0;
    public double precio_venta = 0;
    //Cadena de Conexión
    static string CnnStr = "Data Source=.;Initial Catalog = ejemplos; User Id=sa;Password = <p@ssword>";
    public List<Productos> BuscarProducto(string textoBuscar) {
        SqlConnection cnn = new SqlConnection(CnnStr);
        List<Productos> result = new List<Productos>();
        try
        {
            cnn.Open();
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = cnn;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "proc_ProductoBuscar";
            cmd.Parameters.Add("@texto_buscar", SqlDbType.NVarChar, 50).Value = textoBuscar;
            SqlDataReader dr = cmd.ExecuteReader();      
            while (dr.Read())
            {
                result.Add(new Productos
                {
                    id_producto = Convert.ToInt32(dr["id_producto"]),
                    codigo_barras = dr["codigo_barras"].ToString(),
                    nombre_producto = dr["nombre_producto"].ToString(),
                    stock = Convert.ToDouble(dr["stock"]),
                    precio_venta = Convert.ToDouble(dr["precio_venta"])
                });
            }
            dr.Close();
            return result;
        }
        catch (Exception ex) { throw (ex); }
        finally { cnn.Close(); cnn.Dispose(); }
    }
    public struct Message {
        public int status;
        public string message;
        public string date;
    }
    public Message GrabarProducto()
    {
        SqlConnection cnn = new SqlConnection(CnnStr);
        try
        {
            cnn.Open();
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = cnn;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "proc_ProductoGrabar";
            cmd.Parameters.Add("@id_producto", SqlDbType.Int).Value = id_producto;
            cmd.Parameters.Add("@codigo_barras", SqlDbType.NVarChar, 50).Value = codigo_barras;
            cmd.Parameters.Add("@nombre_producto", SqlDbType.NVarChar, 100).Value = nombre_producto;
            cmd.Parameters.Add("@stock", SqlDbType.Float).Value = stock;
            cmd.Parameters.Add("@precio_venta", SqlDbType.Float).Value = precio_venta;
            //Grabar y Obtener el ID       
            id_producto =Convert.ToInt32( cmd.ExecuteScalar());
            return new Message()
            {
                status = 1,
                message = "El producto ha sido grabado correctamente.",
                date = String.Format("{0:dd/MM/yyyy HH:mm}", DateTime.Now)
            };
        }
        catch (Exception ex) {
            return new Message()
            {
                status = 0,
                message = ex.Message,
                date = String.Format("{0:dd/MM/yyyy HH:mm}", DateTime.Now)
            };
        }
        finally { cnn.Close(); cnn.Dispose(); }
    }
    public Message EliminarProducto(int idProducto)
    {
        SqlConnection cnn = new SqlConnection(CnnStr);
        try
        {
            cnn.Open();
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = cnn;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "proc_ProductoEliminar";
            cmd.Parameters.Add("@id_producto", SqlDbType.NVarChar, 50).Value = idProducto;
            //Eliminar y Obtener el ID       
            id_producto = Convert.ToInt32(cmd.ExecuteScalar());
            return new Message()
            {
                status = 1,
                message = "El producto ha sido eliminado.",
                date = String.Format("{0:dd/MM/yyyy HH:mm}", DateTime.Now)
            };
        }
        catch (Exception ex)
        {
            return new Message()
            {
                status = 0,
                message = ex.Message,
                date = String.Format("{0:dd/MM/yyyy HH:mm}", DateTime.Now)
            };
        }
        finally { cnn.Close(); cnn.Dispose(); }
    }
}


Mostrar listado de productos

En nuestro código HTML, tenemos un objeto:

<input type="text" class="form-control" placeholder="Código de barras, Nombre del producto" />

Lo modificamos para que quede así:

<input type="text" class="form-control" ng-keyup="$event.keyCode == 13 ? BuscarProducto() : null" id="txtTextoBuscar" placeholder="Código de barras, Nombre del producto" />

Esto, lo que hará es que cuando el cursor se encuentre en el objeto y se presione la tecla "Enter" se ejecute la función "BuscarProducto", a cual crearemos en un momento mas.

Luego, modificamos la tabla para que, en su conjunto, quede así:

<tbody>
 <tr ng-repeat="producto in listaProductos">
  <td class="text-center"><a href="#" class="btn btn-sm btn-primary"><i class="glyphicon glyphicon-pencil"></i></a></td>
  <td class="text-center"><a href="#" class="btn btn-sm btn-danger"><i class="glyphicon glyphicon-trash"></i></a></td>
  <td>{{producto.codigo_barras}}</td>
  <td>{{producto.nombre_producto}}</td>
  <td class="text-right">{{producto.stock|number:2}}</td>
  <td class="text-right">{{producto.precio_venta| currency}}</td>
 </tr>
</tbody>

Para, "casi", finalizar esta primera parte, agregamos el siguiente código Javascript:

myApp.controller('cProductos', function ($scope, $http) {
 var myData = { textoBuscar: '' };
 $scope.producto = { id_producto: 0, codigo_barras: "", nombre_producto: "", stock: 0, precio_venta: 0 };
 var http = $http({
  method: "POST",
  url: "formulario-abc.aspx/BuscarProducto",
  data: myData,
  headers: { "Content-Type": "application/json" },
  dataType: "json"
 }).success(function (response) {
  $scope.listaProductos = JSON.parse(response.d);
 });
 $scope.BuscarProducto = function () {
  var myData = { textoBuscar: String($("#txtTextoBuscar").val()) };
  $http({
   method: "POST",
   url: 'formulario-abc.aspx/BuscarProducto',
   data: myData,
   headers: { "Content-Type": "application/json" },
   dataType: "json"
  }).success(function (response) {
   $scope.listaProductos = JSON.parse(response.d);
  });
 };
 //Aquí agregaremos mas código
});

Ahora, en el código C# de "formulario-abc.aspx"...

Agregamos la siguientes directivas (Solo en caso de que hagan falta):

using System;
using System.Web.Script.Serialization;
using System.Web.Services;

Entonces, agregamos un WebMethod:

[WebMethod]
public static string BuscarProducto(string textoBuscar) {
 Productos prod = new Productos();
 return new JavaScriptSerializer().Serialize(prod.BuscarProducto(textoBuscar));
}

Este es el resultado que conseguimos:
Hay que detenernos un momento y revisar que funcione como se espera.

Grabando Productos

En esta segunda parte agregaremos un formulario "modal", de tal manera que cuando demos clic en el botón correspondiente para editar se abra y podamos, ya sea, agregar un producto nuevo o editar uno existente.

El código para agregar la ventana "modal" es el siguiente:

<!-- Modal Producto -->
<div id="modalProducto" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal">×</button>
    <h4 class="modal-title">Producto</h4>
   </div>
   <div class="modal-body">
    <div class="row">
     <div class="col-sm-6">
      <label>Código de barras:</label>
      <div class="form-group input-group">

       <input type="text" id="txtCodigoBarras" ng-model="producto.codigo_barras" class="form-control" />
       <span class="input-group-addon"><i class="glyphicon glyphicon-barcode"></i></span>
      </div>
     </div>
    </div>
    <div class="row">
     <div class="col-sm-12">
      <div class="form-group">
       <label>Nombre del producto:</label>
       <input type="text" id="txtNombreProducto" ng-model="producto.nombre_producto" class="form-control" />
      </div>
     </div>
    </div>
    <div class="row">
     <div class="col-sm-6">
      <label>Stock:</label>
      <div class="form-group input-group">
       <input type="text" id="txtStock" ng-model="producto.stock" class="form-control" />
       <span class="input-group-addon">0.00</span>
      </div>
     </div>
     <div class="col-sm-6">
      <label>Precio Venta:</label>
      <div class="form-group input-group">

       <input type="text" id="txtStock" ng-model="producto.precio_venta" class="form-control" />
       <span class="input-group-addon"><i class="glyphicon glyphicon-usd"></i></span>
      </div>
     </div>
    </div>
   </div>
   <div class="modal-footer">
    <button type="button" ng-click="Grabar()" class="btn btn-primary"><i class="glyphicon glyphicon-floppy-disk"></i> Grabar</button>
    <button type="button" class="btn btn-default" data-dismiss="modal"><i class="glyphicon glyphicon-remove"></i> Cancelar</button>
   </div>
  </div>
 </div>
</div>
<!-- Modal Producto -->

Ubicamos el botón "+ Agregar nuevo producto":

<a href="#" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> Agregar nuevo producto</a>

Y lo dejamos así:

<a href="#" class="btn btn-primary" ng-click="AbrirNuevo();"><i class="glyphicon glyphicon-plus"></i> Agregar nuevo producto</a>

También ubicamos la línea en donde se ubica el botón para editar:

<td class="text-center"><a href="#" class="btn btn-sm btn-primary"><i class="glyphicon glyphicon-pencil"></i></a></td>

La modificamos:

<td class="text-center"><a href="#" ng-click="AbrirEditar(this.producto);" class="btn btn-sm btn-primary"><i class="glyphicon glyphicon-pencil"></i></a></td>


El el código Javascript, justo enseguida de la marca que habíamos dejado:

//Aquí agregaremos mas código

Agregamos el siguiente código:

$scope.AbrirEditar = function (item) {
 $scope.producto = item;
 $("#modalProducto").modal();
};
$scope.AbrirNuevo = function () {
 $scope.producto = { id_producto: 0, codigo_barras: "", nombre_producto: "", stock: 0, precio_venta: 0 };
 $("#modalProducto").modal();
};
$scope.Grabar = function () {
 $http({
  method: "POST",
  url: 'formulario-abc.aspx/GrabarProducto',
  data: $scope.producto
 }).success(function (response) {
  var resp = JSON.parse(response.d); 
  if (resp.status === 1) {
   alert(resp.message);
   $scope.BuscarProducto();
   $("#modalProducto").modal("hide");
  } else {
   alert(resp.message);
  }
 });
};

Para finalizar esta segunda parte, en el código C#, agregamos un WebMethod llamado "GrabarProducto"

[WebMethod]
public static string GrabarProducto(int id_producto, string codigo_barras, string nombre_producto, double stock, double precio_venta)
{
 Productos prod = new Productos();
 prod.id_producto = id_producto;
 prod.codigo_barras = codigo_barras;
 prod.nombre_producto = nombre_producto;
 prod.stock = stock;
 prod.precio_venta = precio_venta;
 return new JavaScriptSerializer().Serialize(prod.GrabarProducto());
}

Listo, ahora podemos hacer pruebas.

Si hacemos clic en el botón "+ Agregar nuevo producto", se verá lo siguiente:
Luego, llenamos todos los datos (escribimos a propósito un código de barras que ya existe):
Corregimos el código de barras:
Se agrega el producto:

Eliminar Productos

En esta tercera, y última parte de nuestro ejercicio, vamos a agregar la funcionalidad para Eliminar.

Agregamos el siguiente código HTML, el cual agregará un "modal". Así, cuando hagamos clic en el botón correspondiente, se abrirá:

<!-- Modal Eliminar-->
<div id="modalProductoEliminar" class="modal fade" role="dialog">
 <div class="modal-dialog">
  <!-- Modal content-->
  <div class="modal-content">
   <div class="modal-header text-center">
    <button type="button" class="close" data-dismiss="modal">×</button>
    <h4 class="modal-title" style="color: red;">¿Realmente desea eliminar el producto?</h4>
   </div>
   <div class="modal-body">
    <div class="row">
     <div class="col-sm-6">
      <label>Código de barras:</label>
      <div class="form-group input-group">

       <input type="text" ng-model="producto.codigo_barras" readonly="true" class="form-control" />
       <span class="input-group-addon"><i class="glyphicon glyphicon-barcode"></i></span>
      </div>
     </div>
    </div>
    <div class="row">
     <div class="col-sm-12">
      <div class="form-group">
       <label>Nombre del producto:</label>
       <input type="text" ng-model="producto.nombre_producto" readonly="true" class="form-control" />
      </div>
     </div>
    </div>
   </div>
   <div class="modal-footer">
    <button type="button" ng-click="Eliminar()" class="btn btn-danger"><i class="glyphicon glyphicon-trash"></i> Eliminar</button>
    <button type="button" class="btn btn-default" data-dismiss="modal"><i class="glyphicon glyphicon-remove"></i> Cancelar</button>
   </div>
  </div>
 </div>
</div>
<!-- Modal Eliminar-->

Luego, en la sección donde estamos poniendo el código Javascript, agregamos lo siguiente:

$scope.AbrirEliminar = function (item) {
 $scope.producto = item;
 $("#modalProductoEliminar").modal();
};
$scope.Eliminar = function () {
 $http({
  method: "POST",
  url: 'formulario-abc.aspx/EliminarProducto',
  data: $scope.producto
 }).success(function (response) {
  var resp = JSON.parse(response.d);
  if (resp.status === 1) {
   alert(resp.message);
   $scope.BuscarProducto();
   $("#modalProductoEliminar").modal("hide");
  } else {
   alert(resp.message);
  }
 });
};

En la tabla, ubicamos la línea en donde se ubica el botón para eliminar:

<td class="text-center"><a href="#" class="btn btn-sm btn-danger"><i class="glyphicon glyphicon-trash"></i></a></td>

Lo modificamos:

<td class="text-center"><a href="#" ng-click="AbrirEliminar(this.producto);" class="btn btn-sm btn-danger"><i class="glyphicon glyphicon-trash"></i></a></td>

Por último, en el código C#, agregamos un WebMethod:

[WebMethod]
public static string EliminarProducto(int id_producto)
{
 Productos prod = new Productos();
 return new JavaScriptSerializer().Serialize(prod.EliminarProducto(id_producto));
}


Ahora hacemos unas pruebas:

Si damos clic en el botón para eliminar, veremos lo siguiente:
Si damos clic en el botón "Eliminar":

Listo, hemos terminado.

No olvides hacer G+1

1 comentario: