Post Taggati ‘jQuery’

magnifier – plugin per immagini navigabili con jQuery

Nessun Commento »

magnifier- Logo
Questo plugin permette di costruire un navigatore in stile photoshop su di una miniatura, caricando in maniera asincrona l’immagine originale in fullsize. E’ ideale per immagini macro, panoramiche, o per mappe statiche.

La versione attuale (0.5) si trova ancora in fase di beta, come al solito ogni segnalazione di errori o suggerimenti d’implementazione è ben accetta.

Come funziona

Molto banalmente, basta inserire la miniatura nel proprio html, settarne uno pseudo attributo fullsize con il percorso dell’immagine originale, ed eseguire il metodo .magnifier() su di essa.
fullsize, non è un attributo html standard, e probabilmente non lo diverrà mai, ma è semanticamente espressivo, e se non facciamo troppa attenzione alla validazione della nostra pagina web, tanto basta.

Per default il plugin aggiunge il contenitore dell’immagine originale direttamente nel parent del contenitore della miniatura, settandone le dimensioni a 4 volte la miniatura stessa.
Ovviamente questo comportamento è configurabile tramite parametri, descritti estensivamente nella pagina ufficiale del plugin.

HTML:

<img src="miniature.jpg" fullsize="fullsize.jpg" alt="navigator" />

JS, parametri di default:

$("img[fullsize]").magnifier();

Posizioniamo invece la miniatura in alto a destra, e modifichiamo il messaggio di attesa:

$("img[fullsize]").magnifier({
    position: "top-right",
    loadingMessage: "Caricamento in corso..."
});

Compatibilità

E’ stato testato correttamente con Firefox 3.0.x, Opera, Safari, Chrome, e IE6/IE7.


showcase – slideshow plugin per jQuery

45 Commenti »

seek - Logo
Showcase è un plugin per jQuery che consente di costruire in maniera semplicissima un moderno slideshow per un set di immagini, consentendo un’ampia personalizzazione .

Edit 02/02/2010: versione 2.0! Tutto nuovo, date un’occhiata alla documentazione.
Edit 27/07/2009: rilasciata la versione 1.0, che implementa la creazione asincrona del set di immagini.

La versione attuale (0.5) si trova ancora in fase di beta, ogni segnalazione di errori o suggerimenti d’implementazione è ben accetta.

Come funziona

Edit 27/07/2009: Vedere le note di rilascio della versione 1.0.

Allo stato attuale il plugin richiede la presenza di un div contenente una serie di tag anchor contenenti a loro volta le immagini che si intende visualizzare. L’attributo alt dell’immagine, poi, viene utilizzato per visualizzare una descrizione dell’immagine.

Ipotizzando la presenza di un div con id showCase1, il metodo per attivare lo showcase è molto semplicemente:

$("#showCase1").showcase();

Nel caso si voglia personalizzare l’aspetto dello showcase sono previsti numerosi parametri (per una descrizione completa consultare la pagina ufficiale del plugin).

Se ad esempio volessimo utilizzare un effetto di slide verticale e numerare i pulsanti di navigazione:

$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true }
});

Ancora, vogliamo che i pulsanti di navigazione compaiano nell’altro angolo dell’immagine, e che siano disposti verticalmente:

$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true,
                     position: "top-left",
                     orientation: "vertical" }
});

Visualiziamo la miniatura delle immagini nei pulsanti di navigazione:

$("#showCase1").showcase({
    animation: { type: "vertical-slider" },
    navigator: { showNumber: true,
                     position: "top-left",
                     showMiniature: true,
                     orientation: "vertical",
                     item: { width: "64px", height: "48px" } }
});

Problemi noti

Il plugin si comporta correttamente con Firefox 3.0.x, Opera, Safari e Chrome, ma con IE6/IE7 si verificano (in maniera casuale, a quanto pare) dei problemi nel caricamento delle informazioni di dimesione delle immagini. Una possibile soluzione sarebbe quella di scolpire larghezza/altezza dello showcase in fase d’istanziazione, prevedendo un paio di parametri in più. Ma ciò non mi convince granchè.

