Eseguire codice javascript contenuto in una response Ajax

datedomenica 8 novembre 2009 alle 9.30  - posted by Manuel Scapolan in Javascript

Ajax consente l'aggiornamento dinamico di una pagina web, ma se nel contenuto della response è presente del codice javascript questo non viene eseguito.
Come possiamo risolvere questo problema?

Cercando in rete ho trovato che la soluzione più semplice è quella di estrarre con una regular expression il codice all'interno del tag <script> e di passarlo alla funzione eval(). Vediamo in dettaglio un esempio utilizzando per la chiamata Ajax la libreria prototype:

var regex = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
 
function DoAjax(url)
{
  new Ajax.Request(url,
      {
         method: 'get',
         onSuccess: updateAjaxArea,
         onFailure: showMessage
      });
}
 	
function updateAjaxArea(transport)
{
  $('ajaxarea').innerHTML = transport.responseText;
  var str = transport.responseText; 
  var scripts = str.match(regex);
  eval(scripts[0].replace(/(<\s*\/?\s*)script(\s*([^>]*)?\s*>)/gi,''));
}
 	
function showMessage(transport)
{
  alert('An error occurred during the AJAX request.');
}

tagsTags:

Ajax TreeView con Yahoo! User Interface Library via JSON

datesabato 26 settembre 2009 alle 16.23  - posted by Manuel Scapolan in Javascript

E' incredibile la facilità con cui è possibile soddisfare funzionalità applicative anche complesse utilizzando opportunamente le API della libreria Yahoo! User Interface. Ecco ad esempio come possiamo ottenere un controllo TreeView in grado di caricare dinamicamente i nodi tramite Ajax:

YAHOO.widget.AjaxTreeView = function(id, nodes, path, action) {
  if (id) { this.init(id); }
  if (nodes) {
    this.path = path;
    this.action = action;
    if (path && path.length > 0) {
      this.selectedValue = path[path.length - 1];
    }
  this.buildTreeDynamically(nodes);
  }
  else if (YAHOO.lang.trim(this._el.innerHTML)) {
    this.buildTreeFromMarkup(id);
  }
};

Al costruttore passiamo l'id dell'elemento contenitore (tipicamente un div), la lista dei nodi "radice" in formato JSON, il percorso dei nodi da aprire per arrivare al nodo corrente e l'action a cui far puntare il link dell'etichetta di un nodo.
Nel pezzo di codice seguente vediamo come estendere l'oggetto TreeView di Yahoo! per aggiungere il caricamento dinamico dei nodi:

YAHOO.extend(YAHOO.widget.AjaxTreeView, YAHOO.widget.TreeView, {
  path: null,
  action: null,
  selectedValue: null,
  
  buildTreeDynamically: function(nodes) {
    var roots = [];
    roots = YAHOO.lang.JSON.parse(nodes);
    this.root.setDynamicLoad(loadNodeData);
    for (var i = 0; i < roots.length; i++) {
      var oData = roots[i];
      var aNode = new YAHOO.widget.DynamicNode(oData, this.root);
      if (aNode.isActive) {
        if (this.selectedValue &&
          aNode.contentElId == eval(this.selectedValue)) {
          aNode.labelStyle = 'ygtvlabel-selected';
          aNode.href = null;
        }
        if (this.path && this.path.length > 0) {
          for (var level = 0; level < this.path.length; level++) {
            if (this.path[level] == aNode.contentElId) {
              aNode.expand();
            }
          }
        }
      }
      else {
        aNode.isLeaf = true;
        aNode.href = null;
      }
    }
    this.draw();
  },
  
  getNodeById: function(id) {
    var node = null;
    for (var i in this._nodes) {
      var n = this._nodes[i];
      if (n.contentElId == id) {
        node = n;
        break;
      }
    }
    return node;
  }
});

Il metodo buildTreeDynamically riceve i nodi radice dal costruttore e li salva in un array locale tramite la funzione parse dell'API JSON (riga 08). Poi imposta la funzione di caricamento dinamico dei nodi (vedi snippet seguente). Successivamente per ogni elemento dell'array controlla se il nodo è attivo (riga 13), se non lo è viene definito come foglia ed impostato il link a null (righe 27-30). Se il nodo è attivo controlla se è quello corrente o se fa parte del percorso per arrivare al nodo corrente, in quest'ultimo caso lo espande (riga 22).

