SharePoint, jQuery y la Experiencia de Usuario

jQuery es un  framework de Javascript que nos permite realizar ciertas maldades en SharePoint para mejorar la experiencia del usuario.

jquery

Una de las grandes ventajas de SharePoint es que es una aplicación extensible. Podemos crear o editar los formularios estándares de las listas, podemos crear Web Parts de usuarios, nuevos tipos de datos y definir cual es la interfaz html que implementa el tipo, etc.

Toda esta potencia en forma de extensibilidad no es nada si no le aplicamos una experiencia de usuario buena, y para esto podemos utilizar jQuery.

Por ejemplo, SharePoint nos permite definir campos de búsquedas que se renderizan como Combos y permiten al usuario seleccionar un elemento de una lista externa. Cuando tenemos más de 10 elementos a buscar, la usabilidad de ese Combo deja mucho que desear,  ¿por qué no añadir un elemento al formulario que nos permite buscar ese elemento y en el caso que se encuentren más de un resultado, que le de la elección al usuario, en un Modal Popup, con la lista de elementos encontrados?.

sharepoint_jquery_1

Cuando el usuario escribe un término de búsqueda y pulsa enter o hace click en la imagen, si el resultado no es único, obtiene un Modal Popup con el resultado de la búsqueda para que seleccione el elemento que estaba buscando.

sharepoint_jquery_2

Una vez seleccionado y para mejorar aún su experiencia, se le incluye un resumen a la derecha del formulario de alta.

sharepoint_jquery_3

Estas mejoras se han desarrollado modificando el formulario de alta NewForm.aspx inyectando código con jQuery. Se han utilizado varias librerías, entre las que se encuentra una librería que nos permite consultar elementos de listas de SharePoint utilizando jQuery y los servicios web de este (SPServices).

   1: <script language="javascript" type="text/javascript" src="/jquery/jquery-1.4.2.min.js"></script>

   2: <script language="javascript" type="text/javascript" src="/jQuery/jquery.SPServices-0.5.2.min.js" ></script>

   3: <script language="javascript" type="text/javascript" src="/jQuery/jquery-ui.js"></script>

   4: <script language="javascript" type="text/javascript" src="/jQuery/Load.js"></script>