Edit 26/05/2009:
Nella versione 0.5 il problema della lettura delle dimensioni dell’immagine sembra definitivamente risolto.


seek – plugin di evidenziazione elementi

5 Commenti »

seek - Logo
Si tratta di un plugin dalla dubbia utilità, ma altamente spettacolare, che consente di evidenziare tramite un’animazione uno o più elementi della pagina.

Potrebbe essere impiegato nell’evidenziazione degli elementi di una form validata, o chissà :)

Come funziona:

Il trucco consiste nel costruire dinamicamente un div che inizialmente ha la dimensione di un contenitore (di default il window) e che viene trasformato fino a boxare l’elemento indicato nel selettore; infine viene rimosso dal DOM.

Il metodo viene applicato banalmente:

$("#myDOMElement").seek();

E ci sono diversi aspetti customizzabili, attraverso i seguenti parametri:

  • css (object): espone alcune proprietà CSS
    • backgroundColor, borderColor, borderStyle, borderWidth, position, top, bottom, right, left
  • startFrom (jQuery object, default $(window)): indica l’elemento dal quale si esegue il morphing del div.
  • animation (object): ciò che riguarda la trasformazione.
    • easeFunction (string, default “swing”): il tipo di funzione easing applicata alla trasformazione, per una raccolta fantastica di funzioni fare
      riferimento a http://gsgd.co.uk/sandbox/jquery/easing/
    • speed (int, default 500): il tempo, espresso in millisecondi, di durata della singola animazione.
  • multiple (object): Se il selettore di partenza corrisponde a più elementi del DOM entrano in gioco le seguenti proprietà.
    • simultaneous (bool, default true): indica se l’animazione deve scattare simultaneamente per tutti gli elementi.
    • interval (int, default 800): il tempo, espresso in millisecondi, di intervallo tra l’animazione di un elemento e l’altro.

Compatibilità

jQuery.seek è stato testato con successo su IE6, IE7, Firefox 3.0.x, Safari 3&4, Opera 9.x e Google Chrome.

Contest

Il plugin può anche essere affascinante, ma lascia perplessi per quel che riguarda l’effettivo impiego.
Lanciamo quindi un piccolo contest: chi linkerà una pagina in cui si fa un uso creativo e funzionale del plugin vincerà… niente di che: una nota al merito nella pagina dei plugin di jQuery.

Happy seeking.


jqPrint – plugin di stampa per jQuery

20 Commenti »

livedemo

jqPrint - Logo
jqPrint è un plug-in per jQuery che consente di stampare una pagina o una porzione di html partendo da un elemento o un selettore jQuery.

Si basa principalmente sul famoso jPrintArea, di cui abbiamo corretto alcune imperfezioni e a cui abbiamo aggiunto alcune comode funzioni.

Come funziona:

Tutto ciò che bisogna fare è selezionare un elemento jQuery o un selettore e invocare il metodo jqprint():

$("div.toPrint").jqprint();
// . . .
$divToPrint.jqprint();

Otterremo la finestra di dialogo del sistema per la stampa.

Di default il plug-in ricerca tra i CSS della pagina i file con l’attributo media='print' e li importa nel documento da stampare, ciò permette di separare nettamente la presentazione per la normale navigazione col browser e per la stampa.

Update 19/06/2009

Attualmente sono previsti i seguenti parametri:

  • debug (bool, default false): permette di visualizzare in fondo al body il contenuto dell’iframe utilizzato per la stampa.
  • importCSS (bool, default true): indica al plugin se importare il CSS (con media=’print’, se presente) o se eseguire una stampa grezza del documento.
  • printContainer (bool, default true): indica al plugin se stampare anche l’html
    dell’elemento usato come selettore, se settato a false può causare dei problemi nell’applicazione delle regole css.
  • operaSupport (bool, default true): attiva un support per la stampa in Opera, effettuando la stampa da un tab temporaneo.

Compatibilità

