Blog sobre desarrollo de aplicaciones Software basado en tecnología Microsoft (.Net/SQL/IIS) y otras (php/MySQL/Apache/Android...).
martes, febrero 21, 2012
Autenticación ASP.Net
lunes, febrero 06, 2012
Redirigir a pantalla de Logon cuando el usuario no está "logado"
Respuesta: Simplemente añadiendo el atributo [Authorize] a la acción. Ejemplo:
public class MiController : Controller { // GET [Authorize] public ViewResult Index() {
[...]
}
}
viernes, enero 27, 2012
Guardados parciales de formularios web mediante Ajax
Código javascript:
// Variable donde se guardan los valores de la última grabación automática. var _savedFormValues;
// Carga del formulario (jQuery) $(document).ready(function () { // Guardado automático (lanzado por el sistema). Mientras no se diga lo contrario (=0) los guardados son de tipo automático (=-1) $('#GuardadoAutomatico').value = -1; // Inicializar campos _savedFormValues _savedFormValues = $(document.forms[0]).serialize(); }
// Guardado asíncrono (no se espera al retorno) function guardarAutomatico() { // Serializar los valores actuales de la pantalla currentFormValues = $(document.forms[0]).serialize(); // Comparar los valores actuales con los últimos guardados if (currentFormValues != _savedFormValues) { // Lanzar POST al controller, mediante ajax (asíncrono y transparente para el usuario) $.ajax({ type: 'POST', url: $(this).attr('action'), cache: false, data: currentFormValues //,success: function (data) { //$('#result').html(data); // result es un campo del formulario donde escribir el retorno del POST; en este caso no se utiliza. //} }); // Actualizar los últimos valores guardados _savedFormValues = currentFormValues; } }
NOTAS: Obsérvese...
1) Cómo se recogen todos los valores del formulario y se serializan para pasarlos al post: $(document.forms[0]).serialize()
2) Si no hay cambios, no se hace el post.
3) El campo GuardadoAutomatico, con el que controlaremos si el guardado es automático/parcial o no. Sólo en el guardado final deberemos establecerlo a 0.
El controller sería algo así:
[HttpPost] public ActionResult Edit(MiViewModel viewModel) {
...
if (viewModel.GuardadoAutomatico == -1) { // Guardar parcial automático
...
// No redirigir return null; } else {
// Guardar normal lanzado por el usuario
...
// Redirigir a donde toque... return RedirectToAction("Index", "PáginaX"); }
}
Gestión de cookies en javascript
Funciones javascript para leer, crear o borrar cookies (Copiado de http://www.quirksmode.org/js/cookies.html):
function createCookie(name, value, days) { var expires = ""; if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toGMTString(); } document.cookie = name + "=" + value + expires + "; path=/"; } function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } function eraseCookie(name) { createCookie(name, "", -1); }
jueves, enero 12, 2012
Combos en cascada en ASP.Net MVC Razor mediante Ajax y Json utilizando Ajax.BeginForm
@model IEnumerable<Dream.Models.Articulo> @{ ViewBag.Title = "Referencias"; Layout = "~/Views/Shared/_Layout.cshtml"; } <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script type="text/javascript"> $.ajaxSetup({ cache: false }); // Inicialización $(document).ready(function () { _$("comboGruposDeProductos").options[0].disabled = true; _$("comboProductos").disabled = true; }); // Carga el desplegable de productos en función // del grupo que le llega como parámetro. function actualizaComboProductos(grupoDeProductos) { // Eliminar el elemento "0" = "<Selecciona grupo>" si es que aún existe if (_$("comboGruposDeProductos").options[0].value == "0") _$("comboGruposDeProductos").options[0] = null; // Limpiar combo de Productos y poner "Espere..." mientras se carga. var dd = _$("comboProductos"); dd.options.length = 0; dd.options[0] = new Option("Espere..."); dd.selectedIndex = 0; dd.disabled = true; // Control de errores $("#comboProductos").ajaxError(function (event, request, settings) { dd.options[0] = new Option("Grupo incorrecto"); }); // Obtenemos los datos... var selectedProductIndex = 0; $.getJSON( '@Url.Action("GetJsonProductSelectList")', // URL a la acción {grupo: grupoDeProductos }, // Objeto JSON con parámetros function (data) { // Función de retorno exitoso dd.options[0] = new Option("<Selecciona producto>", "0"); dd.options[0].disabled = true; $.each(data, function (i, item) { dd.options[i + 1] = new Option(item.Text, item.Value); }); dd.selectedIndex = selectedProductIndex; dd.disabled = false; } ); } //Recargar lista de artículos: se utiliza submit para que MVC haga una llamada ajax function actualizaListaDeArticulos() { $('form').submit(); // Eliminar el elemento "0" = "<Selecciona producto>" si es que aún existe if (_$("comboProductos").options[0].value == "0") _$("comboProductos").options[0] = null; } function _$(id) { return document.getElementById(id); } </script> <h3>@Request.Params["vehiculo"]</h3> @using (Ajax.BeginForm("ItemListGrid", new { idCar = int.Parse(Request["idCar"]) }, new AjaxOptions { UpdateTargetId = "divReferencias", LoadingElementId = "divCargando", HttpMethod = "GET", InsertionMode = InsertionMode.Replace, OnBegin = "_$(\"divReferencias\").style.display = \"none\";", OnComplete = "_$(\"divReferencias\").style.display = \"block\";" }, new { name = "formReferencias", style = "display:fixed" })) { @Html.DropDownList("comboGruposDeProductos", (SelectList)ViewData["ProductGroupSelectList"], new { onchange = "actualizaComboProductos(this.options[this.selectedIndex].value)", style = "width: 300px; display:fixed" }) var productSelectList = new List<SelectListItem>(); // De entrada no necesito nada en el 2º combo... @Html.DropDownList("comboProductos", productSelectList, new { onchange = "actualizaListaDeArticulos()", style = "width: 400px" }) } <div id="divReferencias" class="tabletGrid"> @Html.Partial("ItemListGrid", Model) </div> <!--div id="divCargando" class="divCargando"><p>Cargando...</p><img src=@Url.Content("~/Images/cargando2.gif") alt="Cargando..." title="Cargando..." /></div!!!-->
Otras entradas del blog relacinadas:
Carga de combos en cascada en página web mediante Ajax y Json
Actualización de datos en página web mediante AJAX y JSON
Ejemplo de uso de WebGrid en Razor
Definición del HTML que contiene un grid, utilizando sintaxis Razor
@model IEnumerable<Dream.Models.Articulo> @{ if (Model != null) { var grid = new WebGrid( source: Model, canPage: false, canSort: false ); @grid.GetHtml( //rowStyle: @MyHelpers.Utils.GetArticleLineStyleClass(p => p.codProdAE, 5), //"font-size:1.3em", columns: new[] { grid.Column("marca", "Fabricante", format: p => @MyHelpers.HtmlHelpers.GetArticleLineStyleClass(p.codProdAE, p.stock, p.marca), style: "leftAlign" ), grid.Column("articulo", "Ref. Fabricante", format: p => @MyHelpers.HtmlHelpers.GetArticleLineStyleClass(p.codProdAE, p.stock, p.articulo), style: "leftAlign" ), grid.Column("codProdAE", "Ref. Autoequip", format: p => @MyHelpers.HtmlHelpers.GetArticleLineStyleClass(p.codProdAE, p.stock, p.codProdAE, false), style: "centerAlign200" ), grid.Column("stock", "Stock", format: p => @MyHelpers.HtmlHelpers.GetArticleLineStyleClass(p.codProdAE, p.stock, p.stock.ToString(), false), //format: p => (p.codProdAE == "") ? "" : p.stock.ToString() + " ", style: "centerAlign50" ) /*, grid.Column("precioConIVA", "P.V.P.", format: p => @MyHelpers.Utils.GetArticleLineStyleClass(p.codProdAE, p.stock, p.precioConIVA.ToString() + " €", false), style: "rightAlign" )*/ } ) } }
Helper que retorna un <p> con diferentes class en función de los parámetros que se le pasen:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Linq.Expressions; using System.Runtime.CompilerServices;
namespace MyHelpers { public static class HtmlHelpers { public static MvcHtmlString GetArticleLineStyleClass(string codProdAE, decimal stock, string text, bool showIfThereIsNoCodProd = true) { var builder_p = new TagBuilder("p") { InnerHtml = text }; if (codProdAE == null) if (!showIfThereIsNoCodProd) builder_p.InnerHtml = ""; else builder_p.Attributes.Add("class", "gridRowWithoutCodProdAutoequip"); else if (stock == 0) builder_p.Attributes.Add("class", "gridRowWithoutStock"); else builder_p.Attributes.Add("class", "gridRowWithStock"); return MvcHtmlString.Create(builder_p.ToString(TagRenderMode.Normal)); }
} }
Funciones javascript para formatear campos númericos con decimales
// Formatear campos numéricos function formatNumber(input, maxNumber) { var num = input.value.replace(/\./g, ''); num = num.replace(/\D/g, '') if (!isNaN(num)) { if (num > maxNumber) { num = num.toString().substring(0, maxNumber.toString().length); if (num > maxNumber) num = num.toString().substring(0, maxNumber.toString().length - 1); } num = num.toString().split('').reverse().join('').replace(/(?=\d*\.?)(\d{3})/g, '$1.'); num = num.split('').reverse().join('').replace(/^[\.]/, ''); input.value = num; } } // Formatear campos numéricos con decimales function formatNumber2(input, numberOfDigitsBeforeComma, numberOfDigitsAfterComma, completeDecimalsWithZeros) { var num = input.value.replace(/\./g, ','); var numparts = num.split(','); numparts[0] = numparts[0].replace(/\D/g, ''); //Elimina cualquier cosa que no sea un carácter numérico if (numparts[0].length > 1) //Elimina 0 delante de otro carácter numérico if (numparts[0].substr(0, 1) == 0) numparts[0] = numparts[0].substr(1, numparts[0].length - 1); if (numparts[0].length > numberOfDigitsBeforeComma) { //Un bug de Android provoca que al picar 1234 se ponga 12,43!!! if (numparts.length == 1) { // Poner la coma automáticamente numparts = (numparts[0].substr(0,numberOfDigitsBeforeComma) + ',' + numparts[0].substr(numberOfDigitsBeforeComma,1)).split(','); } else { numparts[0] = numparts[0].substr(0,numberOfDigitsBeforeComma); } } if (numparts.length >= 2) { numparts[1] = numparts[1].replace(/\D/g, ''); if (numparts[1].length > numberOfDigitsAfterComma) numparts[1] = numparts[1].substr(0,numberOfDigitsAfterComma); } if (completeDecimalsWithZeros == true) { if (numparts.length >= 2) { if (numparts[0].length == 0) numparts[0] = '0'; numparts[1] = (numparts[1] + '00').substring(0,2); input.value = numparts[0] + ',' + numparts[1]; } else { if (numparts[0].length > 0) input.value = numparts[0] + ',00'; else input.value = '0,00'; } } else { if (numparts.length >= 2) input.value = numparts[0] + ',' + numparts[1]; else input.value = numparts[0]; } } // Formatear campos numéricos con decimales function formatNumber3(input, numberOfDigitsBeforeComma, numberOfDigitsAfterComma, deleteCommaIfNotDecimals) { var num = input.value.replace(/\./g, ','); var numparts = num.split(','); numparts[0] = numparts[0].replace(/\D/g, ''); if (numparts[0].length > numberOfDigitsBeforeComma) numparts[0] = numparts[0].substring(0,numberOfDigitsBeforeComma); if (numparts.length > 1) { numparts[1] = numparts[1].replace(/\D/g, ''); if (numparts[1].length > numberOfDigitsAfterComma) numparts[1] = numparts[1].substring(0,numberOfDigitsAfterComma); if (numparts[1].length == 0 && deleteCommaIfNotDecimals == true) { num = numparts[0]; } else { num = numparts[0] + ',' + numparts[1]; } } else { num = numparts[0]; } input.value = num; }
Carga de combos en cascada en página web mediante Ajax y Json
cuestión a
resolver
Imaginemos que queremos cargar 3 combos en
cascada, que contienen respectivamente continentes, países y ciudades: Cuando el
usuario cambie el valor del primer combo (continente) hay que cambiar el
contenido del combo de países, y cuando cambie el de países, hay que cambiar el
de las ciudades.
Esta otra entrada de mi blog hace referencia a algunos de los conceptos que aquí se utilizan: Actualización de datos en página web mediante AJAX y JSON
parte cliente
Definición de los combos EN Html
<select name="cmbContinentes" id="continentes" class="combosLugar"></select>
<select name="cmbPaises" id="paises" class="combosLugar "></select>
<select name="cmbCiudades" id="ciudades" class="combosLugar"></select>
eL CÓDIGO Javascript
1. En la carga de la pantalla (con jQuery) añadir la carga del primer combo que a su vez desencadena la carga de los siguientes, y el evento .change para los elementos con class="comboDeLugar" (es decir, nuestros combos).
$(document).ready(function () {
// Cargar 1er combo (que a su vez desencadena la carga de los demás)
cargaCombo("cmbContinentes", '../../Lugar/Continentes', null);
// Definición evento OnChange para los combos de medida de neumáticos
$(".combosLugar").change(function (e) {
cargaSiguienteCombo(this.id)
});
// Inicialización (opcional). Sólo el primer combo a modo de ejemplo.
$("#comboContinentes")[0].value = "cmbContinentes"
}
2. Definición de la función que carga un combo mediante Ajax y Json
// Cargar combo de forma asíncrona, utilizando ajax y formato json.
function cargaCombo(cmb, url, params) {
$.ajax({
url: url,
data: params,
dataType: 'json',
success: (function (data) {
$("#" + cmb).html('');
$.each(data, function (i, item) {
$("#" + cmb).append("<option value='" +
item.Value + "'>" + item.Text + "</option>");
});
cargaSiguienteCombo(cmb);
})
});
}
3. Definición de la función que carga el siguiente combo.
// Cargar el siguiente combo
function cargaSiguienteCombo(changedCombo) {
if (changedCombo == "") {
return cargaCombo(
"cmbContinentes",
'../../Lugar/Continentes',
null
);
}
if (changedCombo == "cmbContinentes") {
return cargaCombo(
"cmbPaises",
'../../Lugar/Paises',
"continente=" + $("#cmbContinentes")[0].value
);
}
if (changedCombo == "cmbPaises") {
return cargaCombo(
"cmbCiudades",
'../../Lugar/Ciudades',
"pais=" + $("#comboPaises")[0].value
);
}
}
parte servidor:
la clase que recibe las peticiones ajax
Partimos de la base de que utilizamos
ASP.Net MVC 3.
La definición de las entidades Continente,
Pais y Ciudad no se describe en este documento, porque son muy simples. Sólo
indicaré que están ligadas a sendas tablas cuyos campos son:
·
Continentes: Nombre
·
Paises:
Nombre, Continente
·
Ciudades:
Nombre, Pais
La definición de la clase que recibe las
peticiones Ajax desde el código javascript de la página web, es la siguiente:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Dream.Models;
namespace MiProyecto.Controllers
{
public class LugarController : Controller
{
private MisEntities db = new MisEntities();
/// <summary>
/// Retorna los continentes, hardcoded (no desde BDD)
/// </summary>
/// <returns></returns>
public JsonResult Continentes()
{
var list = new List<object>() {
new { Text = "Europa", Value = "Europa" },
new { Text = "América", Value = "América" },
new { Text = "Asia", Value = "Asia" },
new { Text = "África", Value = "África" },
new { Text = "Oceanía", Value = "Oceanía" },
};
return this.Json(list, JsonRequestBehavior.AllowGet);
}
/// <summary>
/// Retorna los países de cierto continente
/// </summary>
/// <param name="continente">Nombre de continente</param>
/// <returns></returns>
public JsonResult Paises(string continente)
{
var list = from pais in db.Paises
where pais.Continente == continente
select new { Text = pais.Nombre, Value = pais.Nombre };
return this.Json(list.ToList(), JsonRequestBehavior.AllowGet);
}
/// <summary>
/// Retorna las ciudades de cierto pais
/// </summary>
/// <param name="pais">Nombre de pais</param>
/// <returns></returns>
public JsonResult Ciudades(string pais)
{
var list = from ciudad in db.Ciudades
where ciudad.Pais == pais
select new { Text = ciudad.Nombre, Value = ciudad.Nombre };
return this.Json(list.ToList(), JsonRequestBehavior.AllowGet);
}
}