function loadNodeData(node, fnLoadComplete) {
 var path = node.tree.path;
 var selectedValue = node.tree.selectedValue;
 
 var callback = {
  success: function(oResponse) {
    var childrenNodes = [];
    var;
    try {
      if (oResponse.responseText != "") {
        childrenNodes = YAHOO.lang.JSON.parse(oResponse.responseText);
      }
      else {
        node.isLeaf = true;
      }
    }
    catch (x) {
      alert("Server non raggiungibile.");
      return;
    }
    for (var k = 0; k < childrenNodes.length; k++) {
      var oData = childrenNodes[k];
      var aNode = new YAHOO.widget.DynamicNode(oData, node);
      if (path && path != "") {
        for (var level = 0; level < path.length; level++) {
          if (path[level] == aNode.contentElId) {
            aNode.expand();
          }
        }
      }
      if (selectedValue && aNode.contentElId == eval(selectedValue)) {
        aNode.labelStyle = 'ygtvlabel-selected';
        aNode.href = null;
        selected = true;
      }
    }
    oResponse.argument.fnLoadComplete();
  },
 
  failure: function(oResponse) {
    oResponse.argument.fnLoadComplete();
  },
 
  argument: {
    "node": node,
    "fnLoadComplete": fnLoadComplete
  },
 
  timeout: 8000
 };
 if (node.isActive) {
  YAHOO.util.Connect.asyncRequest('GET', 
            node.tree.action + node.contentElId, callback);
 }
};

La funzione loadNodeData viene chiamata all'espansione di un nodo del TreeView e corrisponde ad una GET (righe 52-53) verso un servizio applicativo che mette nella risposta HTTP la collezione di nodi da caricare in formato JSON. Questi nodi vengono poi interpretati nella funzione di callback in modo analogo a come visto per i nodi radice.

A completamento dell'esempio qui di seguito vediamo il codice che definisce la struttura del DynamicNode ovvero una specializzazione dell'oggetto Node di Yahoo! al quale sono state aggiunte informazioni specifiche come lo stato di inattività.

YAHOO.widget.DynamicNode = function(oData, oParent) {
  if (oData) {
    if (YAHOO.lang.isString(oData)) {
      oData = { label: oData };
    }
    else {
      this.label = oData.Label;
      this.href = oData.Href;
      this.isLeaf = oData.IsLeaf;
      this.isActive = oData.IsActive;
    }
    this.init(oData, oParent, false);
    this.nowrap = true;
    this.contentElId = oData.Id;
    this.setUpLabel(oData);
  }
};
  
YAHOO.extend(YAHOO.widget.DynamicNode, YAHOO.widget.Node, {
  
  labelStyle: "ygtvlabel",
  labelElId: null,
  label: null,
  href: null,
  target: '_self',
  isActive: true,
  
  setUpLabel: function(oData) {
    if (YAHOO.lang.isString(oData)) {
      oData = {
        label: oData
      };
    }
    this.label = oData.Label;
    this.labelElId = "ygtvlabelel" + this.index;
  },
  
  /**
  * Ritorna la rappresentazione in HTML del nodo eseguendo 
  * un override del metodo della classe base Node per 
  * aggiungere le funzionalità  di attivo/disattivo
  * e la presenza degli attributi target, href e title.
  * @method getContentHtml
  * @return {string} rappresentazione in HTML del nodo
  */
  getContentHtml: function() {
    var sb = [];
    sb[sb.length] = (this.isActive && this.href) ? '<a' : '<span';
    sb[sb.length] = ' id="' + this.labelElId + '"';
    sb[sb.length] = ' class="' + 
     (this.isActive ? this.labelStyle : 'ygtvlabel-disabled') + '"';
    if (this.href) {
      sb[sb.length] = ' href="' + this.href + '"';
      sb[sb.length] = ' target="' + this.target + '"';
    }
    sb[sb.length] = ' title="' + this.label + '"';
    sb[sb.length] = ' >';
    sb[sb.length] = this.label;
    sb[sb.length] = (this.isActive && this.href) ? '</a>' : '</span>';
    return sb.join("");
  }
});

In conclusione questo esempio dimostra ancora una volta come sia semplice estendere le API di Yahoo! integrandone le funzionalità a vantaggio dell'interfaccia utente.

Autosave del contenuto di una textarea in un cookie con Yahoo! User Interface Library

dategiovedì 25 giugno 2009 alle 17.37  - posted by Manuel Scapolan in Javascript

Può essere utile avere a disposizione nelle applicazioni web un sistema di salvataggio automatico del testo, vediamo come ottenerlo con l'aiuto della Yahoo! User Interface Library.

Utilizziamo per l'occasione il componente Rich Text Editor e l'utility di gestione dei cookie. Con lo snippet seguente dichiariamo l'editor e poi ne salviamo il testo ad intervalli regolari in un cookie:

var editor1 = new YAHOO.widget.Editor('textarea1', {
   toolbar: {
   titlebar: 'Testo:',
   collapse: true,
   buttons: [
    { group: 'style',
    buttons: [
      { type: 'push', label: 'Grassetto', value: 'bold' },
      { type: 'push', label: 'Corsivo', value: 'italic' },
      { type: 'push', label: 'Sottolineato', value: 'underline' },
      { type: 'push', label: 'Ripristina', value: 'recovery' }]
    }
   ]}
});
var started = false;
editor1.on('editorKeyDown', function(ev) {
   if(!started){
      setInterval("saveContentToCookie()",15000);
      started = true;
   }
  }, this, true);