jqPrint è stato testato con successo con Firefox 3.0.x, IE6, IE7, Safari 3&4, Google Chrome e, dalla versione 0.3, anche in Opera.
Attualmente la stampa dei documenti fra i vari browser presenta alcune diversità. E’ anche completamente dipendente dal CSS. Potrebbe essere interessante capire se siano applicabili delle funzioni di normalizzazione del layout… vedremo con le future release.

Per il momento buona stampa!


Costruire un semplice comando di ricerca per Ubiquity

3 Commenti »

ubiquityPer molti ancora è sconosciuto ma l’approccio di Ubiquity, l’estensione sperimentale di firefox, promette di cambiare la filosofia con cui utiliziamo il browser.

A detta dei suoi sviluppatori, infatti, ubiquity ha il compito di “connettere il web con il linguaggio” e ciò si traduce nella possibilità di utilizzare un linguaggio naturale per eseguire una serie di azioni sul web.
Immaginiamo ad esempio di visitare una pagina web in cui viene citato il nome di un film, o di un luogo di cui non conosciamo niente. In condizioni normali dovremmo aprire un altro tab del browser, collegarci a google, yahoo, imdb o quant’altro, incollare il testo da ricercare ed effettuare la ricerca. Con ubiquity possiamo selezionare il testo, premere CTRL+spazio (o la combinazione di tasti configurata) e selezionare uno dei comandi suggeriti, che agiranno in questo caso direttamente sulla selezione, consentendoci velocemente di effettuare una traduzione, una ricerca, una conversione di formato ecc…

E’un esempio piuttosto banale, in realtà ubiquity è molto di più, ma questo non vuol essere un articolo introduttivo sull’utilizzo di ubiquity, è una semplice guida su come costruire un comando di ricerca.
Per l’esattezza l’obiettivo è quello di costruire un comando di ricerca, con preview, per l’ottimo sito di q&a stackoverflow.com.

Come prima cosa dobbiamo accedere alla sezione di configurazione di ubiquity, eseguendo il comando help.
Da quì possiamo accedere alla pagina “Command editor”, che ci consentirà di digitare il codice dei nuovi comandi, che diventeranno immediatamente funzionanti:
ubiquity_0

Ovviamente avremo bisogno di dare uno sguardo alle command APIs e più precisamente alla libreria CmdUtils.
Fortunatamente, come possiamo constatare, esiste un helper per la creazione di un comando di ricerca standard, il metodo makeSearchCommand, una specializzazione del più generico CreateCommand.

Come possiamo vedere makeSearchCommand accetta 3 parametri: name, urle parser.

Banalmente il name sarà ciò che si dovrà digitare per eseguire il comando una volta che questo sia stato completato; possiamo utilizzare “stackoverflow” per esteso, o il più rapido “so”, che non corrisponde a nessun comando di default.
Per capire invece cosa passare ai parametri “name” e “parser” dobbiamo innanzitutto fare un po di reverse engineering su www.stackoverflow.com.

Proviamo come prima cosa ad eseguire una ricerca direttamente dal sito, ricercando i termini “asp.net”. Notiamo subito che il parametro di ricerca viene passato in querystring componendo il seguente url: “http://stackoverflow.com/search?q=asp.net”.

Possiamo quindi immaginare che il template dell’url, in conformità a quanto indicato nelle API, sarà una cosa del tipo: “http://stackoverflow.com/search?q={QUERY}”, dove il placeholder {QUERY} verrà di volta in volta sostituito dal parametro del nostro comando.

Il parametro parser è un po più complesso. E’ costituito da alcune proprietà attraverso cui possiamo indicare, al parser che si occupa della preview del comando, come interpretare il risultato della ricerca. Vediamo in dettaglio quelle che ci interessano:

  • container: indica il contenitore che incapsula i singoli risultati
  • title: il titolo del risultato
  • preview: cosa mostrare come anteprima
  • baseurl: nel caso in cui i link ai risultati siano espressi in indirizzi relativi, rappresenta la parte da rimontare a sinistra dell’indirizzo.

Le proprietà container, title e preview verranno utilizzate dal parser come selettori per generare oggetti jQuery. Quello che ci serve di sapere quindi è la struttura della pagina dei risultati di stackoverflow, e per questo ci basta una sbirciatina all’html con firebug:
ubiquity_1
Ora sappiamo che il selettore per il container sarà “.question-summary“, quello per il title “h3 a” e quello per la preview “.excerpt“.
Dal codice si nota anche che l’href del link del titolo è un indirizzo relativo, avremo quindi bisogno impostare baseurl a “http://stackoverflow.com”.

Abbiamo tutto ciò che ci serve a questo punto per costruire il comando di ricerca, che risulterà vergognosamente semplice:

CmdUtils.makeSearchCommand({
  name: "stackoverflow",
  url: "http://stackoverflow.com/search?q={QUERY}",
  parser: {container: ".question-summary",
           title: "h3 a",
           preview: ".excerpt",
           baseurl: "http://stackoverflow.com" }
});

Nel momento stesso in cui scriviamo il comando nella finestra Command Editor, se è sintatticamente corretto, diventa immediatamente disponibile in ubiquity. Premiamo quindi CTRL+spazio e scriviamo “stackoverflow” seguito dai termini che ci interessa ricercare.
Come previsto l’area di preview si popolerà con i primi 4 risultati disponibili, numerati per uno shortcut. A questo punto premendo invio eseguiremo la ricerca su un altro tab di firefox, mentre premendo CTRL+ALT+[numero] apriremo un tab direttamente al “baseurl+link” del risultato indicato dal numero del preview.

Ovviamente quest’esempio di comando risulta notevolmente semplificato dalla presenza del metodo makeSearchCommand, ma potrebbero presentarsi necessità più complesse di una semplice ricerca, basta dare un’occhiata ai comandi predefiniti per rendersi conto che abbiamo visto appena la punta dell’iceberg nell’analizzare il lato “developer” di questo interessantissimo componente.

Per ora è tutto. Liberi di commentare.


Cross binding client/server con XSLT

1 Commento »

download esempio

Non molto tempo fa mi sono trovato nella situazione di dover gestire il bind di una lista di elementi in maniera “mista”, ossia dovevo effettuare un primo bind lato server sulla base dei record di un database, e consentire poi all’utente finale di modificare lato client tale lista.

Il progetto in questione era in .NET, avrei potuto effettuare il bind lato server con un asp:Repeater o un qualsiasi altro controllo bindabile.
Lato client avrei potuto utilizzare jQuery per gestire l’input dell’utente rigenerando l’html degli elementi aggiunti, con tutte le chiamate ajax del caso per il salvataggio dei dati sul DB.

Il problema più grosso di questo approccio è che il template utilizzato nel controllo .NET (tipicamente l’ItemTemplate del Repeater) non è esportabile lato client, ciò significa che se avessi dovuto modificare la struttura degli elementi della lista l’avrei dovuto fare su entrambi i fronti, client e server, mantenendoli allineati.
Il bind lato client, poi, avrebbe dovuto essere effettuato allo stesso modo del server, quindi al disallineamento del template si sarebbe aggiunto un eventuale disallineamento delle logiche sui dati.

Non so se la soluzione da me trovata è già standardizzata né pretendo che sia la soluzione a tutti i problemi, analizzeremo in seguito i difetti di tale approccio, ma di sicuro ha una sua eleganza e portabilità.

Questa soluzione “cross” si basa fondamentalmente sull’utilizzo di XML per i dati e di fogli XSL per produrre frammenti di HTML partendo da tali dati, più alcuni accorgimenti per rendere il tutto il più automatico possibile.

Lato server
Per il nostro esempio (scaricabile) ci baseremo su una semplice lista anagrafica modellata dalle seguenti classi:

public class Anagrafiche
{
    public List<Persona> Persone { get; set; }
}

public class Persona
{
    public string Nome { get; set; }
    public string Cognome { get; set; }
    public string Comune { get; set; }

    public Persona() { }
}

Avremo quindi la necessità di presentare una lista di persone e di consentire all’utente di aggiungerne altre, evitando fastidiosi postback e l’utilizzo degli altrettanto fastidiosi UpdatePanel :)

La pagina .aspx contiene un controllo

<asp:Xml ID="xmlData" runat="server" />

che consente di renderizzare un documento XML (o un frammento XHTML, nel nostro caso) passatogli, effettuando eventualmente una trasformazione sui dati.
Avremo poi 2 fogli .XSL per effettuare le trasformazioni, che funzioneranno da template. Il primo è il contenitore della lista, persone.xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="persona.xsl" />
	<xsl:template match="/">
		<div class="persone">
			<span class="title">Lista utenti:</span>
			<xsl:apply-templates select="//Persona" />
			<div style="clear:both;"></div>
		</div>
	</xsl:template>
</xsl:stylesheet>

il secondo rappresenta invece l’elemento dell’anagrafica, persona.xsl:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:template match="Persona">
		<div class="persona">
			<span>
				<xsl:value-of select="Nome" />
			</span>
			<xsl:text xml:space="preserve"> </xsl:text>
			<span>
				<xsl:value-of select="Cognome" />
			</span>
			<span id="comune">
				Comune:
				<xsl:value-of select="Comune" />
			</span>
		</div>
	</xsl:template>
</xsl:stylesheet>

Nell’esempio creo la lista delle anagrafiche a mano, ovviamente i dati possono avere qualsiasi provenienza

Anagrafiche anag = new Anagrafiche();

anag.Persone = new List<Persona>();
anag.Persone.Add(new Persona() { Nome = "Mario", Cognome = "Rossi", Comune = "FantasiaPortamiVia" });
anag.Persone.Add(new Persona() { Nome = "Luigi", Cognome = "Verdi", Comune = "ComeSopra" });

Ora dobbiamo ottenere un documento XML dall’oggetto anag, fortunatamente ci viene in aiuto la serializzazione del .NET.
Modifichiamo le classi Anagrafiche e Persona, aggiungendo i seguenti attributi:

using System.Xml;
using System.Xml.Serialization;
...

public class Anagrafiche
{
    [XmlArray("Persone"), XmlArrayItem(typeof(Persona), ElementName = "Persona")]
    public List<Persona> Persone { get; set; }
}

[XmlRoot("Persona")]
public class Persona
{
	...
}

Ciò ci consentirà, tramite un XmlSerializer di ottenere un documento nel formato richiesto dai fogli XSL.

A questo punto possiamo già realizzare il bind lato server:

protected void Bind_Xml(Anagrafiche anag)
{
    // Serializzatore xml, sulla base della classe Anagrafiche
    XmlSerializer ser = new XmlSerializer(typeof(Anagrafiche));
    string sXml = "";

    // Creo un memorystream in cui riversare la stringa dell'xml, occhio allo using
    using (MemoryStream ms = new MemoryStream())
    {
        ser.Serialize(ms, anag);
        ms.Seek(0, SeekOrigin.Begin);

        StreamReader sr = new StreamReader(ms);

        sXml = sr.ReadToEnd();
    }

    if (sXml != "")
    {
        // Creo un XslTransform.
        // La classe è deprecata, in favore di XslCompiledTransform, ma per un piccolo esempio come questo andrà più che bene,
        // sembra anche più semplice da usare.

        XslTransform xsltRiepilogo = new XslTransform();
        xsltRiepilogo.Load(Server.MapPath("~/xslt/persone.xsl"));

        // Assegno il foglio xslt all'asp:Xml
        xmlData.Transform = xsltRiepilogo;

        // Assegno il documento xml al controllo, che in congiunzione con l'xslt produrrà l'html di cui ho bisogno.
        xmlData.DocumentContent = sXml;
        xmlData.DataBind();
    }
}

la parte a mio avviso più interessante è il processo automatico (con l’utilizzo dell’XmlSerializer) attraverso cui otteniamo il documento XML necessario, che per il nostro esempio sarà:

<?xml version="1.0"?>
<Anagrafiche xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Persone>
    <Persona>
      <Nome>Mario</Nome>
      <Cognome>Rossi</Cognome>
      <Comune>FantasiaPortamiVia</Comune>
    </Persona>
    <Persona>
      <Nome>Luigi</Nome>
      <Cognome>Verdi</Cognome>
      <Comune>ComeSopra</Comune>
    </Persona>
  </Persone>
</Anagrafiche>

Da notare il fatto che il documento ha appunto il formato dichiarato nelle classi da cui deriva [XmlArray("Persone") ...] e [XmlRoot("Persona")] ci hanno consentito di definire i tag di output.
Questo documento sarà quindi assegnato come stringa a sXml, a sua volta assegnato a xmlData.DocumentContent, come documento XML da elaborare.
La riga

xmlData.Transform = xsltRiepilogo;

definisce invece con quale foglio si vogliono processare i dati.
Basta un’infarinatura di XPath e XSLT per capire che l’output sarà un frammento HTML del tipo:

<div class="persone">
    <span class="title">Lista utenti:</span>
    <div class="persona">
        <span>Mario</span>
        <span>Rossi</span>
        <span id="comune"> Comune: FantasiaPortamiVia</span>
    </div>
    <div class="persona">
        <span>Luigi</span>
        <span>Verdi</span>
        <span id="comune"> Comune: ComeSopra</span>
    </div>
    <div style="clear: both;" />
</div>

Et voilà, abbiamo la prima parte dell’opera.

Lato client
Come dicevamo, vogliamo fare in modo che la lista possa crescere in modo asincrono tramite input inserito dall’utente.
Per far ciò utilizzeremo jQuery, più l’ottimo plug-in Transform che trovate a quest’indirizzo http://plugins.jquery.com/project/Transform, e che effettua lato client (e cross-browser, importante) le stesse operazioni della classe .NET XslTransform.

Come prima cosa dobbiamo configurare un web service che possa gestire le chiamate client, con un metodo che accetti in input nome, cognome e comune, e che ci restituisca quantomeno l’XML necessario ad effettuare la trasformazione con jQuery.

Ovviamente potremmo non passare dal server, tutto dipende da cosa nel dettaglio debba fare la nostra applicazione, ma c’è almeno un buon motivo per farlo: potremo utilizzare la serializzazione .NET su un elemento Persona, creato in base all’input dell’utente, ottenendo automaticamente l’XML che ci occorre per essere trasformato, questa volta non da persone.xsl, ma da persona.xsl.
E questo spiega anche perchè abbiamo creato 2 fogli anzichè uno solo…

Nel mio caso, poi, il passaggio per il web service era obbligato dalla necessità di salvare il record nel database.

Quindi, una volta creato il Web Service assicuriamoci che abbia i seguenti using e attributi:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;

[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class WebService : System.Web.Services.WebService {
	...
}

A questo punto creiamo una classe per le risposte al client:

[DataContract]
public class jsonResult
{
    [DataMember]
    public bool result { get; set; }

    [DataMember]
    public string xmlItem { get; set; }
}

Gli attributi [DataContract] e [DataMember] ci consentiranno di serializzare in json l’oggetto. Comodo no?
Il metodo per l’aggiunta delle persone, che chiameremo inserisci, è il seguente:

[ScriptMethod(UseHttpGet = false)]
[WebMethod(EnableSession = true)]   // Ci serve per poter recuperare la session
public jsonResult inserisci(string nome, string cognome, string comune)
{
    bool bResult = true;
    string xml = "";

    try
    {
        XmlSerializer ser = new XmlSerializer(typeof(Persona));
        Persona nuovaPersona = (new Persona() { Nome = nome, Cognome = cognome, Comune = comune });

        using (MemoryStream ms = new MemoryStream())
        {
            ser.Serialize(ms, nuovaPersona);
            ms.Seek(0, SeekOrigin.Begin);

            StreamReader sr = new StreamReader(ms);

            xml = sr.ReadToEnd();
        }
    }
    catch
    {
        bResult = false;
    }

    jsonResult json = new jsonResult() { result = bResult, xmlItem = xml };

    return json;
}

L’attributo [ScriptMethod(UseHttpGet = false)] ci permette di effettuare la chiamata al metodo in post.

Nell’html abbiamo intanto creato 3 input text con i seguenti id: txtNome, txtCognome e txtComune; più un button btnAggiungi per eseguire l’inserimento.
Il javascript è nell’esempio notevolmente semplificato, manca ogni tipo di validazione e gestione dell’errore, ma rende comunque l’idea:

$(function() {
    $("#btnAggiungi").click( function() {

        var v_nome = $("#txtNome").val();
        var v_cognome = $("#txtCognome").val();
        var v_comune = $("#txtComune").val();

        var jsonData = JSON.stringify({ nome: v_nome,
                                        cognome: v_cognome,
                                        comune: v_comune });

        $.ajax({
            type: "POST",
            url: "WebService.asmx/inserisci",
            data: jsonData,
            contentType: "application/json; charset=utf-8",
            success: function(resp) {
                var serverResp = JSON.parse(resp);
                var json = serverResp.d;

                if (json.result)
                {
                    $.transform({
                        xmlstr: json.xmlItem,
                        xsl: "xslt/persona.xsl",
                        async: false,
                        success: function(html, xsl, xml, obj) {
                            $(html).insertAfter(".persona:last");
                        }
                    });
                }
                else
                {
                    // Gestione dell'errore
                }
            },
            error: function() { /* Gestione dell'errore */ }
        });
    });
});

Come possiamo vedere viene gestito l’evento click di btnAggiungi, dove vengono raccolti i dati inseriti dall’utente, e tramite la libreria JSON2 (http://www.json.org/js.html) viene generata una stringa sulla base dell’oggetto che racchiude i dati da postare al metodo inserisci del Web Sevice (da notare che gli attributi hanno lo stesso nome dei parametri del metodo).

Viene poi effettuata una chiamata ajax al web service, postando l’oggetto jsonData.
La funzione di success riceve, nella variabile resp, il risultato della chiamata, ne effettua una conversione in oggetto json tramite JSON.parse (lato server l’avevamo serializzato) e verifica che json.d.result sia true (non si è verificato un errore lato server).
A questo punto utiliziamo il plug-in Transform per elaborare json.d.xmlItem che come abbiamo visto contiene l’XML della nuova persona, selezionando come foglio persona.xsl.
Nel success di $.transform, infine, effettuiamo l’append dell’html così ottenuto.

E questa è la fine del giro.

Abbiamo effettuato un bind che si potrebbe definire “cross”, svincolandoci dai template del .NET e creando una soluzione altamente portabile, ligia agli standard (XML, XPath e XSLT) e piuttosto flessibile.

Ovviamente ci sono anche i contro.
- Innanzitutto questo esempio funziona bene con controlli html, ma se volessimo utilizzare controlli Asp.Net nel template XSLT le cose si complicherebbero notevolmente.
- Eventuali paginazioni gestite lato server sono inutilizzabili, al crescere degli elementi dal client non ho controllo sul paginatore.
- Infine non ho idea delle prestazioni generali della soluzione. Come ho già detto la classe XslTransform è stata deprecata in favore della XslCompiledTransform, che risulta più veloce, ma non ho fatto dei test per confrontarle con i template di un controllo bindabile…

Diciamo semplicemente che può essere un interessante esperimento.

Qualche commento, suggerimento o errori da segnalare?


Estendere i selettori di jQuery

Nessun Commento »

download esempio

jQuery, il potente framework javascript creato da John Resig, è già fornito nativamente di versatili selettori standard, che coprono la quasi totalità delle necessità degli sviluppatori, garantendo la possibilità di selezionare gli elementi del DOM sulla base di una gerarchia, di un attributo o del contenuto.

Tuttavia possono crearsi situazioni in cui la selezione degli elementi risulta più complessa, creando la necessità di sviluppare specifiche funzioni che applichino una certa logica di selezione.

Fortunatamente esiste un semplice metodo per estendere le potenzialità del framework, creando dei selettori custom, personalizzabili, ed integrabili nelle precedenti espressioni di selezione.

Vediamo come.

Iniziamo con la creazione di un plug-in vuoto, in un file che chiameremo moreselectors.jquery.js, la struttura del plug-in sarà più o meno questa:

(function($) {
    // estensione del namespace jQuery
    $.extend(
        // con questo estendiamo il parser dei selettori jQuery...
        $.expr[":"],
        {
            // aggiungendo una nuova regola e relativa funzione
            espressione: function(e) { ... }
        }
    );
})(jQuery);

La funzione associata alla regola funzionerà da predicato, restituendo true o false determinerà l’inclusione o meno nell’array dei risultati dell’elemento corrente. Il parametro e, o element, indica l’elemento DOM in esame e sono possibili altri 2 parametri i che indica l’indice nell’array della selezione e c che rappresenta i componenti del selettore (come vedremo successivamente).
Una volta che si sarà definita l’espressione e si è incluso il plug-in della pagina la sintassi per il suo utilizzo è quella che ben conosciamo:

$("div:espressione")
// es. filtrerà i div che rispettano il predicato della funzione
$("div:not(:espressione)")
// filtra gli elementi div che non rispettano il predicato

Utiliziamo come esempio questo blocco di html:

...
<div>
<div class="divLevel1">
<div class="divLevel2">a</div>
</div>
<div class="divLevel1" style="border:dotted 1px #dedede;">
<div class="divLevel2">b</div>
</div>
<div class="divLevel1">(vuoto)</div>
<div class="divLevel1" style="background-color:Red;">
<div class="divLevel2">d</div>
</div>
</div>
...

e vediamo come può essere l’espressione per la selezione dell’unico div.divLevel1 senza elementi child, selettore che chiameremo per l’appunto “no-child” (virgolette necessarie, in quanto c’è il carattere -):

...
"no-child": function(e) {
                return $(e).find("*").length == 0;
            }
...
... utilizzo
$("div.divLevel1:no-child")

Ora tentiamo di fare qualcosa di più complicato: selezioniamo il div con lo sfondo rosso:

...
red: function(e) {
    var color = $(e).css("background-color").toLowerCase();
    if (color.indexOf("rgb") == -1)
    {
        return color == "#ff0000" || color == "red";
    }
    else
    {
        var rgb = $(e).css("background-color")
                      .replace("rgb(", "").replace(")", "");
        var componenti = rgb.split(", ");
        return (255 === parseInt(componenti[0], 10))
                && (0 === parseInt(componenti[1], 10))
                && (0 === parseInt(componenti[2], 10));
    }
}
...
... utilizzo
$("#div:red")

In questo caso abbiamo dovuto fare una distinzione tra il modo in cui IE e firefox trattano l’informazione sul colore. In firefox la lettura (quantomeno con jQuery) di un color o di un background-color restituisce la rappresentazione rgb del colore stesso, mentre in IE si ottiene o il nome del colore o la sua rappresentazione esadecimale.

Infine vediamo come può essere creato un semplice selettore con parametri.
Come abbiamo detto prima, il parametro c della funzione di espressione contiene i componenti del selettore, ed è in realtà un array restituito dal parser jQuery il cui contenuto, ipotizzando un selettore nella forma :selettore(parametro) è il seguente:
c[0] -> ':selettore(parametro)'
c[1] -> ':'
c[2] -> 'selettore'
c[3] -> 'parametro'

Detto ciò vediamo come può essere un selettore in grado di selezionare gli elementi con un border-style di un certo tipo:

...
hasBorder: function(e, i, c) {
    // con un'espressione regolare controlliamo che il parametro
    // appartenga a un insieme di valori consentiti
    if (/none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset/
        .test(c[3]))
    {
        // poi controlliamo che i 4 border-style corrispondano al
        // parametro indicato
        return ((c[3] == $(e).css("border-right-style")) &&
                (c[3] == $(e).css("border-bottom-style")) &&
                (c[3] == $(e).css("border-left-style")) &&
                (c[3] == $(e).css("border-top-style")));
    }
    else
    {
        return false;
    }
}
...
... utilizzo
$(".divLevel1:hasBorder(dotted)")

…e si potrebbe andare avanti per delle ore facendo esempi più o meno inutili.
I selettori così creati ovviamente coesistono con i selettori standard e niente ci vieta di combinarli come ci pare.
Commenti? Suggerimenti?