Como crear un árbol con Ext y Rails (Parte 2) +

Por Boris Barroso

En la primera parte de este tutorial pudimos ver como se crea un árbol, como usar el plugin “betternestedset” de Rails, continuearemos con la segunda y ultima parte del tutorial incorporando mas funcionalidad y empecemos.

Además de poder realizar movimiento de nodos en el árbol, en esta parte les voy a mostrar como crear nuevos nodos, editar el texto de ellos y borrar. Comencemos con la creación de nuevos nodos, para crear un nuevo nodo es necesario tener algún botón o comando que nos permita esta operación, por eso es que creamos el toolbar en el TreePanel (Panel Ext que contiene el árbol):

tbar : [{text: "Crear", handler: createNode},
      {text: "Borrar", handler: deleteNode}],

En la parte en donde se crea el árbol var tree = new Ext.tree.TreePanel({ hay un lugar al final donde creamos un toolbar tbar en el cual definimos el texto y la función a la cual llamaran para poder editar estos nodos. Primero esta la función createNode que debe estar definida antes de crear el árbol de la siguiente forma:

createNode = function() {
    if(!currentNode. isExpanded()) {
      currentNode.expand();
      return ;
    }
    var node = new Ext.tree.TreeNode({text: 'Nuevo Nodo'});
    currentNode.appendChild(node);
    currentNode = node;
    ge.editNode = node;
    ge.startEdit(node.ui.textNode);
}

Como pueden ver asignamos a una variable createNode = function(), pero funcionaría de igual forma si es que hiciéramos function createNode(), esto es debido a que cuando uno define una función en JavaScript en realidad también esta definiendo un objeto el cual puede ser instanciado o extendido, sigamos con la explicación, lo primero que realiza es consultar si el nodo esta expandido el nodo puede adicionar nodos adicionales aún si no esta extendido, pero el problema reside en que no estamos seguros cuales son los hijos de este nodo, cada ves que uno hace click en el icon de “+”del árbol se hace una llamada Ajax al servidor para poder obtener sus hijos, en caso de que ya haya sido expandido ya no hará la llamada, si el nodo ha sido expandido se adiciona un nuevo nodo . Ahora como funciona la edición del texto, es bastante simple, para editar hagan “click” en el nodo que deseen editar y después de nuevo hagan “click” en el texto del nodo ( nota no funciona con doble click) cuando se hace click se comienza la edición, Ext se encarga de que existan los componentes necesarios para esto, una ves que se termina de editar el nodo se activa un evento ge.on(’beforecomplete’, function(editor, value, startValue){, donde ge es el editor y lo que dice es cuando se ha completado la edición se hace llamada a una función, los componentes en Ext tienen sus propiedades, funciones y eventos, los eventos generalmente pasan parámetros (Lean la documentación en la carpeta “docs”).

//Evento que permite verificar si el nodo se ha modificado
  ge.on('beforecomplete', function(editor, value, startValue){
    //console.log("Editor: %o, Value: %o, StartValue: %o", editor, value, startValue);
    if(/^[a-z]+-[0-9]+$/.test(editor.editNode.id)) {
      url = ‘/categories/create/’;
    }else{
      url = ‘/categories/edit/’;
    }

    //editot.editNode.id; Id del nodo en edicion
    Ext.Ajax.request({
      url: url,
      params: {
        “category[id]“: editor.editNode.id,
        “category[text]“: value,
        “category[parent_id]“: editor.editNode.parentNode.id,
        authenticity_token: AUTH_TOKEN},
      success: function(resp, o) {
        try{
          r = Ext.decode(resp.responseText);
          if(!r.success) {
            o.failure();
            return;
          }
          //Se debe dar los valores correctos al nuevo nodo
          //en caso de que se adicione un nuevo nodo
          if(url == ‘/categories/create/’) {
            //Darle el id nuevo al nodo
            editor.editNode.id = r.id;
          }
        }catch(e) {
          o.failure();
        }
      },
      failure: function(resp, o) {
        editor.editNode.setText(startValue);
        Ext.Msg.show({title:’Error’, msg:’Existio un error al salvar’, buttons: Ext.MessageBox.OK,icon:Ext.MessageBox.ERROR});
      }
    });
  });

Esta función es bastante larga no solo nos permite adicionar nuevos nodos sino que tambien nos permite editar el texto. Lo primero que se hace es revisar con una expresión regular si es que el id del nodo es de tipo numérico o texto, en caso de que sea numérico se realiza la edición y en caso de que sea texto se crea un nuevo nodo, se inicia una llamada Ajax en el cual se pasa los parámetros params y en caso de que la respuesta sea correcta, re realiza las modificaciones correspondientes al nodo. El código Ruby para poder adicionar nodos es el siguiente:

#Creacion de un nuevo nodo
  def create
    #Se elimina el id para que no realize actualizacion
    params[:category].delete(:id)
    node = Category.create(params[:category])
    node.save
    #Se emparenta con el nodo del cual se creo en caso de que no sea el root
    if params[:category][:parent_id].to_i > 0
      node.move_to_child_of params[:category][:parent_id].to_i
    end
    render :json => {:success => :true, :id => node.id}
  end

y el código para poder realizar la edición es el siguiente:

#Editar el texto del nodo
  def edit
    #eliminar el parametro parent_id, no lo necesitamos en la edición
    params[:category].delete(:parent_id)
    #Buscar nodo a editar
    @category = Category.find(params[:category][:id])
    #Actualizar nodo y respuesta
    if @category.update_attributes(params[:category])
      render :json => {:success => true}
    else
      render :json => {:success => false}
    end
  end

No creo que sea necesario explicar ya que esta bien comentado y el código Ruby es muy simple (No es maravilloso Ruby). Para poder borrar un nodo es necesario realizar lo siguinte:

//Borrar Nodo
  deleteNode = function() {
    //No se debe borrar el nodo root
    if(currentNode.id == 0)
      return false;
    //Mensaje para confirmar el borrado
    Ext.Msg.confirm('Borrar nodo', 'Esta seguro de borrar el nodo '+currentNode.text+' ?', function() {
       if(conf=='yes'){
        Ext.Ajax.request({
          url: '/categories/delete/',
          //Parametros
          params: {id: currentNode.id, authenticity_token: AUTH_TOKEN },
          success: function(resp, o) {
            //Manejo de excepciones
            try {
              var r = Ext.decode(resp.responseText);
              if(r.success)
                currentNode.remove();
                //Debido a que se ha borrado el nodo se debe seleccionar un current node
                currentNode = root;
            }catch(e) {
              o.failure();
            }
          },
          //Error al borrar
          failure: function() {
            Ext.Msg.show({
              title:'Error',
              msg:'No se pudo borra el nodo '+currentNode.text,
              buttons: Ext.MessageBox.OK,icon:Ext.MessageBox.ERROR
            });
          }
        });
      }
    });
  }

Primero se revisa de que el id del nodo sea distinto a 0; osea que no sea la raíz, se realiza una consulta de si esta seguro de borrar el nodo y se realiza una llamada Ajax la cual si nos devuelve una respuesta positiva eliminamos el nodo seleccionado currentNode.remove(); y cambiamos currentNode a la raíz. El código Ruby para borrar es el siguiente:

#Borra los nodos
  def delete
    save = true
    #Manejo de excepciones
    begin
      node = Category.find(params[:id]) #id del nodo a borrar
      node.destroy
    rescue ActiveRecord::RecordNotSaved
      save = false
    end
    #Presentar el resultado JSON
    render :json => {:success => save}
  end

Lo que hace es primero busca el nodo con el id que le pasaron y luego se realiza el borrado usando
node.destroy, se verifica de que el borrado sea correcto para lo cual la variable save retorna si fue borrado correctamente y se envía una respuesta JSON. Pueden descargar de aquí el código fuente que usamos para este tutor

3 comentarios en “Como crear un árbol con Ext y Rails (Parte 2)”

  1. Hola, no puedo entender c

  2. Me gustaria saber como puedo ver el ejemplo funcionando si lo tienes publicado en algun lugar sin tener que instalarlo ????

  3. Bueno, no tengo un ejemplo en línea pero si te animas a descargar y probar el proyecto enano http://github.com/boriscy/enano/tree/master , podrás ver como funciona, es muy sencillo de instalar. :)

Su comentario