function saveContentToCookie() {
   if(editor1)
   {
      var content = editor1.getEditorHTML();
      if(content.length > 5){ //comprendo anche il br
         YAHOO.util.Cookie.set("recovery", content, { expires:
          new Date(new Date().getTime() + 2*24*60*60*1000) });
      }
   }
}

Analizziamo il codice passaggio per passaggio: prima creo una istanza del Rich Text Editor di Yahoo! (righe 01-14) poi all'evento KeyDown dell'editor associo un handler che faccia partire ad intervalli regolari la funzione che esegue il salvataggio del testo nel cookie (righe 15-21). Infine nella funzione di salvataggio (righe 22-31) prendo il testo contenuto nell'editor e lo salvo nel cookie attraverso il metodo Cookie.set dell'utility messa a disposizione dall'API Yahoo! YUI.

Per recuperare i dati dal cookie aggiungo un pulsante all'editor (vedi riga 11 snippet precedente) al click del quale visualizzo in un div i dati presenti nel cookie prelevati precedentemente attraverso il metodo Cookie.get.

Ma andiamo con ordine. Prima vediamo la struttura del div utilizzato per visualizzare il contenuto del cookie:

<div id="cookieContent">
<div class="hd">
Contenuto salvato
</div>
<div id="cookieContentBody" class="bd">
<script>
    document.getElementById('cookieContentBody').innerHTML = 
        YAHOO.util.Cookie.get("recovery");
   </script>
</div>
<div class="ft">
   <span style="font-size: 10px; color: #666666">
   (Il testo viene salvato ogni 15 secondi)</span>
</div>
</div>

Il div ha una struttura ben definita che usufruisce degli skin messi a disposizione dall'API di Yahoo! e nel suo corpo accede al DOM per inserire nell'html il contenuto del cookie.

Successivamente da codice javascript istanzio su quel div un oggetto Panel inizialmente non visibile ed al click del pulsante dell'editor definito per l'occasione lo visualizzo attraverso il metodo show():

var cookieContent = new YAHOO.widget.Panel("cookieContent", 
    { xy:[300,220], visible:false, width:"500px" } );
cookieContent.render(); 
 
editor1.on('toolbarLoaded', function() {
   this.toolbar.on('buttonClick', function(o) {
   switch(o.button.value) {
      case 'recovery':
        onRecoveryClick(o, editor1);
          break;
      }
   });
 }, editor1, true);
var onRecoveryClick = function (e, obj) {
   cookieContent.show();
};

Per rendere del tutto user-friendly il recupero dei dati salvati ho aggiunto una funzione che chiamata da un pulsante all'interno del Panel consente di incollare il contenuto del cookie direttamente nell'area di testo dell'editor:

function pasteContentFromCookie() {
   if(editorRisposta){
      editorRisposta.setEditorHTML(YAHOO.util.Cookie.get("recovery"));
   }
   cookieContent.hide();
}

In conclusione aggiungo che per far funzionare il tutto è necessario linkare nell'head i file sorgenti della libreria Yahoo! YUI e precisamente:

<script src="/yahoo-min.js" type="text/javascript"></script>
<script src="/cookie-min.js" type="text/javascript"></script>
<script src="/element-min.js" type="text/javascript"></script>
<script src="/container-min.js" type="text/javascript"></script>
<script src="/button-min.js" type="text/javascript"></script>
<script src="/editor-min.js" type="text/javascript"></script>

E questo è davvero tutto!

tagsTags: ,

About me

manuel scapolanSono un consulente informatico. Nel 2004 terminati gli studi in Ingegneria Informatica (1° livello), ho iniziato come freelance collaborando con una ditta di consulenza informatica ed una agenzia di marketing e comunicazione nello sviluppo di applicazioni web. Attualmente divido il lavoro di sviluppatore e progettista web con attività di formazione nel settore della programmazione.
View Manuel Scapolan's profile on LinkedIn

Follow me on Follow manuelscapolan on Twitter

Calendario


<<  agosto 2010  >>
lumamegivesado
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

Disclaimer

Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati mediante:
creative commons
Attribuzione: Non commerciale
Condividi allo stesso modo. R.2.5

Books (a bit more about my library)

Domain Driven Design - Eric Evans Applying Domain-Driven Design and Patterns - Jimmy Nilsson Refactoring to Patterns - Joshua Kerievsky Design Patterns -  Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides Code Complete Second Edition - Steve McConnell Patterns of Enterprise Application Architecture - Martin Fowler Agile Principles, Patterns, and Practices in C# - Robert C. Martin xUnit Test Patterns - Gerard Meszaros Refactoring - Martin Fowler CLR via C# Second Edition - Jeffrey Richter Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries - Krzysztof Cwalina, Brad Abrams Don't make me think! - Steve Krug Bulletproof Ajax - Jeremy Keith

Manuel Scapolan Copyright © 2007 - 2010 - Tutti i diritti riservati - Powered by BlogEngine.NET 1.5.0.7 - silk icons by famfamfam - Time CET