Cuando se carga el formulario se inyecta el label, input y button para la búsqueda.

   1: $(document).ready(function() {

   2:  

   3:     var trow = $("<tr>");

   4:  

   5:     $("<h3>").addClass("ms-standardheader")

   6:         .text("Buscar")

   7:         .appendTo(

   8:             $("<td>").addClass("ms-formlabel")

   9:                 .css("width", "190")

  10:                 .attr("noWrap", "nowrap")

  11:                 .attr("vAlign", "top")

  12:                 .appendTo(trow)

  13:         );

  14:     $("<td>").addClass("ms-formbody")

  15:         .html("<div style='float:right; margin:2px 10px 0 0;'><img id='btSearch' src='_layouts/images/checkitems.gif' alt='Comprobar' title='Comprobar' style='cursor:pointer;' /></div>' +

  16:             "<span dir="none"><input id="tfBuscar" name="tfBuscar" title="Buscar" class="ms-long" type="text" maxLength="255" style='width:365px' /></span>' +

  17:             "<div id='progressSearch' style='display: none;'>Buscando...</div>')

  18:         .css("width", "400")

  19:         .attr("vAlign", "top")

  20:         .appendTo(trow);

  21:  

  22:     $($(".ms-formtable").children("tbody").children("tr")[0]).before(trow);

  23:  

  24:     $("#btSearch").click(function() {

  25:         clickImageButtonService("Buscar");

  26:     });

  27:     $("#tfBuscar").focus().keypress(function(event) {

  28:         if ((event.keyCode && event.keyCode == '13') || (event.which && event.which == '13')) {

  29:             event.preventDefault();

  30:             clickImageButtonService("Buscar");

  31:         }

  32:     });

La función de búsqueda utiliza AJAX para realizar la consulta a un servicio WCF JSON externo (obtiene los datos un sistema de información externo) y los muestra. Si el resultado de la búsqueda es mayor que uno, se muestra el Modal Popup.

   1: function clickImageButtonService(displayName) {

   2:     indicatorID = '#progressSearch'; 

   3:     var valueQuery = $("input:[title='" + displayName + "']").val();

   4:     var params = { "search": valueQuery };

   5:     var urlService =  "http://server/test/service.svc/json/Search";

   6:  

   7:     if (valueQuery != "" && valueQuery != "undefined") {

   8:         var incObj = $("#incResult");

   9:         if (incObj != null && incObj != undefined)

  10:             incObj.remove();    

  11:             

  12:         $.ajax({

  13:             type: "GET",

  14:             url: urlService,

  15:             data: params,

  16:             contentType: "application/json; charset=utf-8",

  17:             dataType: "json",

  18:             success: successFunction,

  19:             error: function(xhr, err) {

  20:                 alert("Se ha producido un error al realizar la búsqueda.rnrnEstado del proceso: " + 

  21:                 xhr.readyState + "rnCódigo de estado: " + xhr.status +

  22:                 "rnMensaje de respuesta: " + xhr.responseText); 

  23:             }

  24:         });

  25:     }

  26: }

Esta es la función que se ejecuta cuando la llamada asíncrona al servicio es satisfactoria.

   1: function successFunction(msg) {

   2:     if (msg != null) {

   3:         if (msg.d.length == 0) {

   4:             initFields();

   5:             var divObj = $("#errorMesage");

   6:             if (divObj.html() != null && divObj != undefined) {

   7:                 divObj.css("display","block");

   8:             }

   9:             else {

  10:                 $("#tfBuscar").parent("span")

  11:                     .append("<div id='errorMesage' class='ms-error'>No sé encontró ningún elemento. Realice una nueva búsqueda.</div>");

  12:             }

  13:         }

  14:         else {

  15:             var divObj = $("#errorMesage");

  16:             if (divObj != null && divObj != undefined) {

  17:                 divObj.css("display","none");

  18:             }

  19:             if (msg.d.length > 1) {

  20:                 createDynamicTableServices(msg.d);

  21:                 $("#modalPopUp td img")

  22:                     .click(function() {

  23:                         fillFieldsFromTable($(this).parent("td").parent("tr"));

  24:                         $("#modalPopUp").dialog('close');

  25:                     });

  26:                 $("#modalPopUp").dialog({ resizable: false, draggable: false, height: 400, width: 600, modal: true, closeOnEscape: false });

  27:             }

  28:             else {

  29:                     fillFieldsFromDataServices(msg.d);

  30:             }

  31:         }

  32:     }

  33: }

Para mostrar el resumen del elemento encontrado en la búsqueda, se realiza una llamada al método GetListItems del servicio web de lista de SharePoint (Lists.asmx). SPServices nos permite utilizar los servicios de SharePoint pero como si fueran funciones jQuery, tan solo tenemos que construir el CAML con la consulta y los campos que queremos recibir de la lista.

   1: function searchIncidencias(numero) {

   2:  

   3:     var queryString = "<Query><Where><And>" +

   4:         "<Contains><FieldRef Name='Numero' /><Value Type='Text'>" + numero + "</Value></Contains>" +

   5:         "<Or><Eq><FieldRef Name='Estado' /><Value Type='Lookup'>Iniciada</Value></Eq>" +

   6:         "<Eq><FieldRef Name='Estado' /><Value Type='Lookup'>En curso</Value></Eq></Or>" +

   7:         "</And></Where></Query>";

   8:  

   9:     var viewFields = "<ViewFields><FieldRef Name="ID"></FieldRef><FieldRef Name="Title"></FieldRef><FieldRef Name="Serie"></FieldRef>" +

  10:             "<FieldRef Name="Estado"></FieldRef><FieldRef Name="Fecha_x0020_Alta"></FieldRef></ViewFields>";

  11:     

  12:     $().SPServices({

  13:         operation: "GetListItems",

  14:         listName: "Solicitudes",

  15:         async: false,

  16:         CAMLViewFields: viewFields,

  17:         CAMLQuery: queryString, 

  18:         completefunc: completeSearchIncidencias

  19:     });

  20: }

  21:  

  22: function completeSearchIncidencias(xData, Status) {

  23:     var tdHtml = "<td valign="top"><div id="incResult" style='margin-left: 5px; margin-top: 29px;' > </div></td>';

  24:     $($("#onetIDListForm").children("tbody").children("tr")[0]).append(tdHtml);

  25:     var tbody = $("<table>").attr("cellspacing", "0").attr("cellpadding", "0");

  26:     var trow = $("<tr>");

  27:     $("<td>").addClass("ms-toolbarContainer")

  28:             .html("<span class="ms-toolbar" style='padding: 2px 2px 2px 2px; height = 18px;'>Incidencias activas</span>')

  29:             .attr("colspan", "2").appendTo(trow);

  30:     trow.appendTo(tbody);

  31:     $("<td>").html("<img height="5" width="1" alt="" src="/_layouts/images/blank.gif">").attr("colspan", "2").appendTo($("<tr>").appendTo(tbody));

  32:  

  33:     if ($(xData.responseXML).find("z\:row").length > 0) {

  34:         $(xData.responseXML).find("z\:row").each(function() {

  35:             trow = $("<tr>");

  36:             var incidencia = getEnlaceIncidencia($(this).attr("ows_ID"), $(this).attr("ows_Title"));

  37:             $("<td>").addClass("ms-formlabel").html("<h3 class="ms-standardheader">Número</h3>").appendTo(trow);

  38:             $("<td>").addClass("ms-formbody").html(incidencia).appendTo(trow);

  39:             trow.appendTo(tbody);

  40:             trow = $("<tr>");

  41:             $("<td>").addClass("ms-formlabel").html("<h3 class="ms-standardheader">Número serie</h3>").appendTo(trow);

  42:             $("<td>").addClass("ms-formbody").text($(this).attr("ows_N_x00fa_mero_x0020_Serie")).appendTo(trow);

  43:             trow.appendTo(tbody);

  44:             trow = $("<tr>");

  45:             $("<td>").addClass("ms-formlabel").html("<h3 class="ms-standardheader">Fecha Alta</h3>").appendTo(trow);

  46:             var fecha = $(this).attr("ows_Fecha_x0020_Alta");

  47:             formatDate(fecha);

  48:             $("<td>").addClass("ms-formbody").text(formatDate(fecha)).appendTo(trow);

  49:             trow.appendTo(tbody);

  50:             trow = $("<tr>");

  51:             $("<td>").addClass("ms-formlabel").html("<h3 class="ms-standardheader">Estado</h3>").appendTo(trow);

  52:             $("<td>").addClass("ms-formbody").text($(this).attr("ows_Estado").split("#")[1]).appendTo(trow);

  53:             trow.appendTo(tbody);

  54:             $("<td>").addClass("ms-formline").html("<img height="5" width="1" alt="" src="/_layouts/images/blank.gif">").attr("colspan", "2").appendTo($("<tr>").appendTo(tbody));

  55:         });

  56:     }

  57:     else {

  58:         trow = $("<tr>");

  59:         var incidencia = getEnlaceIncidencia($(this).attr("ows_ID"), $(this).attr("ows_Title"));

  60:         $("<td>").addClass("ms-formlabel").attr("colspan", "2").text("No encontradas incidencias activas").appendTo(trow);

  61:         trow.appendTo(tbody);

  62:         $("<td>").addClass("ms-formline").html("<img height="5" width="1" alt="" src="/_layouts/images/blank.gif">").attr("colspan", "2").appendTo($("<tr>").appendTo(tbody));

  63:     }

  64:     $("#incResult").append(tbody);

  65: }

  66:  

Gracias a jQuery hemos sido capaces de mejorar notablemente la experiencia del usuario y que nuestra lista de SharePoint sea más usable.

Aunque el mundo de jQuery, este código lo dice todo, es un poco singular, tengo que reconocer que la facilidad de realizar estas modificaciones me ha asombrado gratamente. Una vez que hemos realizado estos cambios, me he propuesto hacerle más seguimiento a jQuery y buscarle más utilidades en SharePoint.

 

Saludos a todos…

3 comentarios sobre “SharePoint, jQuery y la Experiencia de Usuario”

  1. hola Alexis

    no conozco ningún libro de jquery y sharepoint, es cuestión de echarle imaginación y algunas ayuditas como la spservices.

    piensa que sharepoint no es más que una aplicación asp.net bastante extensible

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *