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
Hola, no puedo entender c
Me gustaria saber como puedo ver el ejemplo funcionando si lo tienes publicado en algun lugar sin tener que instalarlo ????
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.