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) ? '' : '';
    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.