Escolar Documentos
Profissional Documentos
Cultura Documentos
i
Aplicación web con ASP.NET
MVC 2
Hace un año empezamos a trabajar con aplicaciones web con ASP.NET MVC 2 en Visual Studio 2010,
en este curso nuevamente trabajaremos con esta tecnología aunque contamos ya con la versión 3
del ASP.NET MVC, podremos pasar a la versión 3 no sin antes haber trabajado con la versión 2 y como
la red de la escuela no nos permite descargar no podemos actualizar nuestro Visual Studio.
Aún se encuentra la publicación del ejemplo anterior en http://afelipelc.mx/2011/05/23/mi-primera-
aplicacion-asp-net-mvc-2-paso-a-paso/ del cual se retomarán algunos datos pero en esta ocasión el trabajo
será diferente.
Por otra parte, empezaremos con la parte de crear un primer proyecto de ASP.NET MVC 2.
Requisitos: Visual Studio 2010 con Visual Web Developer y SQL Server Express 2008. Para este
ejemplo se está utilizando Visual Studio 2010 versión Ultimate.
Crear un proyecto de Aplicación Web de ASP.NET MVC 2
En Visual Studio -> Crear nuevo Proyecto -> C# /Web >> elegimos Aplicación web de ASP.NET MVC
2, elegimos donde guardarlo y le ponemos el nombre de EscuelaWebApp (cambia el nombre si lo
prefieres).
1
Img. 1. Crear el proyecto
Al crear el proyecto nos pregunta si queremos crear un proyecto de prueba unitaria, por ahora elegimos
NO y damos clic en Aceptar.
2
scripts JS (Scripts) que incluye por default JQuery y otras librerias y los archivos de configuración de la
aplicación.
Si se elige un proyecto de Aplicacion web vacía de ASP.NET MVC 2 creará solo la estructura sin el
controlador AcountController.
El controlador HomeController
El controlador Home es el punto de entrada para nuestra aplicación, si analizamos el código vemos que
contiene:
3
return View();
}
donde encontramos la etiqueta ASP llamada ContentPlaceHolder con el Id TitleContent, cuya función es
poner en ese espacio el título de la página proporcionado por las vistas, lo mismo que donde deberá
insertarse el contenido de la página (pasado por la vista).
La estructura del sitio creada por la plantilla es el siguiente:
4
</p>
</asp:Content>
Podemos encontrar que contiene la etiqueta ASP llamada Content con un identificador y especifica
el ContentPlaceHolderID donde deberá mostrarse la información que recide dentro de esta etiqueta, para
í ContentPlaceHolderID=”TitleContent” el contenido a mostrar es “Página principal” que en sí es el
título que le estamos dando a nuestra página.
Para el ContentPlaceHolderID=”MainContent” es todo el contenido de nuestra página, en este caso
mostrará el valor que contiene el parámetro “Message” pasado por la acción Index() del controlador
Home, donde observamos que para imprimir el valor de una variable, parámetro o datos del modelo se
hace por medio de <%:NombreElemento%> ejemplo: <%:ViewData[“Message”] %>, el contenido
también incluye un poco más de información contenida en un párrafo.
Ejecutando nuestro proyecto, VS abrirá el navegador web mostrando el resultado:
Pues bien, hasta aquí es solo lo que nos ha generado el Visual Studio al crear nuestro proyecto, en el
siguiente avance agregaremos algunos controladores y vistas para ir avanzando en el desarrollo.
5
Aplicación web con ASP.NET
MVC 2 – parte 2
Continuando con el desarrollo de nuestra aplicación, primero debemos tener nuestro modelo de datos para
que posteriormente agreguemos los controladores, acciones y vistas correspondientes, para ello se
propone la siguiente estructura para la base de datos.
6
Img. 2. Agregar la base de datos al proyecto.
Agregada la base datos aparecerá en nuestro explorador de soluciones.
7
Ahora abra la base de datos, haciendo doble clic sobre ella, VS nos mostrará el explorador de servidores,
donde deben crearse las diferentes tablas contenidas en el diagrama (img. 1), considere los tipos de datos
apropiados donde las claves primarias sean definidas como identidad con auto incremento en 1 excepto el
campo Alumno.Matricula que será definido por el administrador, recuerde también crear las relaciones
correspondientes (puede consultar el modelo mostrado en la img. 7 donde se muestran mejor las
relaciones y su cardinalidad) para que posteriormente creemos el modelo de datos y este sea correcto.
Crear el modelo de datos.
Ya con nuestra base de datos, creemos ahora nuestro modelo conceptual de datos, para ello utilizaremos
el ADO.NET Entity Framework, para obtener más información acerca de este Framework,
visite http://msdn.microsoft.com/es-mx/data/aa937723 consulte también el siguiente
enlace http://msdn.microsoft.com/en-us/library/bb399572.aspx que contiene información
sobreADO.NET EF.
Entonces para crear nuestro modelo de datos utilizaremos el elemento ADO.NET Entity Data Model para
ello consulte el enlace http://msdn.microsoft.com/en-us/library/bb399249.aspx que contiene información
acerca de este componente y las diferentes herramientas que se utilizarán posteriormente (por ejemplo el
asistente para crear el modelo).
Ya para no marear más, en la carpeta Models en el menú contextual(clic derecho) elegimos Agregar /
Nuevo elemento, en la ventana para agregar el nuevo elemento nos situamos sobre las plantillas C# /
Datos donde seleccionamos ADO.NET Entity Data Model el cual le pondremos el nombreModeloDatos.
En el segundo paso veremos que automáticamente nos muestra nuestra base de datos BDEscolar.mdf (si
no la muestra debemos crear la conexión hacia nuestro archivo de base de datos), seleccionada la base de
datos nos muestra la cadena de conexión que se almacenará en el archivo Web.Config del proyecto, a la
cadena de conexión le asigna el nombre BDEscolarEntities (lo dejamos como tal) que utilizaremos como
clase para poder utilizar el modelo en nuestros controladores u otras clases.
8
En el tercer paso seleccionamos las tablas y otros elementos de nuestra BD que queremos agregar al
modelo, notemos que el espacio de nombres para el modelo es BDEscolarModel.
9
Img. 7. Modelo de datos para nuestra aplicación.
En el modelo conceptual observamos que se han creado correctamente las propiedades de navegación,
para saber más acerca de las propiedades de navegación visite el enlace http://msdn.microsoft.com/es-
es/library/ee382841.aspx
El siguiente avance será para agregar los controladores y empezar a crear y editar los primeros registros
de nuestra aplicación.
Cualquier comentario para complementar la información o aportar para mejorarla son bienvenidos.
10
aplicación esté terminada tendremos que limpiar la BD para que ahora sí empiece a trabajar desde cero,
entonces será más fácil reemplazar la BD con datos de prueba por la vacía.
11
using EscuelaWebApp.Models;
Agregaremos el código correspondiente para que la acción Index() del nuevo controlador pase la lista de
programas educativos a la vista, para ello primero crearemos una instancia de nuestra conexión a la BD
(Modelo).
El código de nuestro controlador es el siguiente (no incluí aquí el código del namespace ni los using):
}
Crear una nueva vista.
Es importante recordar que cada una de las acciones (ActionResult) debe devolver una vista (por lo
general con el mismo nombre) o redireccionar a otra acción que devuelva una vista.
Para crear una vista para una acción, sobre el nombre de la acción Index() presionamos botón derecho y
seleccionamos CrearVista esto nos abrirá una ventana donde:
1. Debemos especificar el nombre para nuestra vista (mismo nombre de la acción).
2. Como queremos que el Index nos muestre la lista de programas educativos, seleccionamos
“Crear una vista fuertemente tipada” donde en “Clase de datos de View” seleccionamos la
clase “Programa educativo”.
3. En Contenido de view seleccionamos el contenido para nuestra vista, como queremos que
muestre una lista, seleccionamos “List”.
4. Las opciones de “Seleccionar la página maestra” la dejamos como tal, si fuera el caso que
tenemos diferentes plantillas debemos seleccionar la plantilla a utilizar.
5. Si cambiamos el contenedor principal donde debe mostrarse el contenido en nuestra plantilla,
debemos especificar su ID.
6. Agregamos la vista.
12
Img. 3. Agregar una vista fuertemente tipada.
Observamos que el código de la nueva vista mostrará como título “Index”, así que cambiamos el título a
“Programas Educativos”.
Vemos también que el código contiene una tabla en HTML con las columnas correspondientes a los
campos de la tabla “ProgramaEducativo” y una primera columna que mostrará los links correspondientes
para Editar, Detalles y eliminar cada uno de los registros.
Observemos que para mostrar los datos de los PE registrados nuestra vista los escribirá en la tabla
utilizando un foreach que recorre los elementos contenidos en el Modelo (lista de objetos pasados por el
controlador).
Consideremos que los enlaces que se muestren en nuestra página aún no funcionan, ya que los enlaces
apuntan hacia las acciones Create, Edit, Details y Delete que no existen en nuestro controlador (más
adelante crearemos las nuestras, por ahora lo dejamos así).
13
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<table>
<tr>
<th></th>
<th>
IdPE
</th>
<th>
Nombre
</th>
<th>
NombreCorto
</th>
</tr>
<tr>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id=item.IdPE }) %> |
<%: Html.ActionLink("Details", "Details", new { id=item.IdPE })%> |
<%: Html.ActionLink("Delete", "Delete", new { id=item.IdPE })%>
</td>
<td>
<%: item.IdPE %>
</td>
<td>
<%: item.Nombre %>
</td>
<td>
<%: item.NombreCorto %>
</td>
</tr>
<% } %>
</table>
<p>
<%: Html.ActionLink("Create New", "Create") %>
</p>
</asp:Content>
Ejecutemos nuestra aplicación e ingresemos a la ruta de nuestro controlador, en mi caso
es http://localhost:2958/AdministrarPE (recuerda que 2958 es el puerto que el IIS de desarrollo asigna a
nuestra aplicación).
14
Img. 4. Nuestra primera vista mostrando la lista de programas educativos.
Y ese es el resultado que tenemos hasta ahora, vaya que es rápido desarrollar aplicaciones web con esta
tecnología, pero apenas estamos empezando :D
3.
4. <%: Html.ActionLink(“Edit”, “Edit”, new { id=item.IdPE }) %>
5. <%: Html.ActionLink(“Details”, “Details”, new { id=item.IdPE })%>
6. <%: Html.ActionLink(“Delete”, “Delete”, new { id=item.IdPE })%>
Por medio de Html.ActionLink genera los vínculos a acciones de un controlador, por ejemplo el link 1)
genera el enlace http://localhost:2958/Home/About, el 2) genera el
enlace http://localhost:2958/AdministrarPE/ el 3)
genera http://localhost:2958/AdministrarPE/Edit/Xnúmero, etc. nótese que no se pone la palabra
“Controller” en Home, AdministrarPE (nombre de los controladores), entonces la sintaxis es:
15
%>
Esta sintaxis es la utilizada en los links 3, 4 y 5 porque solo se están mostrando y utilizando en las vistas
del controlador AdministrarPE.
Vaya a Site.Master y en el div que contiene el menú, agrege el código para generar el enlace.
Ejecutamos nuestra aplicación donde en la página principal y por toda la aplicación podremos ver nuestro
nuevo link.
Ya para no aburrir más con esto de la aplicación aquí paramos esta lección, continuaremos con la parte de
agregar, editar y eliminar en la siguiente entrega.
// GET: /AdministrarPE/Registrar
public ActionResult Registrar()
{
return View();
}
Ahora creemos la vista correspondiente:
17
Img. 2. Crear vista para Registrar.
Modifiquemos ahora el código de la vista, como el campo IdPE de PrgramaEducativo es auto incremento,
eliminemos el código HTML del form que corresponde al campo para ingresar el valor de IdPE, mostrar
la leyenda y textos correspondientes y modificar el link que lleva al index, el código de la vista quedaría
así:
<fieldset>
<legend>Datos del programa educativo</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Nombre) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
18
<div class="editor-label">
<%: Html.LabelFor(model => model.NombreCorto) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.NombreCorto) %>
<%: Html.ValidationMessageFor(model => model.NombreCorto) %>
</div>
<p>
<input type="submit" value="Registrar programa educativo" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Regresar a la lista de programas educativos",
"Index") %>
</div>
</asp:Content>
Ahora ingresemos manualmente a nuestra acción en http://localhost:2958/AdministrarPE/Registrar donde
vemos el formulario.
Las acciones encargadas de recibir datos por POST se les antepone [HttpPost ] antes de la declaración,
nuestra acción Registrar (POST) quedaría así:
19
[HttpPost]
public ActionResult Registrar(ProgramaEducativo programaeducativo)
{
BD.AddToProgramaEducativo(programaeducativo);
BD.SaveChanges();
Ahora ejecute nuevamente la aplicación y vuelva al form anterior, llene los datos y cree el registro para
que pueda ver el resultado.
20
Img. 5. Registro agregado.
Ahora en la vista Index, modifique el enlace “Create new” para que apunte a nuestra acción Registrar:
La acción Editar
De igual forma como la acción Registrar, deben considerarse las peticiones GET y POST.
// GET: /AdministrarPE/Editar
public ActionResult Editar(int id)
{
//buscar el objeto en la BD
var programaeducativo = BD.ProgramaEducativo.Where(p => p.IdPE
== id).FirstOrDefault();
21
Img. 6. Crear la vista Editar (con contenido Edit).
Modificaremos el código de la vista donde agregaremos los textos correspondientes y el textbox
correspondiente al Id lo pondremos como solo lectura para que el usuario no pueda modificarlo.
<fieldset>
<legend>Datos del programa educativo</legend>
<div class="editor-label">
ID
</div>
<div class="editor-field">
<% // Considerar poner el textbox del ID como solo lectura %>
<%: Html.TextBoxFor(model => model.IdPE, new
Dictionary<string, object>() { { "readonly", "true" } })%>
<%: Html.ValidationMessageFor(model => model.IdPE) %>
</div>
22
<div class="editor-label">
Nombre
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
<div class="editor-label">
Nombre Corto
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.NombreCorto) %>
<%: Html.ValidationMessageFor(model => model.NombreCorto) %>
</div>
<p>
<input type="submit" value="Guardar Cambios" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Regresar a la lista de programas educativos",
"Index")%>
</div>
</asp:Content>
La acción Editar (POST)
Ahora creemos la acción Editar que reciba los nuevos datos y actualice la BD.
[HttpPost]
public ActionResult Editar(int id, FormCollection formValues)
{
//buscar el objeto en la BD
var programaeducativo = BD.ProgramaEducativo.Where(p =>
p.IdPE == id).FirstOrDefault();
//Actualizar los datos del objeto con los recibidos del form.
UpdateModel(programaeducativo, formValues);
23
Img. 7. Vista de la lista con el nuevo link Editar.
24
Img. 9. Datos guardados.
Eliminar Registros.
Ahora crearemos la acción que elimine los registros.
// GET: /AdministrarPE/Eliminar
public ActionResult Eliminar(int id)
{
//buscar el objeto en la BD
var programaeducativo = BD.ProgramaEducativo.Where(p =>
p.IdPE == id).FirstOrDefault();
25
Img. 10. Crear vista con contenido Delete.
Modificar el código de la vista (puede utilizar el diseñador):
<div class="display-field"><strong>Nombre</strong>:
<%: Model.Nombre %></div>
</fieldset>
<% using (Html.BeginForm()) { %>
<p>
<input type="submit" value="Si Eliminar" /> |
<%: Html.ActionLink("Cancelar", "Index") %>
26
</p>
<% } %>
</asp:Content>
Crear la acción Eliminar para confirmar la eliminación.
[HttpPost]
public ActionResult Eliminar(int id, string confirmar)
{
//buscar el objeto en la BD
var programaeducativo = BD.ProgramaEducativo.Where(p =>
p.IdPE == id).FirstOrDefault();
27
Img. 11. Crear vista llamada "Eliminado", para utilizar como confirmación.
Agregue el mensaje correspondiente a la nueva vista:
</asp:Content>
Modifiquemos ahora los enlaces en la lista de programas educativos para que el link “Delete” apunte a la
nueva acción Eliminar.
quedaría como
28
Elimine el link <%: Html.ActionLink(“Details”, “Details”, new { id=item.IdPE })%> ya que no lo
implementaremos.
Implementar una acción Detalles implica mostrar todos los detalles correspondientes para cada PE como
datos estadísticos: cuatrimestres, alumnos en total, grupos, etc.
Terminada nuestra acción Eliminar y los links correspondientes, ahora comprobemos que ya puede
eliminar.
29
Img. 14. Notificación del registro eliminado.
30
Img. 1. Resultado final de este avance.
Continuando con el desarrollo de nuestra aplicación ASP.NET MVC 2, seguiremos trabajando con el
AdministrarPEController donde agregaremos un poco más de funcionalidad, para ello se plantea lo
siguiente:
El Index de AdministrarPEController muestra la lista de PE’s, donde ahora queremos que al dar clic en
un PE (por ahora no tienen link) nos lleve a ver sus cuatrimestres considerando solo mostrar los
cuatrimestres del año actual; por ejemplo: estamos en el 2011, cuando entremos a ver los cuatrimestres,
solo nos muestre los del 2011, si aún no se han creado, nos permita hacerlo pero evitar que se creen
cuatrimestres duplicados (son solo 3 por año y cada uno tiene definido su periodo), además nos permita
ver el historial de cuatrimestres del PE, por ejemplo ver la lista de cuatrimestres del año pasado y así
poder ingresar al historial (más adelante será el historial de grupos).
Si se hacen pelotas, imaginen como podríamos saber cuantos grupos tiene el PE TIC-SI (TSU) en el
cuatrimestre septiembre-diciembre del 2011, cuantos tuvo en el cuatrimestre mayo-agosto 2011, y
cuantos tuvo en el cuatrimestre enero-abril2011, para saberlo hay que revisar el historian ¿No?, y
si quisiéramos saber ¿cuantos tuvo en los cuatrimestres del año 2010?, solo debemos imaginarlo y pensar
en como le daríamos solución.
Entonces a trabajar, de aquí en adelante solo iré agregando las partes del código y una breve explicación,
puesto que ya se familiarizaron con el controlador, las acciones, las vistas, los ActionLink, las consultas
LINQ y expresiones LAMBDA.
A partir de aquí empezaremos a utilizar CSS y JQuery para realizar consultas AJAX junto con JSON, así
que si también no estamos familiarizados (por lo menos tener la idea de que se esta haciendo o de que se
trata) entonces es necesario consultar http://jquery.com/ y http://www.json.org/
La acción ProgramaEducativo.
31
Será la encargada de mostrar los cuatrimestres del año actual de cada PE, permitirá agregar nuevos
cuatrimestres y eliminar cuatrimestres siempre y cuando no tengan ningún grupo asociado, la vista
permitirá visualizar la lista de cuatrimestres de años anteriores.
// GET: /AdministrarPE/ProgramaEducativo
public ActionResult ProgramaEducativo(int id)
{
//buscar primero el programa educativo
32
Img. 2. Crear vista tipada para mostrar la lista de cuatrimestres del PE.
Modifiquemos parte del código de la vista para mostrar los textos deseados, la tabla de cuatrimestres solo
debe mostrar el Id del cuatrimestre, un campo Periodo (conformado por Inicio, Fin y Año), eliminaremos
el link de Edit y Details.
Al parecer el campo Nombre sale sobrando, así que ve al diseño de la tabla, puedes eliminarlo de la tabla
y actualizar el modelo (recuerda hacerlo también en la BD vacía que tenemos de respaldo para que junto
con al de prueba sean iguales).
Esta vista también ya contiene el form para crear un nuevo cuatrimestre (estará oculto) donde los textbox
son de solo lectura, en tiempo de ejecución los llenaremos con JavaScript, incluye también la tabla del
historial (también aparcererá oculta).
33
<h4>Cuatrimestres del año <%: DateTime.Now.Year %></h4>
<div class="editor-label">
<label for="PeriodoInicio">Inicio</label>
</div>
<div class="editor-field">
<input id="PeriodoInicio" name="PeriodoInicio" type="text"
value="" readonly="readonly"/>
</div>
<div class="editor-label">
<label for="PeriodoFin">Fin</label>
</div>
<div class="editor-field">
<input id="PeriodoFin" name="PeriodoFin" type="text"
value="" readonly="readonly"/>
</div>
<div class="editor-label">
<label for="Anio">Año</label>
</div>
<div class="editor-field">
<input id="Anio" name="Anio" type="text" value="<%:
DateTime.Now.Year %>" readonly="readonly"/>
</div>
<input type="hidden" id="IdPE" name="IdPE" value="<%:
ViewData["IdPE"] %>" />
<p>
<input type="submit" id="EnviarForm"
value="Registrar cuatrimestre" /> |
<a href="#" id="cancelarcrearcuatrimestre">Cancelar</a>.
</p>
<div id="mensaje"></div>
</form>
</fieldset>
</div>
<% //FIN Mostrar el form para crear un nuevo cuatrimestre. %>
34
<th>
Periodo
</th>
<th>
Año
</th>
</tr>
<tr>
<td>
<%: Html.ActionLink("Eliminar", "Delete", new { id=item.Id })%>
</td>
<td>
<%: item.Id %>
</td>
<td>
<%: item.PeriodoInicio + " - " + item.PeriodoFin %>
</td>
<td>
<%: item.Anio %>
</td>
</tr>
<% } %>
</table>
<% //FIN Tabla de cuatrimestres del PE. %>
</asp:Content>
Ejecutando nuestra app, y accediendo nuestra nueva
acciónhttp://localhost:2958/AdministrarPE/programaeducativo/1 tenemos el resultado:
35
Img. 3. Vista previa de la vista ProgramaEducativo/N
Creemos nuestro CSS
Abre tu editor de textos favorito, guarda tu nuevo archivo como misestilos.css en la carpeta Content de tu
proyecto (busca tu carpeta de proyecto en el explorador de windows), el contenido del archivo es:
.oculto{
display:none;
}
Ahora incluyamos nuestro archivo en la plantilla, ve a Views/Shared/ y abre Site.Master, y agrega la linea
correspondiente para que se incluya tu nuevo css.
36
Img. 4. Elementos ocultos en la vista.
Creemos nuestros JavaScripts
Ahora utilizaremos JQuery, las aplicaciones ASP.NET MVC 2 ya incluyen la versión 1.4.1, actualmente
está disponible la versión 1.6.4, puedes descargarla y actualizar tus JS.
Bien, entonces en tu editor de textos creemos un nuevo archivo y lo guardaremos en la carpeta
Content/Scripts de nuestra aplicación, el archivo le llamaremos cuatrimestres.js ya que si la guardamos
en la carpeta Scripts de la app donde estan los jquery y otros js no nos permitirá utilizar nuestro propio js.
En el explorador de soluciones mostremos los archivos ocultos e incluyamos en el proyecto el
archivo misestilos.css y la carpeta Scripts.
Donde agregaremos todo lo necesario para mostrar los elementos ocultos, crear el nuevo cuatrimestre y
cargar el historial de cuatrimestres del PE.
37
$.each(data, function(i, item) {
//verificar cuales cuatrimestres ya existen
//si existen deshabilitar el radiobutton
correspondiente
if(data.c1== true)
$("#cuatrimestre1").attr("disabled","disabled");
if(data.c2== true)
$("#cuatrimestre2").attr("disabled","disabled");
if(data.c3== true)
$("#cuatrimestre3").attr("disabled","disabled");
});
});
$("#crearcuatrimestre").show("slow");
//event.preventDefault();
//ocultar el link
$("#crearnuevocuatrimestre").hide();
});
38
En el JS al hacer clic sobre el link “Crear nuevo cuatrimestre”, primero obtiene la lista de cuatrimestres
actuales, de los cuales si alguno ya existe, deshabilita el radiobutton correspondiente para evitar que
vuelva a crearse el mismo cuatrimestre.
Para esto hace una llamada AJAX que como resultado obtiene un conjunto de datos con formato JSON
los cuales son el resultado de invocar a una acción en el controlador, la acción es la siguiente:
//recorrer la lista
foreach(Cuatrimestre cuatrimestre in cuatrimestresPE)
{
if(cuatrimestre.PeriodoInicio.Trim() == "Enero" &&
cuatrimestre.PeriodoFin.Trim() == "Abril")
c1=true;
if(cuatrimestre.PeriodoInicio.Trim() == "Mayo" &&
cuatrimestre.PeriodoFin.Trim() == "Agosto")
c2=true;
if(cuatrimestre.PeriodoInicio.Trim() == "Septiembre" &&
cuatrimestre.PeriodoFin.Trim() == "Diciembre")
c3=true;
}
39
Img. 5. Registrar algunos datos históricos.
Comprobando funcionalidad
Ya con los datos, ahora comprobemos que nuestra app puede agregar nuevos cuatrimestres.
40
Img. 7. Registrar el cuatrimestre seleccionado.
41
Img. 8. Cuatrimestre creado, verificando que no permita volver a crearlo.
Y bien, hasta aquí ya funciona nuestra app, pero aún falta la parte del historial.
Agregue los link en la vista Index para que al hacer clic sobre el nombre de un PE lo dirija hacia sus
cuatrimestres (el trabajo realizado).
42
Dada la situación de que la lista de cuatrimestres proveniente de nuestra base de datos no la podemos
transformar a JSON, debemos crearnos una clase auxiliar llamada AuxCuatrimestre, esta clase debemos
crearla en la carpeta Models, el código para esta clase es:
namespace EscuelaWebApp.Models
{
public class AuxCuatrimestre
{
private int id;
private string periodo;
private int anio;
public int Id
{
get { return id; }
set { id = value; }
}
}
}
Ahora, el código de nuestra acción HistorialPE que devuelve JSON es:
[ActionName("HistorialPE")]
public JsonResult HistorialPE(int id)
{
var cuatrimestres = BD.Cuatrimestre.Where(c => c.IdPE == id
&& c.Anio < DateTime.Now.Year).ToList();
List<AuxCuatrimestre> historial = new List<AuxCuatrimestre>();
43
//Cuando de clic sobre MostrarHistorial
var historialcargado=false;
$("#mostrarh").live("click", function(event){
//mostrar el historial
$("#historial").slideToggle("slow");
});
Verifiquemos que funcione.
44
Img. 1. Mostrando el historial.
Mostrar el historial ahora esta funcionando, continuemos ahora con la parte de eliminar cuatrimestres.
Eliminar cuatrimestres.
Creemos la acción correspondiente.
// GET: /AdministrarPE/EliminarCuatrimestre
public ActionResult EliminarCuatrimestre(int id)
{
//buscar el cuatrimestre
var cuatrimestre = BD.Cuatrimestre.Where(c =>
c.Id == id).FirstOrDefault();
actualmente el link se crea con <%: Html.ActionLink(“Eliminar”, “Delete”, new { id=item.Id })%>
modificado quedaría:
<%
//solo si el cuatrimestre no tiene ningún grupo, mostrar el link
if(item.Grupo.Count == 0)
{
%>
<%: Html.ActionLink("Eliminar", "EliminarCuatrimestre",
new { id = item.Id }) %>
<%
}
%>
Ahora sí, creemos la vista EliminarCuatrimestre:
45
Img. 2. Crear la vista EliminarCuatrimestre.
Modifiquemos el código de la vista:
<h2>Eliminar Cuatrimestre</h2>
<div class="display-label"><strong>Id</strong>:</div>
<div class="display-field"><%: Model.Id %></div>
<div class="display-label"><strong>Año</strong>:</div>
46
<div class="display-field"><%: Model.Anio %></div>
</fieldset>
<% using (Html.BeginForm()) { %>
<p>
<input type="submit" value="Eliminar" /> |
<%: Html.ActionLink("Cancelar", "ProgramaEducativo",
new { id = Model.IdPE })%>
</p>
<% } %>
</asp:Content>
En el caso que el cuatrimestre a eliminar ya tiene grupos, no puede eliminarse, para notificarlo al usuario
lo haremos con otra vista, así que creemos una nueva vista llamada ErrorEliminar, la vista es tipada de
la clase Cuatrimestre con contenido Empty.
47
grupos asignados.</b></p>
<fieldset>
<legend>Datos del cuatrimestre</legend>
<div class="display-label"><strong>Id</strong>:</div>
<div class="display-field"><%: Model.Id %></div>
<div class="display-label"><strong>Año</strong>:</div>
<div class="display-field"><%: Model.Anio %></div>
</fieldset>
</asp:Content>
Confirmar la eliminación.
Como en el ejemplo de eliminar PE, ahora creemos la acción de confirmación para eliminar el
cuatrimestre.
[HttpPost]
public ActionResult EliminarCuatrimestre(int id, string confirmar)
{
//buscar el objeto en la BD
var cuatrimestre = BD.Cuatrimestre.Where(c => c.Id == id).FirstOrDefault();
48
Img. 4. Crear vista CuatrimestreEliminado.
Su código:
</asp:Content>
Comprobando funcionalidad.
Ejecutando nuestra aplicación, ingresemos a los cuatrimestres del PE de prueba.
49
Img. 5. Intentar eliminar un cuatrimestre.
Si fuera el caso que el cuatrimestre tiene grupos asignados nos mostrará:
50
Img. 6. Cuando un cuatrimestre tiene grupos.
Pero cuando un cuatrimestre no tiene grupos:
51
Img. 9. Cuatrimestres del PE.
Bien, pues hasta aquí terminamos la parte de los cuatrimestres, continuaremos con la gestión de grupos
para cada cuatrimestre.
Lo que haremos ahora será implementar un controlador que nos permita administrar los registros de
tutores y poder filtrarlos por PE.
52
Img. 1. Resultado a obtener al finalizar esta parte.
Como ya estamos familiarizados con la parte de crear vistas tipadas, iremos avanzando un poco más
rápido, solo que a nuestra vista principal le agregaremos un ComboBox más para realizar los filtros.
Agregaremos también una clase personalizada para usarla en nuestra vista que muestre la lista.
53
{
BDEscolarEntities BD = new BDEscolarEntities();
//
// GET: /AdministrarTutores/
public ActionResult Index(int idpe=0)
{
if (idpe == 0)
{
ViewData["NombrePE"] = "Todos los programas educativos";
var tutores = BD.Tutor.ToList();
return View(tutores);
}
else
{
ProgramaEducativo pe = BD.ProgramaEducativo.Where(p => p.IdPE == idpe).FirstOrDefault();
if (pe != null)
{
ViewData["NombrePE"] = pe.Nombre;
var tutores = BD.Tutor.Where(p => p.ProgramaEducativo.IdPE == pe.IdPE).ToList();
return View(tutores);
}
else
return View("Error");
}
}
}
Crear la vista Index (para mostrar una lista de tutores):
54
Img. 3. Crear vista para mostrar la lista de tutores.
Hagamos algunas modificaciones a la vista como: título, mostrar nombre del programa educativo
mostrado, mostrar el programa educativo de cada tutor, modificar el link para registrar un nuevo tutor,
etc.
<table>
<tr>
<th></th>
<th>
Id
</th>
<th>
Nombre
</th>
<th>
Apellidos
</th>
<th>
Programa Educativo
</th>
</tr>
<tr>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id=item.IdTutor }) %> |
<%: Html.ActionLink("Details", "Details", new { id=item.IdTutor })%> |
<%: Html.ActionLink("Delete", "Delete", new { id=item.IdTutor })%>
</td>
<td>
<%: item.IdTutor %>
</td>
<td>
<%: item.Nombre %>
</td>
<td>
<%: item.Apellidos %>
</td>
<td>
<%: item.ProgramaEducativo.NombreCorto%>
</td>
</tr>
<% } %>
</table>
<p>
<%: Html.ActionLink("Registrar nuevo tutor", "Registrar") %>
55
</p>
</asp:Content>
Como tenemos ya algunos PE registrados, agreguemos ahora 2 registros a la tabla tutores.
56
Img. 6. Tutores filtrados por Id del programa educativo.
Registrar nuevos tutores.
En la vista correspondiente al mostrar el formulario para registrar el nuevo tutor, debemos mostrar la lista
de programas educativos para seleccionar al que forma parte el nuevo tutor.
La acción Registrar.
//
// GET: /AdministrarTutores/Registrar
public ActionResult Registrar()
{
//Preparar lista de los programas educativos
var programaseduc = BD.ProgramaEducativo.ToList();
//Pasar la lista de pe's a la vista
ViewData["ProgramasEducativos"] = programaseduc;
//crear un nuevo objeto de la clase tutor
//para poder asociarlo con el combo a mostrar en el formulario
Tutor nuevotutor = new Tutor();
//pasar el objeto nuevotutor a la vista
return View(nuevotutor);
}
Crear la vista.
57
Img. 7. Crear la vista Registrar.
Modificado el código de la vista para que muestre el combo de programas educativos queda de esta
forma:
<fieldset>
<legend>Datos del tutor</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Nombre) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
58
<div class="editor-label">
<%: Html.LabelFor(model => model.Apellidos) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Apellidos) %>
<%: Html.ValidationMessageFor(model => model.Apellidos) %>
</div>
<div class="editor-label">
<%// eliminar Html.LabelFor(model => model.IdPE) %>
Programa Educativo.
</div>
<div class="editor-field">
<% // reemplazar Html.TextBoxFor(model => model.IdPE) por el Html.DropDownList %>
<% // Esto nos crea un combobox mostrando el nombre corto del pe como texto
// y el IdPE como name y value del combo.
// y esta sociado a la propiedad IdPE del tutor.
%>
<%: Html.DropDownList("IdPE", new SelectList(ViewData["ProgramasEducativos"] as IEnumerable,
"IdPE", "NombreCorto", Model.IdPE))%>
<%: Html.ValidationMessageFor(model => model.IdPE) %>
</div>
<p>
<br />
<input type="submit" value="Guardar" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Regresar a la lista de tutores", "Index") %>
</div>
</asp:Content>
Ahora verificando la funcionalidad obtenemos:
59
Img. 8. Registrar nuevo tutor mostrando la lista de programas educativos.
Registrar el nuevo tutor.
Nuestra acción [HttpPost] Registrar se encargará de guardar el nuevo objeto en la BD y redireccionarnos
a la lista de tutores del programa educativo al que pertenece el nuevo tutor.
// GET: /AdministrarTutores/Registrar
[HttpPost]
public ActionResult Registrar(Tutor tutor)
{
//Agregar el nuevo tutor
BD.AddToTutor(tutor);
//guardar los cambios en la bd
BD.SaveChanges();
60
Img. 9. Registrando un nuevo tutor.
Img. 10. El tutor ha sido registrado y se ha redireccionado a la lista de tutores del PE.
61
Ahora por su propia cuenta cree las acciones correspondientes para Editar y Eliminar tutores, apóyese en
el controlador AdministrarPE donde se realizó la implementación similar, pero en esta ocasión también
debe mostrar el mismo formulario que se muestra en la vista Registrar, considere también que al intentar
eliminar el tutor debe comprobarse que no esté asignado a algún grupo.
El resultado que debe obtener será como se muestra en la imagen (modificar los links de la vista Index).
Img. 11. La vista Index con links para editar, eliminar y ver la lista completa de tutores.
62
Img. 12. Editando los datos del tutor.
63
Img. 14. Eliminando un tutor.
Entonces la tarea es implementar las acciones comentadas, ahora que si después de intentarlo nadamás no
se puede, pues agreguen un comentario para pasarles los fragmentos de código en los que tienen
problemas.
64
Img. 1. Validación de formularios.
Plantillas compartidas.
Como nos dimos cuenta al crear vistas para Registrar y Editar es que ambas vistas incluyen los mismos
campos en el formulario, excepto por IdPE para Editar; esto es lo mismo con cualquier otras vistas para
crear y editar registros.
Una de las formas de evitar crear 2 vistas para un mismo fin es crear plantillas compartidas
llamadasEditor Template.
65
Img. 3. Carpeta para plantillas.
Si se requiere crear plantillas que serán utilizadas en un solo controlador, entonces en la carpeta
Views/[NombreControlador] debe crearse una carpeta llamada EditorTemplates, en este ejemplo
crearemos esta carpeta en Views/AdministrarPE como se ve en la imagen 3.
.
Para ello, sobre esta carpeta, con botón derecho seleccionamos Agregar/ Vista.
La vista a crear será un poco diferente a las vistas Registrar y Editar, ya que se está pensando que esta
vista será incluida dentro de las vistas Registrar y Editar, este tipo de plantillas se le define como vista
parcial y debe llevar el mismo nombre de la clase correspondiente, en este caso, la clase es
ProgramaEducativo.
Ya en la ventana para crear vista.
66
Img. 4. Crear vista parcial ProgramaEducativo.
Solo asegúrese de seleccionar Crear una vista parcial, en content seleccione Create o Edit.
Agregada la vista, le haremos modificaciones al código de forma que solo quede la parte para los campos
Nombre y NombreCorto.
<div class="editor-label">
<%: Html.LabelFor(model => model.Nombre) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
<div class="editor-label">
Nombre corto
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.NombreCorto) %>
<%: Html.ValidationMessageFor(model => model.NombreCorto) %>
</div>
Modificar la vista Registrar y Editar.
Abra el código de la vista Registrar.aspx y elimine la parte del código que muestra los elementos del
formulario (Nombre y NombreCorto), ya que ahora se encuentran en la plantilla, la parte del código
modificado en la vista Registrar quedaría algo como:
<fieldset>
67
<legend>Datos del programa educativo</legend>
<p>
<input type="submit" value="Registrar programa educativo" />
</p>
</fieldset>
Nota: El código resaltado en negrita fue el que reemplazo al código que ahora tenemos en nuestra
plantilla ProgramaEducativo.ascx
Ejecutemos nuestra app y vayamos a intentar registrar un nuevo programa educativo.
<p>
<input type="submit" value="Guardar Cambios" />
</p>
</fieldset>
La validación de datos del formulario.
ASP.NET MVC tiene la ventaja de ofrecernos herramientas y uso fácil para realizar esta tarea, el
procedimiento es el siguiente:
1. Crear una clase que se encargue de la validación de datos (clase parcial de metadatos).
2. Configurar la validación con MicrosoftAjax en nuestro formulario.
3. Verificar su funcionamiento.
68
Actualmente nuestros formularios tienen un fallo muy importante que debemos considerar, estos no
realizan ninguna validación de los datos que son ingresados en sus campos, las validaciones que debemos
considerar son los tipos de datos correctos, ya que de no recibir los datos correctos colapsará la base de
datos y de paso la aplicación.
Es fácil agregar la validación a nuestra aplicación mediante la adición de anotaciones de datos para las
clases de nuestro modelo. Las anotaciones de datos nos permiten describir las reglas que se desean aplicar
a las propiedades de nuestro modelo (clases), y ASP.NET MVC se hará cargo de hacerlas cumplir y la
visualización de los mensajes adecuados a los usuarios.
Para una clase simple del modelo, agregando una anotación de datos es manejada por la instrucción
using paraSystem.ComponentModel.DataAnnotation, y a continuación, colocar los atributos ensus
propiedades, de esta forma:
using System.ComponentModel.DataAnnotations;
namespace Validacion.Models
{
public class Persona
{
[Required]
public string Nombre { get; set; }
[Required]
public string Apellidos { get; set; }
Nuestro ejemplo se basa en validar los datos al registrar un nuevo programa educativo, así que en nuestra
carpeta Models, agregaremos una nueva clase con el mismo nombre de “ProgramaEducativo”.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace EscuelaWebApp.Models
{
[MetadataType(typeof(ProgramaEducativoMetaData))]
public partial class ProgramaEducativo
{
//Reglas de validación para la clase ProgramaEducativo
69
}
}
}
Agregando validaciones (Data Annotations) a nuestro formulario ProgramaEducativo.
La clase parcial ProgramaEducativo tiene un atributo llamado MetadataType que apunta a la clase
ProgramaEducativoMetaData, en esta última declararemos las reglas de validación a las cuales
agregaremos algunos atributos como las anotaciones:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace EscuelaWebApp.Models
{
[MetadataType(typeof(ProgramaEducativoMetaData))]
public partial class ProgramaEducativo
{
//Reglas de validación para la clase ProgramaEducativo
//El campo IdPE lo configuramos para Scaffold dejando la gestión de su valor al sql server.
//el campo ya ha sido excluido en nuestro formulario - plantilla (ProgramaEducativo.ascx)
[ScaffoldColumn(false)]
public object IdPE { get; set; }
70
Habilitar la validación con AJAX (Validación por el lado del cliente).
Solo falta modificar nuestra plantilla o formulario ProgramaEducativo.ascx para que pueda realizar la
validación de datos con AJAX, entonces vamos al código de ProgramaEducativo.ascx donde agregamos.
<%@ Import Namespace="EscuelaWebApp" %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EscuelaWebApp.Models.ProgramaEducativo>" %>
<fieldset>
<legend>...
Donde la línea resaltada en negrita debe eliminarse, y agregar una nueva linea antes del inicio del
formulario, al final esta parte del código queda:
<fieldset>
<legend>Datos del programa educativo</legend>
La línea resaltada en negrita es la nueva linea agregada para activar la validación.
Ahora comprobemos la validación, para ello ejecute la aplicación y vaya a registrar un nuevo programa
educativo, sin ingresar ningún datos, haga clic en el botón para crearlo, deben saltar nuestros mensajes de
error especificados en nuestra clase parcial ProgramaEducativo.
71
Img. 6. Probando validación de datos.
72
Img. 8. Validando datos de formularios.
Y bien, la validación ha funcionado al ingresar solo algunos valores e ingresando una longitud excedida
para un campo.
Su labor ahora es implementar la validación para las vistas Registrar y Editar para la clase Tutor
(Controlador AdministrarTutores) y para el resto de las vistas Editar y Registrar que se irán creando debe
ir implementando la validación.
73
Img. 9. Validar datos en Registrar y Editar Tutor.
74
Img. 1. Resultado de nuestro avance al finalizar esta parte.
El nuevo controlador AdministrarGrupos.
Agregue el nuevo controlador AdministrarGrupos, cuya acción Index puede recibir un parámetro que será
el Id del cuatrimestre del cual se quieren visualizar sus grupos, sin recibir parámetro mostrará solo la
vista vacía (sin ninguna lista), pero ademas debe mostrar controles para alternar la información que
queremos visualizar; algo similar ya se ha hecho en la acción Index de AdministrarTutores (revise esa
parte para entender de que se trata y como funciona).
75
public ActionResult Index(int idcuatrimestre=0)
{
//Crear la lista de programas educativos para mostrarlos en la vista Index
var programaseducativos = BD.ProgramaEducativo.ToList();
programaseducativos.Insert(0, new ProgramaEducativo { IdPE = 0, NombreCorto = "" });
76
}
}
La vista Index.
Crear la vista tipada para la clase Grupo con contenido List.
<h2>Administrar grupos</h2>
Filtrar por Programa Educativo: <%: Html.DropDownList("IdPE", new SelectList(ViewData["ProgramasEducativos"]
as IEnumerable, "IdPE", "NombreCorto", ViewData["IdPE"]))%>
Cuatrimestre:
<% // si se tiene la lista de cuatrimestres, crear el combo con sus opciones, sino, crear uno vacío %>
<% if (ViewData["cuatrimestres"] != null)
{ %>
77
<%: Html.DropDownList("IdCuatrimestre", new SelectList(ViewData["cuatrimestres"] as IEnumerable, "Id",
"Periodo", ViewData["IdCuatrimestre"]))%>
<%}
else
{ %>
<select id="IdCuatrimestre" name="IdCuatrimestre"></select>
<% } %>
<br /><br />
<table id="GruposTbl">
<% //Cambia de TR a THEAD la fila del encabezado de la tabla %>
<thead>
<th></th>
<th>
Id Grupo
</th>
<th>
Nombre
</th>
<th>
Cuatrimestre
</th>
<th>
Tutor
</th>
</thead>
<% //Agrega la etiqueta TBODY a la tabla
//Lo utilizaremos para manipular su contenido %>
<tbody>
<% foreach (var item in Model) { %>
<tr>
<td>
<%: Html.ActionLink("Editar", "Editar", new { id=item.IdGrupo }) %> |
<%: Html.ActionLink("Eliminar", "Eliminar", new { id=item.IdGrupo })%>
</td>
<td>
<%: item.IdGrupo %>
</td>
<td>
<%: item.Nombre %>
</td>
<td>
<% //Cambia el IdCuatrimestre por el periodo del cuatrimestre %>
<%: item.Cuatrimestre.PeriodoInicio + " " + item.Cuatrimestre.PeriodoFin %>
</td>
<td>
<% //Cambia el IdTutor por el nombre completo del tutor %>
<%: item.Tutor.Nombre + " " + item.Tutor.Apellidos%>
</td>
</tr>
<% } %>
</tbody>
78
</table>
<p id="LinkAltaGrupo">
<%: Html.ActionLink("Create New", "Create") %>
</p>
</asp:Content>
Comprobando la funcionalidad:
Al ejecutar nuestra aplicación y al ingresar a http://localhost:XXXX/AdministrarGrupos nos mostrará
nuestra vista como se ve en la imagen.
Ahora ingresemos a la vista pero especificando el parámetro idcuatrimestre cuyo valor sea un Id de
cuatrimestre válido, cheque sus PE´s registrados y vea los ID de alguno de sus cuatrimestres, en mi caso
mis ID de cuatrimestres válidos (año 2011) son los ID 9 y 10, así que probaré con uno de ellos.
79
Img. 5. Vista que recibe el id del cuatrimestre.
Hasta aquí va funcionando correctamente, pero hace falta algo muy importante, el registro de nuevos
grupos.
Lo que haremos será, si se ha especificado un Id de cuatrimestre para ver sus grupos, mostraremos el link
Registrar grupo, por ahora el link lo tenemos como “Create New“.
Así que en la vista Index, reemplace la linea de código (dentro del párrafo con id=”LinkAltaGrupo”):
por:
<% } %>
Ahora verifique que al ingresar en http://localhost:XXXX/AdministrarGrupos no muestre el link, y al
especificar el id de cuatrimestre si muestre el link,
ej. http://localhost:XXXX/AdministrarGrupos?idcuatrimestre=9.
Registrar Grupos.
Crearemos la acción Registrar y su vista en la cual el usuario solo tenga que seleccionar los valores
(nombre o letra del grupo y Tutor).
La acción Registrar.
public ActionResult Registrar(int id)
{
//Verificar que el cuatrimestre para el que se ha de registrar el grupo existe
80
Grupo nuevogrupo = new Grupo();
List<AuxTutor> tutores = new List<AuxTutor>(); //crear una lista con la nueva clase auxiliar
public int Id
{
get { return id; }
set { id = value; }
}
}
}
Crear la vista Registrar:
81
Img. 6. Crear la vista Registrar.
En esta vista utilizaremos una nueva vista parcial (plantilla) para no trabajar doble en las vistas
Registrar y Editar, el código de nuestra vista Registrar quedará así:
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Registrar grupo
</asp:Content>
<h2>Registrar grupo</h2>
<fieldset>
<legend>Datos del grupo</legend>
<p><%:ViewData["Mensaje"] %></p>
<%: Html.EditorFor(model => Model, ViewData["Tutores"] = ViewData["Tutores"]) %>
<p>
<input type="submit" value="Registrar grupo" />
</p>
</fieldset>
<% } %>
<div>
82
<%: Html.ActionLink("Regresar a la lista de grupos", "Index", new { idcuatrimestre = Model.Cuatrimestre.Id },
null)%>
</div>
</asp:Content>
Crear nuestra vista parcial llamada Grupo.ascx
En la carpeta Views/AdministrarGrupos agregue su carpeta EditorTemplates, de la misma forma
como se explicó en la parte 8 cuando empezamos a trabajar con la validación de formularios.
Ahora a EditorTemplates agregue una nueva vista parcial llamada Grupo:
<div class="editor-label">
Programa educativo
</div>
<div class="editor-field">
<% // crear un cuadro de texto donde se muestre el nombre del programa educativo como solo lectura %>
<input type="text" value="<%: Model.Cuatrimestre.ProgramaEducativo.NombreCorto %>" readonly="readonly"
/>
</div>
<div class="editor-label">
Cuatrimestre
</div>
<div class="editor-field">
<% // crear nuestro campo IdCuatrimestre
83
// como input Hiden que porte el Id del cuatrimestre (no se puede modificar)
// y aparte un cuadro de texto donde se muestre el periodo del cuatrimestre como solo lectura %>
<%: Html.HiddenFor(model => model.IdCuatrimestre)%>
<input type="text" value="<%: Model.Cuatrimestre.PeriodoInicio + " - " + Model.Cuatrimestre.PeriodoFin %>"
readonly="readonly" />
</div>
<div class="editor-label">
Nombre del grupo
</div>
<div class="editor-field">
<% //: Html.TextBoxFor(model => model.Nombre) %>
<% // Crear un combo para seleccionar la letra del grupo %>
<select id="Nombre" name="Nombre">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
<option value="E">E</option>
<option value="F">F</option>
</select>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
<div class="editor-label">
Tutor
</div>
<div class="editor-field">
<% //: Html.TextBoxFor(model => model.IdTutor) %>
<%: Html.DropDownList("IdTutor", new SelectList(ViewData["Tutores"] as IEnumerable, "Id", "Nombre"))%>
<%: Html.ValidationMessageFor(model => model.IdTutor) %>
</div>
Puede implementar un mecanismo para gestionar las letras para asignar como nombre del grupo, de forma
que pudiera comprobar cuales nombres ya existen y no mostrarlos a usuario para que no pueda
seleccionarlo como opción.
Ahora comprobemos la funcionalidad, para ello ingrese a ver los grupos de un cuatrimestre,
ej.http://localhost:XXXX/AdministrarGrupos?idcuatrimestre=9:
Vemos que tenemos nuestro link para agregar un nuevo grupo a este cuatrimestre.
84
Img. 8. Ir a registrar nuevo grupo.
Ahora al hacer clic sobre “Registrar nuevo grupo“, tenemos nuestro resultado:
85
{
//primero comprobaremos que el nombre del grupo no exista
//para el cuatrimestre seleccionado.
var comprobargrupo = BD.Grupo.Where(g => g.Nombre.Trim() == grupo.Nombre && g.IdCuatrimestre ==
grupo.IdCuatrimestre).FirstOrDefault();
//si el grupo NO existe, agregarlo a la BD.
if (comprobargrupo == null)
{
BD.AddToGrupo(grupo);
BD.SaveChanges();
86
Img. 10. Grupo registrado.
Ahora al intentar registrar nuevamente un grupo con la letra o nombre “A“, nos devuelve la vista con los
datos del grupo pero NO registrado.
87
Img. 11. Intentando registrar el mismo grupo.
Notese que para la clase Grupo no es están validando los datos (no se hace validación de formulario),
lo que se hace es que antes de registrar el grupo se verifica que no exista un grupo con el mismo
nombre o letra para el cuatrimestre seleccionado, si ya existe, pasamos un mensaje de error a la vista,
pero no esta de más que por su cuenta implemente la validación para esta clase y haga que los combos
aparezcan vacíos para que el usuario elija una opción.
Por su cuenta implemente la acciones Editar y Eliminar, solo considere que para eliminar un grupo
debe comprobar que no tenga asignado ningún alumno.
Esta parte del tutorial queda hasta aquí para no hacerlo más largo, en la siguiente parte agregaremos la
funcionalidad a los combos Programa educativo y Cuatrimestre para hacer las consultas AJAX y JSON
para cambiar el contenido de nuestra tabla de grupos, de esa forma el usuario no tendrá que regresar a
PE´s y seleccionar cuatrimestre para administrar sus grupos.
Todos los datos solicitados al servidor serán devueltos en formato JSON, para lo cual implementaremos
una acción en el controlador para que al solicitarle los cuatrimestres (para el PE seleccionado en el
combo.) nos devuelva el resultado y será procesado con JQuery.
88
var cuatrimestres = BD.Cuatrimestre.Where(c => c.IdPE == idpe && c.Anio == DateTime.Now.Year).ToList();
//limpiar el combo
$("#IdCuatrimestre").children("option").remove();
89
//Al seleccionar el cuatrimestre, cargar sus grupos
//...
Ahora agregue el archivo JS de JQuery y admingrupos.js a la vista Index como se hizo anteriormente
en un caso similar.
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script src=”../../Scripts/jquery-1.4.1.js” type=”text/javascript”></script>
<script src=”../../Content/Scripts/admingrupos.js” type=”text/javascript”></script>
Nota: Recuerde que la línea tachada es solo de referencia.
Ahora ingrese a la vista Index de AdministrarGrupos y seleccione el PE para comprobar que el combo de
cuatrimestres cambie sus opciones.
90
Ahora al seleccionar un cuatrimestres, deben cargarse sus grupos, para ello, agregaremos nuestra acción
al controlador.
Pero como una lista de alguna entidad de nuestro modelo no puede serializarse porque contiene
propiedades de navegación, debemos crear una nueva clase auxiliar.
Agregue una nueva clase llamada AuxGrupo a Models, implemente la nueva clase.
public class AuxGrupo
{
private int id;
private string nombre;
private string cuatrimestre;
private string tutor;
public int Id
{
get { return id; }
set { id = value; }
}
[ActionName("GruposCuatrimestre")]
public JsonResult GruposCuatrimestre(int idcuatrimestre)
{
//cargar los grupos del cuatrimestre seleccionado
var grupos = BD.Grupo.Where(g => g.IdCuatrimestre == idcuatrimestre).ToList();
91
Nombre = grupo.Nombre.Trim(),
Cuatrimestre = grupo.Cuatrimestre.PeriodoInicio.Trim() + " - " + grupo.Cuatrimestre.PeriodoFin.Trim() + " - " +
grupo.Cuatrimestre.Anio,
Tutor = grupo.Tutor.Nombre.Trim() + " " + grupo.Tutor.Apellidos.Trim()
});
}
http://localhost:XXXX/AdministrarGrupos/gruposcuatrimestre?idcuatrimestre=9
El resultado devuelto puede ser algo como:
if (idcuatrimestre > 0) {
//mostrar el nombre de PE y el cuatrimestre
$("#nombrepe").html($("#IdPE option:selected").html());
$("#nombrecuatrimestre").html($("#IdCuatrimestre option:selected").html());
92
Img. 4. Comprobando la funcionalidad.
Seleccione un cuatrimestre que tenga ya registrados algunos grupos.
93
Img. 1. Resultado final al terminar esta parte del tutorial.
Agregaremos un nuevo controlador llamado AdministrarAlumnos cuya acciónn Index muestre las listas
cargadas dinámicamente al seleccionar opciones en los combobox, pero antes, agregue el link en
cada registros de la tabla en la vista Index de AdministrarGrupos para acceder al
nuevo controlador.
94
Img. 3. Controlador AdministrarAlumnos.
El código lo iremos agregando poco a poco:
namespace EscuelaWebApp.Controllers
{
public class AdministrarAlumnosController : Controller
{
BDEscolarEntities BD = new BDEscolarEntities();
//
// GET: /AdministrarAlumnos/
return View();
}
}
}
La acción Index:
Como en la acción Indes de AdministrarGrupos, esta nueva acción también recibe un parámetro
(idgrupo), del cual mostraremos sus alumnos solo si el grupo pertenece al último cuatrimestre, caso
contrario, mostraremos un mensaje de error, y si no se recibe un valor para este parámetro, mostraremos
la vista vacía.
El código de la acción queda como:
95
//Lista de grupos
//Nombre del PE
//Periodo del cuatrimestre
//Nombre o letra del grupo
ViewData["IdPE"] = "";
ViewData["Grupos"] = "";
ViewData["IdGrupo"] = "";
ViewData["ProgramaEducativo"] = "";
ViewData["Cuatrimestre"] = " - ";
ViewData["Grupo"] = "";
ViewData["Tutor"] = "";
if (idgrupo == 0)
{
//Crear la lista vacía porque no se mostrará ningún alumno.
List<Alumno> alumnos = new List<Alumno>();
return View(alumnos);
}
else
{
//si se esta recibiendo el id del grupo a mostrar
var grupo = BD.Grupo.Where(g => g.IdGrupo == idgrupo && g.Cuatrimestre.Anio == DateTime.Now.Year)
.FirstOrDefault();
//recuperar el ultimo cuatrimestre del mismo PE que el grupo para verificar que el grupo corresponde a este
//Septiembre - Diciembre
//Mayo - Agosto
//Enero - Abril
//podemos ordenarlos descendentemente por PeriodoInicio y sacar el primero de la lista :D
96
//Crear la lista vacía porque no se mostrará ningún alumno.
List<Alumno> alumnos = new List<Alumno>();
return View(alumnos);
}
}else
return View("Error");
}
}
La vista Index:
La creamos tipada para la clase Alumno con contenido List.
97
<h2>Administrar listas de alumnos</h2>
Grupo:
<fieldset>
<legend>Datos de grupo</legend>
<strong>Programa Educativo</strong>: <span id="nombrepe"><%: ViewData["ProgramaEducativo"] %></span>,
<strong>Cuatrimestre</strong>: <span id="Cuatrimestre"><%: ViewData["Cuatrimestre"] %></span>,
<strong>Grupo</strong>: <span id="nombregrupo"><%: ViewData["Grupo"]%></span>.<br /><br />
<strong>Tutor</strong>: <span id="tutor"><%: ViewData["Tutor"]%></span>.<br /><br />
<table id="AlumnosTbl">
<% //Cambia de TR a THEAD la fila del encabezado de la tabla %>
<thead>
<th></th>
<th>
Matricula
</th>
<th>
Nombre
</th>
<th>
Apellidos
</th>
</thead>
<% //Agregar la etiqueta TBODY a la tabla
//Lo utilizaremos para manipular su contenido %>
<tbody>
<tr>
<td>
<%: Html.ActionLink("Editar", "Editar", new { id=item.Matricula }) %> |
<%: Html.ActionLink("Eliminar", "Eliminar", new { id=item.Matricula })%>
</td>
<td>
<%: item.Matricula %>
</td>
<td>
98
<%: item.Nombre %>
</td>
<td>
<%: item.Apellidos %>
</td>
</tr>
<% } %>
</tbody>
<% // Agregar el pie de la tabla para mostrar los totales %>
<tfoot>
<tr>
<th class="derecha">Total</th>
<th colspan="3"><span id="total"><%:Model.Count()
%></span>Alumnos</th>
</tr>
</tfoot>
</table>
<p id="LinkAltaAlumno">
<% if (ViewData["IdGrupo"] != "")
{ %>
<% } %>
</p>
</asp:Content>
</fieldset>
Las acciones a devolver JSON en el controlador.
Nuestro JS llamará a las acciones en el controlador que le devolverán tanto la lista de grupos del último
cuatrimestre para el PE seleccionado como la lista de alumnos para el grupo seleccionado, para ello
recuerde que no podemos transformar una colección de Entities a JSON, por lo tanto crearemos una clase
llamada AuxAlumno, ya hemos creado anteriormente otra clase llamada AuxGrupo que también se
utilizará aquí:
La acción Grupos:
Esta acción nos devolverá la lista de grupos del último cuatrimestre para el PE seleccionado.
[ActionName("Grupos")]
public JsonResult Grupos(int idpe)
{
//cargar los grupos del ultimo cuatrimestre del PE seleccionado
if (cuatrimestre != null)
{
//ahora obtenemos sus grupos
var grupos = cuatrimestre.Grupo.OrderBy(g => g.Nombre).ToList();
99
List<AuxGrupo> listagrupos = new List<AuxGrupo>();
{
"cuatrimestre":"Septiembre - Diciembre : 2011",
"grupos":[{
"Id":1,
"Nombre":"A",
"Cuatrimestre":"-",
"Tutor":"Juan Vicente Pérez López"
},
{
"Id":2,
"Nombre":"B",
"Cuatrimestre":"-",
"Tutor":"Pedro Torres"
}]
}
Otro caso sería, cuando se pasara un Id de PE no válido, ejemplo,
parahttp://localhost:2958/administraralumnos/grupos?idpe=20 el resultado será:
{"msg":"Error al recuperar los grupos del último cuatrimestre del PE seleccionado."}
La acción AlumnosGrupo:
Esta acción devolverá en resultado JSON la lista de alumnos para el PE seleccionado, para ello primero
crearemos la clase AuxAlumno de la que se comentó más arriba.
La clase AuxAlumno:
100
public class AuxAlumno
{
private int matricula;
private string apellidos;
private string nombre;
}
Ahora sí, la acción AlumnosGrupo:
[ActionName("AlumnosGrupo")]
public JsonResult AlumnosGrupo(int idgrupo)
{
//buscar el grupo
var grupo = BD.Grupo.Where(g => g.IdGrupo == idgrupo && g.Cuatrimestre.Anio ==
DateTime.Now.Year).FirstOrDefault();
//si existe el grupo
if (grupo != null)
{
//recuperar el ultimo cuatrimestre del mismo PE que el grupo para verificar que el grupo corresponde a este
//Septiembre - Diciembre
//Mayo - Agosto
//Enero - Abril
//podemos ordenarlos descendentemente por PeriodoInicio y sacar el primero de la lista :D
var cuatrimestre = BD.Cuatrimestre.Where(c => c.IdPE == grupo.Cuatrimestre.IdPE && c.Anio ==
DateTime.Now.Year).OrderByDescending(g => g.PeriodoInicio).FirstOrDefault();
101
}
} return this.Json(new { msg = "Error al recuperar los alumnos del grupo seleccionado, el grupo no corresponde
al último cuatrimestre." }, JsonRequestBehavior.AllowGet);
}
else
return this.Json(new { msg = "Error al recuperar los alumnos del grupo seleccionado, no se encontró el grupo." },
JsonRequestBehavior.AllowGet);
}
Para comprobar que funciona la acción AlumnosGrupo, registraremos a mano algunos alumnos para
algunos grupos.
102
Img. 6. Resultado en la vista Index.
El código JavaScript.
Ahora nuevamente entra en función el uso de jQuery para recuperar los datos desde el servidor, para ello,
cree un nuevo archivo llamado adminalumnos.js en la carpeta Content/Scripts como en los casos
anteriores, agreguemos entonces el código para recuperar los datos desde el servidor y mostrarlos en la
vista.
Nuestro JavaScript queda como este:
103
});
$("#IdGrupo").focus();
$("#notif").html(" <i><strong>Seleccione el grupo.</strong></i>");
});
}
});
if (idgrupo > 0) {
//mostrar el nombre del PE, periodo del cuatrimestre y grupo
$("#nombrepe").html($("#IdPE option:selected").html());
$("#NCuatrimestre").html($("#Cuatrimestre").val());
$("#nombregrupo").html($("#IdGrupo option:selected").html());
$("#tutor").html(data.tutor);
$.each(data.alumnos, function (i, item) {
//agregar los alumnos a la tabla
var fila = "<tr><td><a href=\"/AdministrarAlumnos/Editar/" + item.Matricula +
"\">Editar</a> | <a href=\"/AdministrarAlumnos/Eliminar/" + item.Matricula + "\">Eliminar</a></td><td>" +
item.Matricula + "</td>" + "<td>" + item.Nombre + "</td>" + "<td>" + item.Apellidos + "</td></tr>";
$("#AlumnosTbl").append(fila);
});
104
Img. 7. Accediendo a nuestras listas.
105
Img. 9. Seleccionando otro grupo en el combobox.
Hasta aquí es todo, por su parte implemente las acciones correspondientes para Registrar, Editar y si lo
prefiere, también Eliminar.
En la siguiente parte solo mostraré la generación de reportes partiendo desde estas listas de alumnos para
cada grupo.
106
Img. 1. Agregar link para imprimir lista.
El código a modificar en la vista Index para generar el link lo dejaremos como:
<p id="LinkAltaAlumno">
<% if (ViewData["IdGrupo"] != "")
{ %>
<%: Html.ActionLink("Registrar nuevo alumno en este grupo.", "Registrar", new { idgrupo = ViewData["IdGrupo"] },
null)%> |
<%: Html.ActionLink("Exportar como PDF", "ImprimirLista", new { idgrupo = ViewData["IdGrupo"] }, null)%>
<% } %>
</p>
Creando el reporte.
El reporte a crear solo consiste en llenarlo con la lista de alumnos del grupo que se este mostrando en
Index, por lo tanto, el reporte será llenado con una colección de objetos de la clase Alumno, ahora,para
poder establecer una fuente de datos (DataSource) a través de la cual se llenará el reporte, debemos
implementar un método en nuestra clase Alumno (puede ser también en una clase que en el servidor es
una vista o directamente podría llenarse a través de una función que es un procedimiento almacenado en
el servidor SQLServer).
Modificar la clase Alumno
Agregue una nueva clase Alumno en la carpeta Models (la clase será declarada como PARTIAL
CLASS, si ya implementó la validación del formulario para el registro y edición de Alumno, entonces ya
no es necesario volver a agregarla).
La partial class Alumno con nuestro nuevo método que será el DataSource de nuestro reporte quedará de
esta forma.
107
{
/*
* Aquí pueden ir o van las reglas de validación (Clase MetaData) si es que ya implementó la validación del form
*/
//Agregamos un nuevo método estático que devuelva la lista de alumnos
//que forman parte del grupo especificado
//utilizamos la interfaz IList para nuestra colección
public static IList<Alumno> ListaAlumnos(int idgrupo)
{
BDEscolarEntities BD = new BDEscolarEntities();
//recuperar la lista de alumnos desde la BD
var alumnos = BD.Alumno.Where(a => a.IdGrupoActual == idgrupo).OrderBy(a => a.Apellidos);
return alumnos.ToList();
}
}
Agregando el reporte:
Antes de agregar el reporte, agregaremos una nueva carpeta llamada Reportes en la carpeta Content.
108
Img. 3. Agregar el reporte.
Ahora tendremos el diseñador de informes, entonces agregaremos el DataSource para nuestro informe,
para esto, en la ventana Datos del informe, seleccione Nuevo > Conjunto de datos.
109
Img. 5. Seleccionar el conjunto de datos.
Agregamos el encabezado y pie al reporte, agregamos también la tabla en cuerpo del reporte.
En la ventana de Datos de informe, nos ubicamos sobre Parámetros, y agregamos los parámetros
correspondientes especificando el Nombre, Tipo de dato, etc.
110
Img. 8. Agregar parámetro.
Tendremos que agregar los parámetros que comentamos.
111
Img. 9. Diseño del reporte.
La acción ImprimirLista.
Esta acción que se encargará de llenar el reporte y enviarlo al navegador, comentamos que recibirá el ID
del grupo que se pretende exportar al reporte.
112
var grupo = BD.Grupo.Where(g => g.IdGrupo == idgrupo).FirstOrDefault();
if (grupo != null)
{
//declarar el objeto de la clase LocalReport
LocalReport localReport = new LocalReport();
localReport.ReportPath = Server.MapPath("~/Content/Reportes/AlumnosGrupo.rdlc");
//Llenar los 4 parámetros que creamos en el reporte
ReportParameter[] parametros = new ReportParameter[4];
parametros[0] = new ReportParameter("ProgramaEducativo",
grupo.Cuatrimestre.ProgramaEducativo.NombreCorto.Trim());
parametros[1] = new ReportParameter("Cuatrimestre", grupo.Cuatrimestre.PeriodoInicio.Trim() + " - " +
grupo.Cuatrimestre.PeriodoFin.Trim() + " "+grupo.Cuatrimestre.Anio);
parametros[2] = new ReportParameter("Grupo", grupo.Nombre.Trim());
parametros[3] = new ReportParameter("Tutor", grupo.Tutor.Nombre.Trim() + " "+grupo.Tutor.Apellidos.Trim());
//agregar los parámetros al reporte
localReport.SetParameters(parametros);
//Preparar el DataSource del reporte
//aquí invocamos al método ListaAlumnos que agregamos a la clase Alumno
//pasandole como valor de parámetro el id del grupo que se quiere imprimir
//No hemos hablado de vistas o procedimientos almacenados, sino, aquí se invocarían directamente
ReportDataSource reportDataSource = new ReportDataSource("DatosAlumnos", Alumno.ListaAlumnos(idgrupo));
//Ahora pasamos los datos al reporte
localReport.DataSources.Add(reportDataSource);
//declaramos las variables de configuración para el reporte
string reportType = "PDF";
string mimeType;
string encoding;
string fileNameExtension;
//En la configuración del reporte debe especificarse para el tipo de reporte
//consulte el sitio para más información
//http://msdn2.microsoft.com/en-us/library/ms155397.aspx
Warning[] warnings;
string[] streams;
byte[] renderedBytes;
//Transformar el reporte a bytes para transferirlo como flujo
renderedBytes = localReport.Render(reportType, null, out mimeType,
out encoding,
out fileNameExtension,
out streams,
out warnings);
//enviar el archivo al cliente (navegador)
return File(renderedBytes, mimeType);
}
else
return View("Error");
}
Ejecutemos ahora nuestra app e intentemos ver un grupo en un reporte como PDF en nuestro navegador.
Ejemplo: quiero ver el documento para el grupo con el ID=1, 2, etc. ingreso a la
url http://localhost:2958/AdministrarAlumnos/ImprimirLista?idgrupo=1 y obtengo
113
Img. 11. Reporte generado como PDF.
Y hasta aquí terminamos nuestra implementación de reportes en nuestra aplicación de ASP.NET MVC
2.
Una de las soluciones es utilizar CSS para crear un contenedor deslizable en nuestra vista y sin mover la
página podamos desplazar el contenido de la tabla.
114
Img. 1. Tabla deslizable en nuestra página con CSS.
.derecha
{
text-align:right;
}
Donde:
table tr:hover{ … } asignamos un color de fondo a la fila que le estemos pasando puntero del
ratón, si desea otro color, utilice algún mezclador de colores para obtener un color deseado, un
mezclador de colores en línea es http://www.colorpicker.com/.
#lista > table {99%} en esta linea #lista es el ID de un DIV que agregaremos como contenedor
de nuestra tabla; table{99%} estamos indicando que la tabla ocupe el 99% del ancho total de
nuestro DIV.
#lista { width: 800px; height: 120px; … } estamos asignando los estilos a nuestro DIV el cual
tendra un ancho de 800 pixeles y un alto de 120 pixeles (el resultado es la img 1), 120px se
estableció solo como prueba, debe modificar estos valores de acuerdo a sus necesidades (alto un
ancho del contenedor para mostrar la tabla.)
115
.derecha { … } es un estilo que alinea el texto a la derecha de cualquier elemento HTML que lo
herede.
Nota: tenga en cuenta que en la img 1 se aprecia la barra de desplazamiento del DIV porque el alto es
solo de 120 px y el alto del contenido de la tabla es mayor a 120px, por lo tanto si al aumentar el alto del
DIV y el alto del contenido de la tabla en pixeles es menor que el alto del DIV, la barra de
desplazamiento no se mostrará.
Antes de utilizar nuestro CSS, nuestra vista Index de AdministrarPE se aprecia de esta forma:
Ahora lo que haremos a nuestra vista es agregar el DIV que comentamos y agregar el pie a nuestra tabla.
El código de nuestra vista quedará en parte como se muestra en el siguiente código, recuerde que solo
vamos a agregar lo que nos hace falta para complementar nuestras funciones, no volveremos a
implementar desde cero nuestra vista.
El código que ya tenemos está tachado y el resaltado en negrita es el que vamos a agregar.
<h2>Lista de programas educativos.</h2>
<div id="lista">
<table>
<tr>
<th></th>
<th>
Id
</th>
<th>
Nombre
</th>
<th>
Nombre corto
</th>
</tr>
<tfoot> <tr>
<th class="derecha">Total</th>
<th colspan="3"><%:Model.Count() %> Programas educativos</th>
</tr>
116
</tfoot>
... contenido de nuestra tabla (aquí va el foreach que genera las filas.) ...
</table>
</div>
Ahora ejecutemos nuestra aplicación y vayamos a AdministrarPE y tendremos el resultado:
Ahora haga lo mismo que se hizo con la vista Index.aspx a las vistas que desee mostrar la tabla
deslizable en el DIV (vistas con contenido List), solo recuerde que ya no tendrá que crear más
archivos CSS y agregarlos, ya que ya se encuentra agregado en nuestro Site.Master, solo agregará el pie
a la tabla y el DIV.
Vista: Index de AdministrarTutores.
117