<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Desarrollosweb.net &#187; javascript</title>
	<atom:link href="http://www.desarrollosweb.net/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.desarrollosweb.net</link>
	<description>Desarrollo de webs y aplicaciones</description>
	<lastBuildDate>Fri, 26 Mar 2010 10:53:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Ext JS &#8211; Primera conferencia Anual y presentación de Ext JS 3.0</title>
		<link>http://www.desarrollosweb.net/2009/03/06/ext-js-primera-conferencia-anual-y-presentacion-de-ext-js-30/</link>
		<comments>http://www.desarrollosweb.net/2009/03/06/ext-js-primera-conferencia-anual-y-presentacion-de-ext-js-30/#comments</comments>
		<pubDate>Fri, 06 Mar 2009 09:28:04 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext JS]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[web 2.0]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=83</guid>
		<description><![CDATA[Jack Slocum y su equipo anuncian la celebración de la primera conferencia de Ext JS que se celebrará del 14 al 16 de Abril en Orlando, Florida, concretamente en el hotel Ritz-Carlton Grande Lakes. La conferencia pretende ser un punto de encuentro entre todos los usuarios de Ext JS, aunque también se aprovechará para hablar [...]]]></description>
			<content:encoded><![CDATA[<p>Jack Slocum y su equipo anuncian la celebración de la primera conferencia de <strong>Ext JS</strong> que se celebrará del 14 al 16 de Abril en Orlando, Florida, concretamente en el <em>hotel Ritz-Carlton Grande Lakes</em>.</p>
<p>La conferencia pretende ser un punto de encuentro entre todos los usuarios de <strong>Ext JS</strong>, aunque también se aprovechará para hablar de las novedades que el equipo de Ext JS nos tiene preparadas: <strong>Ext JS 3.0</strong>,<strong> Ext GWT 2.0</strong> y <strong>Ext Core 1.0</strong>.</p>
<p>El precio de la subscripción es de 250$ y si decides hacer noche en el hotel los organizadores han conseguido pactar un precio de 149$ la noche.</p>
<p>La conferencia se ha dividido en 24 sesiones en las que se tratarán diversos temas: novedades en los frameworks, optimización de rendimiento, buenas prácticas en el código, etc. pasando incluso por una sesión dedicada a javascript básico y temas Web 2.0 en general.</p>
<p>Parece ser que <strong>Ext JS 3.0</strong> viene cargado de novedades y no sólo estamos hablando de nuevos widgets, sino funcionalidades pensadas a mejorar la estructuración del código y facilitar la integración con servicios de datos.</p>
<p>Os mantendremos informados. De momento podéis consultar más información en los siguientes enlaces:</p>
<ul>
<li><a title="Ext JS Roadmap" href="# http://extjs.com/products/extjs/roadmap.php" target="_blank">http://extjs.com/products/extjs/roadmap.php</a></li>
<li><a title="Ext JS 2009 Conference" href="http://www.extjs.com/conference/" target="_blank">http://www.extjs.com/conference/</a></li>
<li><a title="Ext JS Blog, conference 2009" href="http://extjs.com/blog/2009/02/03/ext-conference-2009/" target="_blank">http://extjs.com/blog/2009/02/03/ext-conference-2009/</a></li>
<li><a title="Ext JS 2009 Conference Sessions" href="http://extjs.com/conference/sessions/" target="_blank">http://extjs.com/conference/sessions/</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2009/03/06/ext-js-primera-conferencia-anual-y-presentacion-de-ext-js-30/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cookbook ExtJS: Cargar un registro de un GridPanel en un FormPanel</title>
		<link>http://www.desarrollosweb.net/2008/12/03/cookbook-extjs-cargar-un-registro-de-un-gridpanel-en-un-formpanel/</link>
		<comments>http://www.desarrollosweb.net/2008/12/03/cookbook-extjs-cargar-un-registro-de-un-gridpanel-en-un-formpanel/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 09:40:14 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.Form]]></category>
		<category><![CDATA[Ext.GridPanel]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=51</guid>
		<description><![CDATA[¿Como hacemos para cargar los datos de un registro de un grid en un formulario? Bien, hay diversas opciones (siempre a gusto del programador) así que aquí vamos a explicar una de ellas: asociar el evento &#8216;rowdblclick&#8217; (doble click en una fila) a un handler que cargue el formulario con los datos del registro. GestorCuentas.movimientosGrid.on('rowdblclick',function( [...]]]></description>
			<content:encoded><![CDATA[<p>¿Como hacemos para cargar los datos de un registro de un grid en un formulario? Bien, hay diversas opciones (siempre a gusto del programador) así que aquí vamos a explicar una de ellas: asociar el evento &#8216;rowdblclick&#8217; (doble click en una fila) a un handler que cargue el formulario con los datos del registro.</p>
<pre name="code" class="javascript">
GestorCuentas.movimientosGrid.on('rowdblclick',function( grid, row, evt) {
        var movRecord = GestorCuentas.movimientosDataStore.getAt(row);
        GestorCuentas.movForm.getForm().load({
            url : 'cargaMovimiento.php',
            method: 'POST',
            params: {
                mov_id: movRecord.data.mov_id
            },
            waitMsg : 'Espere por favor'
        });
    });
</pre>
<p>En el API de ExtJS podemos comprobar que cuando asignamos una función al evento &#8216;rowdbclick&#8217; vamos a recibir tres parámetros: el grid que ha generado el evento, el índice del registro dentro del store y un objeto con datos adicionales del evento</p>
<p>Para quedarnos con el registro, sólo tenemos que solicitarle al store del grid que nos devuelva aquel cuyo índice hemos recibido por parámetro</p>
<pre name="code" class="javascript">
var movRecord = GestorCuentas.movimientosDataStore.getAt(row);
</pre>
<p>Ahora en movRecord tendremos el registro y podremos acceder a sus datos para quedarnos con el identificador del registro. Fijáos que en la definición del record del grid hemos incluído la Primary Key del registro en base de datos, aunque no lo estemos mostrando en el propio grid</p>
<p>Fijáos que el formulario que vamos a cargar tiene asignado un reader, de forma que podemos llamar al método load() del mismo para que automáticamente cargue los datos. Obviamente hay que indicarle que registro se va a cargar por lo que le pasamos como parámetro el mov_id del registro que hemos pedido al store</p>
<p>La instancia del formulario se la pedimos al FormPanel mediante el método getForm(). Esto nos devuelve un objeto Ext.form.BasicForm. Este método tiene un método load() al que le pasamos la configuración necesaria para hacer la carga</p>
<pre name="code" class="javascript">
    GestorCuentas.movForm.getForm().load({
            url : 'cargaMovimiento.php',
            method: 'POST',
            params: {
                // OJO, los datos del registro están en movRecord.data y no directamente en movRecord como podría pensarse
                mov_id: movRecord.data.mov_id
            },
            waitMsg : 'Espere por favor'
        });
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/12/03/cookbook-extjs-cargar-un-registro-de-un-gridpanel-en-un-formpanel/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>ExtJS CookBook: Código base</title>
		<link>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/</link>
		<comments>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/#comments</comments>
		<pubDate>Wed, 03 Dec 2008 09:16:17 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=49</guid>
		<description><![CDATA[Voy a iniciar una serie de artículos a los que voy a denominar &#8216;ExtJS Cookbook&#8217;, en los que iré explicando pequeñas acciones o trucos de ExtJS pero que por ser muy comunes merecen un poco de atención La primera parte de esta serie de artículos se van a basar en el código de una miniaplicación [...]]]></description>
			<content:encoded><![CDATA[<p>Voy a iniciar una serie de artículos a los que voy a denominar &#8216;ExtJS Cookbook&#8217;, en los que iré explicando pequeñas acciones o trucos de ExtJS pero que por ser muy comunes merecen un poco de atención</p>
<p>La primera parte de esta serie de artículos se van a basar en el código de una miniaplicación para gestión de un rudimentario libro de cuentas y que <a href="http://appsrv01.desarrollosweb.net/blog/extjs/cuentas/" target="_blank">tenéis disponible</a> para evaluar</p>
<p>Este ejemplo muestra cómo hacer varias cosas: cargar datos desde PHP en Extjs, cargar los datos de un FormPanel desde un GridPanel, filtrar un GridPanel desde un formulario externo, cómo obtener los datos de los campos de un FormPanel, etc.
<p>Espero que los artículos que publique estos días os sean de utilidad</p>
<h2>Fichero 1: Código Javascript de la miniaplicación</h2>
<pre name="code" class="javascript">
/*
 * Definimos nuestros propios validadores de datos
 * para los formularios
 */
Ext.apply(Ext.form.VTypes, {
    daterange : function(val, field) {
        var date = field.parseDate(val);

        if(!date){
            return;
        }
        if (field.startDateField &#038;&#038; (!this.dateRangeMax || (date.getTime() != this.dateRangeMax.getTime()))) {
            var start = Ext.getCmp(field.startDateField);
            start.setMaxValue(date);
            start.validate();
            this.dateRangeMax = date;
        }
        else if (field.endDateField &#038;&#038; (!this.dateRangeMin || (date.getTime() != this.dateRangeMin.getTime()))) {
            var end = Ext.getCmp(field.endDateField);
            end.setMinValue(date);
            end.validate();
            this.dateRangeMin = date;
        }
        return true;
    },
    /**
     * Definimos un nuevo validador: exclusionNotZero
     *
     * Valida un par de campos numéricos para que no puedan tener valor positivo los dos ni
     * valor cero los dos.
     *
     **/
    exclusionNotZero: function(val, field) {
        if (field.exclusionPair) {
            var pair = Ext.getCmp(field.exclusionPair);
            var pairValue = pair.getValue();

            if ((pairValue > 0) &#038;&#038; (val > 0)) {
                field.markInvalid('Un movimiento no puede tener a la vez haber y deber');
                return false;
            } else if ((pairValue == 0) &#038;&#038; (val == 0)){
                field.markInvalid('Un movimiento no puede tener haber y deber a cero');
                return false;
            } else {
                field.clearInvalid();
                pair.clearInvalid();
                return true;
            }
        }
        return true;
    }
});

Ext.onReady(function() {
    /*
     * Creamos un espacio de nombres
     */
    Ext.namespace('GestorCuentas');

    /*
     * Definimos el registro para un movimiento
     */
    GestorCuentas.movimientoRecord = new Ext.data.Record.create([
        {name: 'mov_id', type: 'int'},
        {name: 'mov_concepto', type: 'string'},
        {name: 'mov_fecha', type: 'date', dateFormat: 'Y-m-d'},
        {name: 'mov_deber', type: 'float'},
        {name: 'mov_haber', type: 'float'}
    ]);

    /*
     * Creamos el reader para el Grid de movimientos
     */
    GestorCuentas.movimientosGridReader = new Ext.data.JsonReader({
        root: 'data',
        totalProperty: 'total',
        id: 'mov_id'},
        GestorCuentas.movimientoRecord
    );

    /*
     * Creamos el DataProxy para carga remota de los datos
     */
    GestorCuentas.movimientosDataProxy = new Ext.data.HttpProxy({
        url: 'cargaMovimientos.php',
        method: 'POST'
    });

    GestorCuentas.movimientosDataStore = new Ext.data.Store({
        id: 'movimientosDS',
        proxy: GestorCuentas.movimientosDataProxy,
        reader: GestorCuentas.movimientosGridReader
    });

    /*
     * Creamos el modelo de columnas para el grid de movimientos de la cuenta
     */
    GestorCuentas.movimientosColumnMode = new Ext.grid.ColumnModel(
        [{
            header: 'Fecha',
            dataIndex: 'mov_fecha',
            width: 80,
            renderer: Ext.util.Format.dateRenderer('d/m/Y')
        },{
            header: 'Concepto',
            dataIndex: 'mov_concepto',
            width: 200
        },{
            header: 'Haber',
            dataIndex: 'mov_haber',
            width: 90
        },{
            header: 'Deber',
            dataIndex: 'mov_deber',
            width: 90
        }]
    );

    /*
     * Creamos el reader para el formulario de alta/modificación de movimientos
     */
    GestorCuentas.movimientosFormReader = new Ext.data.JsonReader({
        root : 'data',
        successProperty : 'success',
        totalProperty: 'total',
        id: 'mov_id'
        },GestorCuentas.movimientoRecord
    );

    /*
     * Creamos el grid de movimientos
     */
    GestorCuentas.movimientosGrid = new Ext.grid.GridPanel({
        id : 'mov-movimientos-grid',
        store : GestorCuentas.movimientosDataStore,
        cm : GestorCuentas.movimientosColumnMode,
        enableColLock : false,
        width : 750,
        height : 550,
        bbar : new Ext.PagingToolbar({
            pageSize: 20,
            store: GestorCuentas.movimientosDataStore,
            displayInfo: true
        }),
        selModel : new Ext.grid.RowSelectionModel({singleSelect:false})
    });

    /*
     * Creamos el formulario para filtrar el grid
     */
    GestorCuentas.filterForm = new Ext.FormPanel({
        id: 'form-filtro',
        region: 'north',
        split: false,
        frame: true,
        height: 100,
        width: 800,
        items: [{
            xtype: 'datefield',
            width: 200,
            fieldLabel: 'Desde',
            name: 'startdt',
            id: 'startdt',
            vtype: 'daterange',
            endDateField: 'enddt' // id of the end date field
        },{
            xtype: 'datefield',
            width: 200,
            fieldLabel: 'Hasta',
            name: 'enddt',
            id: 'enddt',
            vtype: 'daterange',
            startDateField: 'startdt' // id of the start date field
        }]
    });

    /*
     * Añadimos el botón para borrar el filtro
     */
    GestorCuentas.filterForm.addButton({
        text : 'Borrar filtro',
        disabled : false,
        handler : function() {
            GestorCuentas.filterForm.getForm().reset();
            GestorCuentas.movimientosDataStore.baseParams = {
                dateStart: '',
                dateEnd: ''
            };
            GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
        }
    });

    /*
     * Añadimos el botón para filtrar
     */
    GestorCuentas.filterForm.addButton({
        text : 'Filtrar',
        disabled : false,
        handler : function() {
            var startDate = GestorCuentas.filterForm.findById('startdt').getValue();
            var endDate = GestorCuentas.filterForm.findById('enddt').getValue();

            GestorCuentas.movimientosDataStore.baseParams = {
                dateStart: startDate.dateFormat('Y-m-d'),
                dateEnd: endDate.dateFormat('Y-m-d')
            };
            GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
        }
    });

    /*
     * Creamos el formulario de alta/modificación de movimientos
     */
    GestorCuentas.movForm = new Ext.FormPanel({
        id: 'form-movimientos',
        region: 'west',
        split: false,
        collapsible: true,
        frame: true,
        width: 300,
        minWidth: 300,
        height: 500,
        waitMsgTarget: true,
        reader: GestorCuentas.movimientosFormReader,
        items: [{
            fieldLabel : 'Haber',
            xtype: 'numberfield',
            id: 'frm_mov_haber',
            name : 'mov_haber',
            exclusionPair: 'frm_mov_deber',
            vtype: 'exclusionNotZero',
            allowBlank:false,
            width : 175
        },{
            fieldLabel : 'Deber',
            xtype: 'numberfield',
            id: 'frm_mov_deber',
            name : 'mov_deber',
            exclusionPair: 'frm_mov_haber',
            vtype: 'exclusionNotZero',
            allowBlank:false,
            width : 175
        }, {
            fieldLabel : 'Concepto',
            id: 'frm_mov_concepto',
            name : 'mov_concepto',
            allowBlank:false,
            xtype: 'textarea',
            width : 175,
            height: 225
        }, {
            fieldLabel : 'Fecha',
            id: 'frm_mov_fecha',
            name : 'mov_fecha',
            allowBlank:false,
            xtype: 'datefield',
            renderer: Ext.util.Format.dateRenderer('d/m/Y'),
            width : 175
        }, {
            id: 'frm_mov_id',
            name : 'mov_id',
            xtype: 'hidden'
        }]
    });

    /*
     * Añadimos el botón para borrar el formulario
     */
    GestorCuentas.movForm.addButton({
        text : 'Borrar formulario',
        disabled : false,
        handler : function() {
            GestorCuentas.movForm.getForm().reset();
        }
    });

    /*
     * Añadimos el botón para guardar los datos del formulario
     */
    GestorCuentas.movForm.addButton({
        text : 'Guardar',
        disabled : false,
        handler : function() {
            GestorCuentas.movForm.getForm().submit({
                url : 'salvaMovimientos.php',
                waitMsg : 'Salvando datos...',
                failure: function (form, action) {
                    Ext.MessageBox.show({
                        title: 'Error al salvar los datos',
                        msg: 'Error al salvar los datos.',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.ERROR
                    });
                },
                success: function (form, request) {
                    Ext.MessageBox.show({
                        title: 'Datos salvados correctamente',
                        msg: 'Datos salvados correctamente',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.INFO
                    });
                    responseData = Ext.util.JSON.decode(request.response.responseText);
                    GestorCuentas.movForm.getForm().reset();
                    GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
                }
            });
        }
    });

    /*
     * Añadimos el evento doble click en una fila para editar el registro correspondiente
     */
    GestorCuentas.movimientosGrid.on('rowdblclick',function( grid, row, evt) {
        var movRecord = GestorCuentas.movimientosDataStore.getAt(row);
        GestorCuentas.movForm.getForm().load({
            url : 'cargaMovimiento.php',
            method: 'POST',
            params: {
                mov_id: movRecord.data.mov_id
            },
            waitMsg : 'Espere por favor'
        });
    });

    /*
     * Creamos la ventana
     */
    GestorCuentas.contentWindow = new Ext.Window({
            id: 'libro-cuentas',
            title:'Gestor Libro de Cuentas',
            width:860,
            height:600,
            iconCls: 'icon-grid',
            shim:false,
            animCollapse:false,
            constrainHeader:true,
            layout: 'border',
            renderTo: document.body,
            items:[GestorCuentas.filterForm, GestorCuentas.movForm, {
                    id: 'col-grid-movimientos',
                    xtype: 'panel',
                    region: 'center',
                    width: 560,
                    layout: 'fit',
                    items:[GestorCuentas.movimientosGrid]
                }
            ]
    });

    /*
     * Mostramos ventana, la centramos y cargamos los datos iniciales en el grid
     */
    GestorCuentas.contentWindow.show();
    GestorCuentas.contentWindow.center();
    GestorCuentas.movimientosDataStore.load({params: {start:0,limit:20}});
});
</pre>
<h2>Fichero 2: PHP de carga de datos del grid</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

$db->setFetchMode(Zend_Db::FETCH_OBJ);

if (!empty($_POST['dateStart']) &#038;&#038; !empty($_POST['dateEnd'])) {
    $filterClause = "WHERE (mov_fecha >= '".$_POST['dateStart']."' AND mov_fecha <= '".$_POST['dateEnd']."')";
} else {
    $filterClause = ' ';
}

if (!empty($_POST['limit']) &#038;&#038; !empty($_POST['start'])) {
    $limit = $_POST['limit'];
    $start = $_POST['start'];
    $limitClause = "LIMIT $start,$limit";
} else {
    $limitClause = ' ';
}

$query = "SELECT * FROM mov_movimientos $filterClause ORDER BY mov_fecha ASC $limitClause";

$result = $db->fetchAssoc($query);

$resultados = array();

foreach ($result as $record) {
    $resultados[] = $record;
}

$retValue = array (
    'total' => count($result),
    'data' => $resultados
);

echo json_encode($retValue);
</pre>
<h2>Fichero 3: PHP de carga de datos del formulario</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

$query = "SELECT * FROM mov_movimientos WHERE mov_id=" . $_POST['mov_id'];

$db->setFetchMode(Zend_Db::FETCH_OBJ);
$result = $db->fetchAssoc($query);

$record = $result[$_POST['mov_id']];

$resultados = array();
$retValue = array (
    'total' => count($result),
    'success'=>true,
    'data' => array (
        array(
            'mov_id' => $_POST['mov_id'],
            'mov_concepto' => $record['mov_concepto'],
            'mov_haber' => $record['mov_haber'],
            'mov_deber' => $record['mov_deber'],
            'mov_fecha' => $record['mov_fecha']
        )
    )
);
echo json_encode($retValue);
</pre>
<h2>Fichero 4: PHP de guardado de datos desde el formulario</h2>
<pre name="code" class="php">
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

require_once 'Config/ConfigCuentas.php';

if (is_null($_POST['mov_id']) || empty($_POST['mov_id'])) {
    $query = "INSERT INTO mov_movimientos(mov_fecha,mov_concepto,mov_haber,mov_deber) "
            ."VALUES(STR_TO_DATE('". $_POST['mov_fecha'] ."', '%d/%m/%Y'),'". $_POST['mov_concepto'] ."',".$_POST['mov_haber'].",".$_POST['mov_deber'].")";
} else {
    $query = "UPDATE mov_movimientos SET "
            ."mov_fecha = STR_TO_DATE('". $_POST['mov_fecha'] ."', '%d/%m/%Y'),"
            ."mov_concepto = '" . $_POST['mov_concepto'] ."',"
            ."mov_haber = " . $_POST['mov_haber'] . ","
            ."mov_deber = " . $_POST['mov_deber'] . " "
            ."WHERE mov_id = " . $_POST['mov_id'];
}

$db->setFetchMode(Zend_Db::FETCH_OBJ);
$result = $db->query($query);
$lastInsert = $db->lastInsertId();

$resultados = array();

$retValue = array (
    'total' => count($result),
    'success'=>true,
    'data' => array (
        array(
            'mov_id' => $mov_id
        )
    )
);

echo json_encode($retValue);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/12/03/extjs-cookbook-codigo-base/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Depurando código con FirePHP y Zend Framework, o como decir adiós a los var_dump()</title>
		<link>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/</link>
		<comments>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 11:02:34 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[FirePHP]]></category>
		<category><![CDATA[depuración]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Firebug]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=46</guid>
		<description><![CDATA[Todos los que llevamos tiempo desarrollando en PHP sabemos que una de las tareas más tediosas es la depuración de nuestros scritps, y mucho más si estos están alimentando la respuesta de una llamada AJAX que tiene que devolver los datos en un cierto formato (JSON por ejemplo). Esta tarea se puede hacer más &#8220;agradable&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Todos los que llevamos tiempo desarrollando en PHP sabemos que una de las tareas más tediosas es la depuración de nuestros scritps, y mucho más si estos están alimentando la respuesta de una llamada AJAX que tiene que devolver los datos en un cierto formato (JSON por ejemplo). Esta tarea se puede hacer más &#8220;agradable&#8221; si disponemos de un IDE con depuración integrada, como puede ser Zend Studio, o recientemente Eclipse con el módulo PDT</p>
<p>A pesar de la comodidad de tener el depurador integrado en el entorno de desarrollo, muchas veces se hace un poco engorroso tener que iniciar una sesión de depuración cada vez que quieres saber qué datos están pasando por tus variables</p>
<p>Pues bien, para facilitar la vida a los desarrolladores tenemos disponible un nuevo complemento para firefox: FirePHP. Este componente nos permite depurar directamente nuestros scripts PHP en la consola del firebug</p>
<p>La herramienta se basa en enviar unas ciertas cabeceras HTTP desde nuestros scripts PHP y que son capturadas por el plugin. De esta forma, la salida de nuestros scripts no se ve alterada (perfecto para llamadas AJAX que devuelvan un XML o un JSON) y ganamos rapidez en la depuración</p>
<p>FirePHP está ganando popularidad rápidamente entre los desarrolladores y de hecho muchos frameworks como Zend Framework o Symphony están incorporando en su API llamadas para depurar con esta herramienta</p>
<p>Si no estamos usando ninguno de estos frameworks, el equipo de desarrollo de FirePHP nos ofrece una pequeña librería en PHP5 que podemos integrar fácilmente en nuestros scripts</p>
<p>Lo primero que tenemos que hacer es instalar la extensión FirePHP en nuestro Firefox. Luego descargamos FirePHPCore, descomprimimos el contenido en el directorio donde estemos almacenando las librerías que usamos y ya sólo nos queda hacer las llamadas pertinentes tal y <a href="http://www.firephp.org/HQ/Use.htm" target="_blank">como se nos explica en este tutorial</a></p>
<p>Si estamos usando Zend Framework y su Modelo Vista Controlador (MVC) también podemos integrar FirePHPCore de forma fácil, si por un casual no nos gustase el API que nos ofrece el propio framework</p>
<p>Primero registramos el logger en el bootstrap.php de nuestra aplicación:</p>
<pre name="code" class="php">//Incluímos la librería
require_once('FirePHPCore/FirePHP.class.php');

// Instanciamos el logger y lo registramos
$firebugLogger = FirePHP::getInstance(true);
Zend_Registry::set('firebugLogger',$firebugLogger);</pre>
<p>Luego en cualquier parte de nuestro código podemos invocar al logger, como por ejemplo en una acción de un controlador:</p>
<pre name="code" class="php">    public function indexAction() {
        // Obtenemos la instancia del logger previamente registrada
        // en el bootstrap.php
        $fbLogger = Zend_Registry::get('firebugLogger');

        // Mensajes de log
        $fbLogger-&gt;log('Mensaje');
        $fbLogger-&gt;info('Mensaje nivel INFO');
        $fbLogger-&gt;warn('Mensaje nivel WARNING');
        $fbLogger-&gt;error('Mensaje niverl ERROR');

        // Mensaje de log con etiqueta adicional
        $fbLogger-&gt;log('Mensaje','Etiqueta Opcional');

        // Podemos mostrar una tabla de datos
        $table   = array();
        $table[] = array('Columna 1','Columna 2');
        $table[] = array('1:1','1:2');
        $table[] = array('2:1','2:2');
        $table[] = array('3:1','3:2');
        $fbLogger-&gt;table('Tabla de datos', $table);  // or FB::

        // También podemos mostrar información de traza
        $fbLogger-&gt;trace('Trace Label');
    }</pre>
<p>Aquí podemos ver cómo queda la salida en la consola del Firebug:</p>
<p style="text-align: center;"><a href="http://www.desarrollosweb.net/wp-content/captura_firephp.png"><img class="size-medium wp-image-47 aligncenter" title="captura_firephp" src="http://www.desarrollosweb.net/wp-content/captura_firephp-300x234.png" alt="" width="300" height="234" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/12/01/depurando-codigo-con-firephp-y-zend-framework-o-como-decir-adios-a-los-var_dump/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducción a formularios en ExtJS: Ext.FormPanel</title>
		<link>http://www.desarrollosweb.net/2008/11/21/introduccion-a-formularios-en-extjs-extformpanel/</link>
		<comments>http://www.desarrollosweb.net/2008/11/21/introduccion-a-formularios-en-extjs-extformpanel/#comments</comments>
		<pubDate>Fri, 21 Nov 2008 11:47:17 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.FormPanel]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=44</guid>
		<description><![CDATA[Definiendo un formulario Realmente la definición de un formulario que haga su carga de datos y guardado mediante Ajax, no dista mucho de la definición de un grid. Lo primero que hacemos es definir el registro que contendrá nuestros datos. Como se puede ver en este ejemplo hemos utilizado la misma definición de registro. var [...]]]></description>
			<content:encoded><![CDATA[<h2>Definiendo un formulario</h2>
<p>Realmente la definición de un formulario que haga su carga de datos y guardado mediante Ajax, no dista mucho de la definición de un grid.</p>
<p>Lo primero que hacemos es definir el registro que contendrá nuestros datos. Como se puede ver en este ejemplo hemos utilizado la misma definición de registro.</p>
<pre name="code" class="javascript">    var categoriesRecord = new Ext.data.Record.create([
        {name: 'cad_id', type: 'int'},
        {name: 'cad_name', type: 'string'},
        {name: 'cad_description', type: 'string'}
    ]);</pre>
<p>Para la carga de datos y respuestas del servidor, definimos también un reader (al igual que en los grids). Para este ejemplo vamos a seguir utilizando la codificación JSON, aunque podríamos haber elegido un reader XML</p>
<pre name="code" class="javascript">    var categoriesFormReader = new Ext.data.JsonReader(
            {
                root : 'data',
                successProperty : 'success',
                totalProperty: 'total',
                id: 'cad_id'
            },categoriesRecord
    );</pre>
<p>Como vemos, su definición es prácticamente igual que en el caso del reader del grid, salvo que en esta ocasión hay que definir una propiedad &#8216;successProperty&#8217; en la respuesta JSON que nos indique si ha tenido éxito o no la operación</p>
<p>Otra novedad respecto al grid, es que podemos indicar a un form desde el servidor si hubo algún error en algún campo. Esto lo podemos utilizar para hacer verificacion de campos en servidor (comprobar duplicados, caracteres no permitidos, etc.). Para ello se define una nueva clase que hereda de JsonReader: JsonErrorReader</p>
<pre name="code" class="javascript">Ext.form.JsonErrorReader = function() {
    Ext.form.JsonErrorReader.superclass.constructor.call(this, {
        root : 'data',
        successProperty : 'success',
        totalProperty: 'total'
     },
     [
        {name: 'id'},
        {name: 'msg'}
     ]);
    };</pre>
<p>Podemos ver que en este caso, en la definición del reader se cambia el registro por otro nuevo con sólo dos campos: &#8216;id&#8217; y &#8216;msg&#8217;. En &#8216;id&#8217; vendrá el identificador del campo en el que hay un error y en &#8216;msg&#8217; vendrá el error para ese campo. Es importante que activemos los mensajes superpuestos mediante la línea Ext.QuickTips.init();</p>
<p>La definición del form es sencilla. Como en cualquier otro panel, en la propiedad &#8216;items&#8217; vamos a introducir el array con todos los componentes del formulario, en este campos (texto, áreas de texto, combos, radios, etc.)</p>
<pre name="code" class="javascript">var formCategories = new Ext.FormPanel({
        frame : true,
        width : 624,
        height: 346,
        waitMsgTarget : true,
        reader: categoriesFormReader,
        errorReader: new Ext.form.JsonErrorReader(),
        items : [{
                fieldLabel : 'Categoría',
                xtype: 'textfield',
                name : 'cad_name',
                allowBlank:false,
                width : 430
            }, {
                fieldLabel : 'Descripción',
                name : 'cad_description',
                allowBlank:false,
                xtype: 'textarea',
                width : 430,
                height: 225
            }, {
                fieldLabel : 'Fecha',
                name : 'cad_date',
                allowBlank:false,
                xtype: 'datefield',
                renderer: Ext.util.Format.dateRenderer('d/m/Y'),
                width : 430
            }, {
                name : 'cad_id',
                xtype: 'hidden'
            }],
    });</pre>
<p>Acto seguido, vamos a añadir un botón. Esta vez en vez de indicarlo en el array de configuración, vamos a utilizar el método addButton y vamos a añadirle un poco de funcionalidad</p>
<pre name="code" class="javascript">    var submitFormCategories = formCategories.addButton({
        text : 'Guardar',
        disabled : false,
        handler : function() {
            formCategories.getForm().submit({
                url : 'formSaver.php',
                waitMsg : 'Salvando datos...',
                failure: function (form, action) {
                    Ext.MessageBox.show({
                        title: 'Error al salvar los datos',
                        msg: 'Error al salvar los datos.',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.ERROR
                    });
                },
                success: function (form, request) {
                    Ext.MessageBox.show({
                        title: 'Datos salvados correctamente',
                        msg: 'Datos salvados correctamente',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.INFO
                    });
                    responseData = Ext.util.JSON.decode(request.response.responseText);
                    formCategories.getForm().load({
                        url : 'formLoader.php',
                        method: 'GET',
                        params: {
                            cat_id: responseData.cat_id
                        },
                        waitMsg : 'Espere por favor'
                    });
                }
            });
        }
    });</pre>
<p>En el handler vemos que realizamos una llamda a la función formCategories.getForm().submit(), a la cual le pasamos un array de configuración. Esta llamada lo que hace es llamar al envío del formulario. Éste se configura pasándole una función para el manejo de error, una función para el manejo de una operación correcta y la URL de actualización.</p>
<p>El código todo unido quedaría de la siguiente forma</p>
<pre name="code" class="javascript">Ext.onReady(function() {
	Ext.QuickTips.init();
	/*
     * Creamos el registro de datos
     */
    var categoriesRecord = new Ext.data.Record.create([
        {name: 'cad_id', type: 'int'},
        {name: 'cad_name', type: 'string'},
        {name: 'cad_date', type: 'date', dateFormat: 'Y-m-d'},
        {name: 'cad_description', type: 'string'}
    ]);

	var categoriesFormReader = new Ext.data.JsonReader(
            {
                root : 'data',
                successProperty : 'success',
                totalProperty: 'total',
                id: 'cad_id'
            },categoriesRecord
    );

	Ext.form.JsonErrorReader = function() {
    Ext.form.JsonErrorReader.superclass.constructor.call(this, {
        root : 'data',
        successProperty : 'success',
        totalProperty: 'total'
     },
     [
        {name: 'id'},
        {name: 'msg'}
     ]);
    };

    Ext.extend(Ext.form.JsonErrorReader, Ext.data.JsonReader);

    var formCategories = new Ext.FormPanel({
        frame : true,
        width : 624,
        height: 346,
        waitMsgTarget : true,
        reader: categoriesFormReader,
        errorReader: new Ext.form.JsonErrorReader(),
        items : [{
                fieldLabel : 'Categoría',
                xtype: 'textfield',
                name : 'cad_name',
                allowBlank:false,
                width : 430
            }, {
                fieldLabel : 'Descripción',
                name : 'cad_description',
                allowBlank:false,
                xtype: 'textarea',
                width : 430,
                height: 225
            }, {
                fieldLabel : 'Fecha',
                name : 'cad_date',
                allowBlank:false,
                xtype: 'datefield',
                renderer: Ext.util.Format.dateRenderer('d/m/Y'),
                width : 430
            }, {
                name : 'cad_id',
                xtype: 'hidden'
            }],
    });

    var submitFormCategories = formCategories.addButton({
        text : 'Guardar',
        disabled : false,
        handler : function() {
            formCategories.getForm().submit({
                url : 'formSaver.php',
                waitMsg : 'Salvando datos...',
                failure: function (form, action) {
                    Ext.MessageBox.show({
                        title: 'Error al salvar los datos',
                        msg: 'Error al salvar los datos.',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.ERROR
                    });
                },
                success: function (form, request) {
                    Ext.MessageBox.show({
                        title: 'Datos salvados correctamente',
                        msg: 'Datos salvados correctamente',
                        buttons: Ext.MessageBox.OK,
                        icon: Ext.MessageBox.INFO
                    });
                    responseData = Ext.util.JSON.decode(request.response.responseText);
                    formCategories.getForm().load({
                        url : 'formLoader.php',
                        method: 'GET',
                        params: {
                            cat_id: responseData.cat_id
                        },
                        waitMsg : 'Espere por favor'
                    });
                }
            });
        }
    });

    var windowForm = new Ext.Window({
        closable: true,
        resizable: true,
        modal: false,
        border: true,
        plain: true,
        closeAction: 'hide',
        title: 'Layout Tab',
        width: 640,
        height: 380,
        renderTo: 'divRender',
        items: [formCategories]
    });

    formCategories.getForm().load({
        url : 'formLoader.php',
        method: 'GET',
        params: {
            cat_id: 1
        },
        waitMsg : 'Espere por favor'
    });
    windowForm.show();
});</pre>
<h2>Introduciendo un combo como campo</h2>
<p>El tratamiento de un combo requiere un poco más de trabajo. En este caso vamos a introducir el combo en el formulario como un objeto independiente.</p>
<pre name="code" class="javascript">    var categoriesComboWebs = new Ext.form.ComboBox({
        fieldLabel: 'Web',
        hiddenName: 'cad_web', // Este campo es importante, sin él no funciona el combo
        store: new Ext.data.SimpleStore({
            fields: ['cad_web', 'cad_web_text'],
            data : websData,
            id: 0
        }),
        valueField: 'cad_web',
        displayField: 'cad_web_text',
        typeAhead: true,
        mode: 'local',
        triggerAction: 'all',
        emptyText: 'Seleccione una web...',
        selectOnFocus: true,
        width: 430
    });</pre>
<p>En este caso hemos hecho que el combo sea local, y que los datos los saque de un Array. Para ello, hemos definido el store del mismo como un Ext.data.SimpleStore. En la propiedad data, hemos indicado cual es la variable javascript que contiene los valores a utilizar, y debe contener valores de la siguiente forma:</p>
<pre name="code" class="javascript">var websData = [
        ['1','http://www.3megapixels.com'],
        ['2','http://www.desarrollosweb.net'],
        ['3','http://www.axena.org'],
        ['4','http://www.javiercaride.es']
    ];</pre>
<p>Una propiedad importante, para que cuando carguemos los datos se seleccione bien el elemento del combo correspondiente y que además se envíe al servidor la variable correcta, es &#8216;hiddenName&#8217;. Debe estar inicializada al valor del registro que contiene la información, en este caso cad_web</p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/21/introduccion-a-formularios-en-extjs-extformpanel/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Introducción a los Grids en ExtJS</title>
		<link>http://www.desarrollosweb.net/2008/11/18/introduccion-a-los-grids-en-extjs/</link>
		<comments>http://www.desarrollosweb.net/2008/11/18/introduccion-a-los-grids-en-extjs/#comments</comments>
		<pubDate>Tue, 18 Nov 2008 12:27:05 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.EditorGridPanel]]></category>
		<category><![CDATA[Ext.GridPanel]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=39</guid>
		<description><![CDATA[En ExtJS tenemos básicamente dos tipos de grids, los que únicamente son para listar información (los llamaremos Grids básicos) y los grids editables, que permiten editar la información que se lista dentro de ellos. Grid básico En los grids tenemos que distinguir 3 partes importantes: El store de datos El modelo de datos de las [...]]]></description>
			<content:encoded><![CDATA[<p>En ExtJS tenemos básicamente dos tipos de grids, los que únicamente son para listar información (los llamaremos Grids básicos) y los grids editables, que permiten editar la información que se lista dentro de ellos.</p>
<h2>Grid básico</h2>
<p>En los grids tenemos que distinguir 3 partes importantes:</p>
<ul>
<li>El store de datos</li>
<li>El modelo de datos de las columnas</li>
<li>La definición del grid en sí</li>
</ul>
<p><strong>El store de datos</strong>, es el objeto que se encarga de almacenar los datos que se van a mostrar en el grid. Básicamete se tratan de arrays de datos, pero su particularidad es que pueden leer los datos desde diversas fuentes:</p>
<ul>
<li>Estáticas: ficheros XML o variables de tipo array</li>
<li>Dinámicas: servicios de datos XML o JSON</li>
</ul>
<p>En la definición de un store tenemos que tener en cuenta dos aspectos importantes: el formato de los datos, que nos va a indicar qué tipo de reader necesitamos configurar y si el origen de dato es remoto, configurar el proxy de lectura de datos</p>
<p><strong>El modelo de datos de las columnas</strong> nos define qué datos se van a mostrar en el grid, con qué formato, y de donde se extraen los datos. Hay que tener en cuenta que aunque en un registro se lean muchos campos, podemos elegir cuales mostrar y cuales no.</p>
<h3>Pasos para configurar un grid</h3>
<p>Primero definimos el formato de cada registro, aquí configuramos cuantos campos hay y de que tipo</p>
<pre name="code" class="javascript">    /*
     * Creamos el registro de datos
     */
    var categoriesRecord = new Ext.data.Record.create([
        {name: 'cad_id', type: 'int'},
        {name: 'cad_name', type: 'string'},
        {name: 'cad_date', type: 'date', dateFormat: 'Y-m-d'},
        {name: 'cad_description', type: 'string'}
    ]);</pre>
<p>Después, configuramos el lector de datos. Como se dijo antes, el lector de datos será diferente dependiendo del formato de los datos que obtengamos. En este caso estamos definiendo que los datos nos vendrán codificados en JSON</p>
<pre name="code" class="javascript">    /*
     * Creamos el reader de datos
     */
    var categoriesGridReader = new Ext.data.JsonReader({
        root: 'data',
        totalProperty: 'total',
        id: 'cad_id'},
        categoriesRecord
    );</pre>
<p>La configuración del reader es fácil de entender:</p>
<ul>
<li>Mediante el campo root, indicamos la etiqueta bajo la que llegarán los datos, en este caso &#8216;data&#8217;</li>
<li>El campo totalProperty nos indica en qué etiqueta nos llega el número total de registros</li>
<li>El campo id, nos indica en qué campo nos llega la identificación del registro</li>
</ul>
<p>Una vez tenemos configurado el registro y el reader, definimos el proxy de datos (para una carga remota de los mismos).</p>
<pre name="code" class="javascript">    /*
     * Creamos el proxy para lectura remota de datos
     */
    var categoriesDataProxy = new Ext.data.HttpProxy({
        url: 'scripts/categoriesService.php',   // Servicio web
        method: 'POST'                          // Método de envío
    });</pre>
<p>En este caso, vemos que la carga la hacemos a través de un script php.El cual nos debe devolver un JSON como el siguiente:</p>
<pre name="code" class="javascript">{
    "total":3,
    "data":[
        {
            "cad_id":1,
            "cad_name":"Categoría 1",
            "cad_description":"Descripción de la primera categoría",
            "cad_date": "2008-10-01"
        },{
            "cad_id":2,
            "cad_name":"Categoría 2",
            "cad_description":"Descripción de la segunda categoría",
            "cad_date": "2008-10-11"
        },{
            "cad_id":3,
            "cad_name":"Categoría 3",
            "cad_description":"Descripción de la tercera categoría",
            "cad_date": "2008-10-20"
        },{
            "cad_id":4,
            "cad_name":"Categoría 4",
            "cad_description":"Descripción de la cuarta categoría",
            "cad_date": "2008-10-21"
        },
    ]
}</pre>
<p>Para ello, el php de prueba tiene este contenido</p>
<pre name="code" class="php">$retValue = array(
    'total' =&gt; 4,
    'data' =&gt; array(
        array(
            'cad_id' =&gt; 1,
            'cad_name' =&gt; 'Categoría 1',
            'cad_description' =&gt; 'Descripción de la primera categoría',
            'cad_date' =&gt; '2008-10-01'
        ),
        array(
            'cad_id' =&gt; 2,
            'cad_name' =&gt; 'Categoría 2',
            'cad_description' =&gt; 'Descripción de la segunda categoría',
            'cad_date' =&gt; '2008-10-11'
        ),
        array(
            'cad_id' =&gt; 3,
            'cad_name' =&gt; 'Categoría 3',
            'cad_description' =&gt; 'Descripción de la tercera categoría',
            'cad_date' =&gt; '2008-10-20'
        ),
        array(
            'cad_id' =&gt; 4,
            'cad_name' =&gt; 'Categoría 4',
            'cad_description' =&gt; 'Descripción de la cuarta categoría',
            'cad_date' =&gt; '2008-10-21'
        )
    )
);
echo json_encode($retValue);</pre>
<p>En el caso de scripts php, primero hay que crear en un array la estructura que queramos devolver y después codificar mediante al función json_encode (PHP5), o bien (en PHP4) utilizar la librería de codificación JSON que provee PEAR</p>
<p>Hay que tener en cuenta una cosa JSON, trabaja únicamente en UTF-8, por lo que si los datos los extraemos por ejemplo de una base de datos configurada en ISO-8859-1, <strong>HAY QUE CONVERTIRLOS A UTF-8</strong>, obligatoriamente.</p>
<p>Ahora ya tenemos todos los elementos necesarios para configurar el store del grid:</p>
<pre name="code" class="javascript">    /*
     * Creamos el datastore donde se van a almacenar los datos de la tabla
     */
    var categoriesDataStore = new Ext.data.Store({
        id: 'categoriesDS',
        //Indicamos de donde se va a leer los datos, en este caso un servicio web
        proxy: categoriesDataProxy,
        // Parámetros base que se enviarán al script
        baseParams: {
            language: "es_ES"
        },
        // Indicamos el reader, es decir el procesador de los datos
        reader: categoriesGridReader
    });</pre>
<p>Ya por último, antes de definir el grid en sí, definimos cuales van a ser nuestras columnas, su tipo, ancho, etc.</p>
<pre name="code" class="javascript">    var categoriesColumnMode = new Ext.grid.ColumnModel(
        [{
            header: '#',
            dataIndex: 'cad_id',
            width: 50,
            hidden: false
        },{
            header: 'Categoría',
            dataIndex: 'cad_name',
            width: 150
        },{
            header: 'Descripción',
            dataIndex: 'cad_description',
            width: 300
        },{
            header: 'Fecha',
            dataIndex: 'cad_date',
            width: 90,
            renderer: Ext.util.Format.dateRenderer('d/m/Y')
        }]
    );</pre>
<p>Ahora ya tenemos todos los elementos necesarios para configurar el grid:</p>
<pre name="code" class="javascript">    var categoriesGrid = new Ext.grid.GridPanel({
        id: 'cat_categoriesGrid',
        store: categoriesDataStore,
        cm: categoriesColumnMode,
        enableColLock:false,
        selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
    });</pre>
<p>Sólo nos falta poner todo dentro de un bloque Ext.onReady y definir una ventana para poder visualizar el grid.</p>
<h2>Grid editable</h2>
<p>Además de los grids básicos, ExtJS permite definir grids en los que se puedan editar valores. Para convertir un grid no editable a uno editable, tenemos que hacer cambios en dos lugares: en el ColumnModel y en la definición del grid En el Column mode, hay que incluir una etiqueta &#8216;editor&#8217; en aquellas columnas que queramos hacer editables. En esta etiqueta los que se pone es la definición de un campo editable cuyo objeto puede ser cualquiera de los que hay disponibles para los formularios (cajas de texto, combos, fechas, tiempo&#8230;)</p>
<pre name="code" class="javascript">    var categoriesColumnMode = new Ext.grid.ColumnModel(
        [{
            header: '#',
            dataIndex: 'cad_id',
            width: 50,
            hidden: false
        },{
            header: 'Categoría',
            dataIndex: 'cad_name',
            width: 150,
            editor: new Ext.form.TextField({})

        },{
            header: 'Descripción',
            dataIndex: 'cad_description',
            width: 300,
            editor: new Ext.form.TextField({})
        },{
            header: 'Fecha',
            dataIndex: 'cad_date',
            width: 90,
            renderer: Ext.util.Format.dateRenderer('d/m/Y'),
            editor: new Ext.form.DateField({})
        }]
    );</pre>
<p>La modificación del grid es &#8220;más fácil&#8221;. En este caso sólo hay que heredar de otra clase distinta: EditorGridPanel</p>
<pre name="code" class="javascript">    var categoriesGrid = new Ext.grid.EditorGridPanel({
        id: 'cat_categoriesGrid',
        store: categoriesDataStore,
        cm: categoriesColumnMode,
        enableColLock:false,
        selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
    });</pre>
<p>Al ejecutar el ejemplo, podemos ver que al hacer doble click en cualquiera de los campos editables, se nos permite cambiar el valor del campo y que dicho cambio queda marcado en la esquina superior izquierda con un pequeño icono rojo</p>
<h2>Tratando los cambios en un grid editable</h2>
<p>De nada nos sirve tener un grid editable si no tratamos los datos, pues todos los cambios que se hagan en un grid editable son cambios en el store local.</p>
<p>ExtJS no ofrece ningún mecanismo para automatizar el guardado de datos, dejando a elección del programador la elección del método. Por ejemplo, podemos hacer actualizaciones campo a campo, capturando el evento de cambio de una celda, o bien podemos enviar todos los cambios de una vez mediante la acción en algún botón o componente</p>
<p>Qué método utilizar queda a elección del desarrollador según las circunstancias en las que se  encuentre. En este caso vamos a explicar cómo se haría un envío de todos los cambios. Para ello vamos a coger el ejemplo anterior y primeramente añadir un botón de actualización de cambio situado en una barra de herramientas superior</p>
<pre name="code" class="javascript">    var categoriesGridTopbar = new Ext.Toolbar({
        id: 'categoriesGridTopbarId',
        items: [
            '-&gt;',
            {
                xtype:'button',
                text:'Guardar Cambios',
                id: 'portadas_ToolbarTop_btn0',
                handler:categoriesGridTopbarHnd,
                type:'button'
            }
        ]
    });</pre>
<p>Como se puede ver, se a asignado un handler al botón, que será el que tenderá la petición de guardado de cambios y que contendrá toda la lógica de actualización</p>
<p>En el handler, lo que hemos hecho es crear una llamada Ajax que envíe al servidor todos los cambios, para ello se pide al store un array con todos los registros que han cambiado, se recorre y se  almacena en un array los datos necesarios para enviar. Este array se codifica en JSON y se envía como una variable POST normal. Después es tarea del script php (o Java, o lo que sea) de tratar el array de cambios y realizar las actualizaciones pertinentes.</p>
<pre name="code" class="javascript">    var failureAjaxFn = function(response, request) {
        Ext.Msg.show({
            title: 'Error',
            msg: 'Cambios no guardados. Status:' + response.status,
            buttons: Ext.Msg.OK,
            animEl: 'elId',
            icon: Ext.MessageBox.ERROR
        });
    }

    var successAjaxFn = function(response, request) {
        /*
         * En response.responseText tenemos la respuesta del script, en este caso
         * la salida del script PHP es JSON con lo que tenemos que decodificarlo
         */
        var jsonData = Ext.util.JSON.decode(response.responseText);

        if (true == jsonData.success) {
            Ext.Msg.show({
                title: 'Operación completada',
                msg: jsonData.message,
                buttons: Ext.Msg.OK,
                animEl: 'elId',
                icon: Ext.MessageBox.INFO
            });

            /*
             * PASO IMPORTANTE
             * Para indicar al store que los cambios se han realizado correctamente
             * y eliminar los indicadores de que hay cambios pendientes de guardar
             * hay que llamar
             */
            categoriesDataStore.commitChanges();

        } else {
            Ext.Msg.show({
                title: 'Error',
                msg: jsonData.message,
                buttons: Ext.Msg.OK,
                animEl: 'elId',
                icon: Ext.MessageBox.ERROR
            });
        }
    }
var categoriesGridTopbarHnd = function (e,o) {
    	/*
    	 * Pedimos al store que nos devuelva los registros modificados
    	 */
        var modifiedRecords = categoriesDataStore.getModifiedRecords()

        if (modifiedRecords.length > 0) {
        	/*
             * Creamos un array con los datos de los registros que han cambiado
             */
        	var changes = new Array();
            for (var i=0; i &lt; modifiedRecords.length; i++){
                changes.push(modifiedRecords[i].data);
            }

            /*
             * Codificamos los cambios en JSON
             */
            changes = Ext.util.JSON.encode(changes);

            Ext.Ajax.request({
                url: 'salvarDatos.php',
                method: 'POST',
                success: successAjaxFn,
                failure: failureAjaxFn,
                timeout: 30000,
                params: {
                    operation: 'update',
                    changes: changes
                }
            });
        }
    }
</pre>
<h2>Grids paginados</h2>
<p>Uno de las barras de herramientas más utilizadas con los grids es la de paginación. Su utilización es<br />
bastante sencilla. Primero creamos la barra de paginación:</p>
<pre name="code" class="javascript">    var categoriesPagingBar = new Ext.PagingToolbar({
        pageSize: 10,
        store: categoriesDataStore,
        displayInfo: true
    });</pre>
<p>Su uso es sencillo, sólo tenemos que indicar cual es el store, y el tamaño de página. Después de definirlo<br />
se lo asociamos al grid, para lo que sólo tenemos que indicar en la configuración<br />
lo siguiente &#8220;bbar: categoriesPagingBar&#8221;:</p>
<pre name="code" class="javascript">    var categoriesGrid = new Ext.grid.EditorGridPanel({
        id: 'cat_categoriesGrid',
        store: categoriesDataStore,
        cm: categoriesColumnMode,
        enableColLock:false,
        tbar: categoriesGridTopbar,
        bbar: categoriesPagingBar,
        selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
    });</pre>
<p>Después en la carga inicial de datos, indicamos que queremos cargar la primera página de resultados<br />
y el número de ellos</p>
<pre name="code" class="javascript">categoriesDataStore.load({params: {start: 0, limit: 10}});</pre>
<p>Y después de esto ya no nos tenemos que preocupar de nada más, pues cada vez que hagamos click en los botones el store nos enviará dos variables nuevas: start y limit, al estilo de las paginaciones clásicas</p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/18/introduccion-a-los-grids-en-extjs/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>Contenedores en ExtJS (III): Ext.Tabpanel y Ext.Window</title>
		<link>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-exttabpanel-y-extwindow/</link>
		<comments>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-exttabpanel-y-extwindow/#comments</comments>
		<pubDate>Fri, 14 Nov 2008 07:58:56 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[contenedores extjs]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.Tabpanel]]></category>
		<category><![CDATA[Ext.Window]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=36</guid>
		<description><![CDATA[Pestañas (Ext.TabPanel) Como la mayoría de los contenedores, Ext.Tabpanel deriva de Ext.Panel, con lo que hereda todas sus funcionalidades básicas. Realmente se comporta como un panel en el que el contenido se puede dividir en varios tabs, donde cada uno de ellos es un panel con su correspondiente layout Ventanas También Ext.Window deriva de Ext.Panel, [...]]]></description>
			<content:encoded><![CDATA[<h2>Pestañas (Ext.TabPanel)</h2>
<p>Como la mayoría de los contenedores, Ext.Tabpanel deriva de Ext.Panel, con lo que hereda todas sus funcionalidades básicas. Realmente se comporta como un panel en el que el contenido se puede dividir en varios tabs, donde cada uno de ellos es un panel con su correspondiente layout</p>
<h2>Ventanas</h2>
<p>También Ext.Window deriva de Ext.Panel, y como cualquier contenedor puede contener otros elementos de ExtJS: sólo tienen que incluirse en el bloque &#8216;items&#8217;. En el ejemplo vemos cómo incluir dentro de una ventana un Ext.Tabpanel.</p>
<p>Además aprovechamos para ver que en el bloque items no es obligatorio definir los componentes que queremos introducir, sino que se pueden incluir los nombres de los objetos definidos aparte. De esta forma, cuando tenemos muchos componentes encadenados se mejora la legibilidad del código, aunque, hay que tener cuidado de que los objetos que añadimos estén definidos en el ámbito (scope) de nuestro objeto.</p>
<pre name="code" class="javascript">
Ext.onReady(function () {
	var backendViewport = new Ext.Panel({
        layout: 'fit',
        width: 625,
        height: 448,
        border: false,
        items: [{
            xtype: 'tabpanel', // En vez de instanciar directamente el objeto, utilizamos la notación del xtype
            height: 450,
            bodyStyle: 'padding:10px',
            activeTab: 0,
            enableTabScroll:true,
            plain: true,
            items: [{
                xtype: 'panel',
                title: 'Tab #1',
                layout: 'fit',
                items: [{
                    /*
                    * Panel con layout 'column'
                    */
                    xtype: 'panel',
                    layout:'column',
                    items: [{
                        title: 'Col 1',
                        columnWidth: .333
                    },{
                        title: 'Col 2',
                        columnWidth: .333
                    },{
                        title: 'Col 3',
                        columnWidth: .333
                    }]
                }]
            },{
                xtype: 'panel',
                title: 'Tab #2',
                layout: 'form',
                width: 300,
                minWidth: 300,
                autoScroll: true,
                split: true,
                items: [{
                        xtype: 'hidden',
                        id: 'gal_id_hidden',
                        name: 'gal_id'
                    },{
                        xtype: 'textfield',
                        name: 'gal_cat_id',
                        anchor: '90%',
                        fieldLabel: 'Categoría'
                    },{
                        xtype: 'textfield',
                        name: 'gal_weight',
                        anchor: '90%',
                        fieldLabel: 'Peso Galería'
                    },{
                        xtype: 'datefield',
                        name: 'gal_date',
                        anchor: '90%',
                        fieldLabel: 'Fecha Alta'
                    },{
                        xtype: 'textfield',
                        name: 'gal_relative_path',
                        anchor: '90%',
                        fieldLabel: 'Directorio'
                    },{
                        xtype: 'textfield',
                        name: 'gal_pho_id',
                        anchor: '90%',
                        fieldLabel: 'Imágen representativa'
                    }
                ]
            },
            {
                xtype: 'panel',
                title: 'Tab #3',
                layout: 'accordion',
                layoutConfig: {
                    titleCollapse: false,
                    animate: false,
                    activeOnTop: false
                },
                items: [{
                    title: 'Subpanel 1',
                    html: 'Contenido subpanel 1'
                },{
                    title: 'Subpanel 2',
                    html: 'Contenido subpanel 2'
                },{
                    title: 'Subpanel 3',
                    html: 'Contenido subpanel 3'
                }]
            }]
        }
        ]
    });

	var windowExample = new Ext.Window({
	    closable: true,
	    resizable: true,
	    modal: false,
	    border: true,
	    plain: true,
	    closeAction: 'hide',
	    title: 'Ventana con paneles',
	    width: 640,
	    height: 480,
	    renderTo: 'divRender',
	    /*
	     * Aprovechamos para ver otro tipo de asignación de items,
	     * en vez de definirlos en el bloque 'items' lo asignamos
	     * desde una variable
	     */
	    items: [backendViewport]
	});

	windowExample.show();
});
</pre>
<p>Podéis <a href="http://appsrv01.desarrollosweb.net/blog/extjs/ext_tabpanel_window.html" target="_blank">ver el ejemplo en el siguiente enlace</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-exttabpanel-y-extwindow/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Contenedores en ExtJS (II): Ext.Viewport</title>
		<link>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-extviewport/</link>
		<comments>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-extviewport/#comments</comments>
		<pubDate>Fri, 14 Nov 2008 07:41:07 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[contenedores extjs]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.Viewport]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=34</guid>
		<description><![CDATA[A efectos prácticos un Ext.Viewport se comporta igual que un Ext.Panel, pero realmente es un contenedor especial. Su peculiaridad es que se renderiza automáticamente en document.body, y que sólo puede existir uno por aplicación. Además su tamaño siempre es igual al de la ventana del navegador, autoajustándose automáticamente a los cambios del tamaño de esta. [...]]]></description>
			<content:encoded><![CDATA[<p>A efectos prácticos un Ext.Viewport se comporta igual que un Ext.Panel, pero realmente es un contenedor especial. Su peculiaridad es que se renderiza automáticamente en document.body, y que sólo puede existir uno por aplicación. Además su tamaño siempre es igual al de la ventana del navegador, autoajustándose automáticamente a los cambios del tamaño de esta.</p>
<pre name="code" class="javascript">
Ext.onReady(function() {
    var backendViewport = new Ext.Viewport({
        layout: 'fit',
        items: [{
            /*
             * Panel tipo accordion
             */
            region: 'center',
            xtype: 'panel',
            layout: 'accordion',
            defaults: {
                bodyStyle: 'padding:15px'
            },
            layoutConfig: {
                titleCollapse: false,
                animate: false,
                activeOnTop: false
            },
            items: [{
                title: 'Subpanel 1',
                html: 'Contenido subpanel 1'
            },{
                title: 'Subpanel 2',
                html: 'Contenido subpanel 2'
            },{
                title: 'Subpanel 3',
                html: 'Contenido subpanel 3'
            }]
        }]
    });
});
</pre>
<p>En el viewport también tenemos disponibles los mismos layouts que para Ext.Panel, con lo que podemos generar aplicaciones a pantalla completa igual de complejas que con un Ext.Panel, por ejemplo aplicándole el layout &#8216;border&#8217;.</p>
<p>En el <a href="http://appsrv01.desarrollosweb.net/blog/extjs/ext_viewport.html" target="_blank">siguiente ejemplo</a>, podéis ver la misma distribución en pantalla que en el ejemplo anterior, con la salvedad de que hemos instanciado un Ext.Viewport en lugar de un Ext.Panel</p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/14/contenedores-en-extjs-extviewport/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Contenedores en ExtJS (I): Ext.Panel</title>
		<link>http://www.desarrollosweb.net/2008/11/13/contenedores-en-extjs-extpanel/</link>
		<comments>http://www.desarrollosweb.net/2008/11/13/contenedores-en-extjs-extpanel/#comments</comments>
		<pubDate>Thu, 13 Nov 2008 14:18:25 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[Ext.Panel]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=32</guid>
		<description><![CDATA[Este es uno de los contenedore más básicos que podemos utilizar en ExtJS y del que derivan otros muchos componentes(TabPanel, Tip, Window, FieldSet, FormPanel, GridPanel, TreePanel). Básicamente, un Ext.Panel es una región rectangular en la que podemos insertar otros componentes. Además de eso un panel puede contener una barra de herramientas superior, otra inferior, botones, [...]]]></description>
			<content:encoded><![CDATA[<p>Este es uno de los contenedore más básicos que podemos utilizar en ExtJS y del que derivan otros muchos componentes(TabPanel, Tip, Window, FieldSet, FormPanel, GridPanel, TreePanel).</p>
<p>Básicamente, un Ext.Panel es una región rectangular en la que podemos insertar otros componentes. Además de eso un panel puede contener una barra de herramientas superior, otra inferior, botones, etc.  además de ciertos métodos que ayudan a su manejo. Como siempre, la mejor forma de ver todas las propiedades y métodos disponibles es consultar el <a href="http://www.extjs.com/deploy/dev/docs/" target="_blank">API de ExtJS</a></p>
<p>Los contenedores Ext.Panel, admiten diversos &#8216;layouts&#8217;, es decir, configuraciones visuales para incluir los distintos componentes en su interior. Aunque existe una gran variedad de layouts (ver API de ExtJS, sección Ext.Layout), los más utilizados en aplicaciones son los siguientes:</p>
<ul>
<li><b>Layout &#8216;border&#8217;:</b> divide el panel en 5 subpaneles (norte, sur, este, oeste y central)</li>
<li><b>Layout &#8216;accordion&#8217;:</b> divide el panel verticalmente en distintos paneles, pero sólo uno puede ser mostrado a la vez</li>
<li><b>Layout &#8216;form&#8217;:</b> configuración pensada especialmente para alojar formularios</li>
<li><b>Layout &#8216;fit&#8217;:</b> mediante esta configuración todos los elementos que contenga el panel se ajustarán en ancho y alto para ocupar el tamaño del panel</li>
<li><b>Layout &#8216;column&#8217;:</b> permite distribuir el/los contenidos en columnas</li>
</ul>
<p> En el siguiente ejemplo podemos ver una composición de Ext.Panel relativamente compleja. En primer lugar tenemos un Ext.Panel con un layout &#8216;border&#8217;, que es el que contiene a todos los demás</p>
<ul>
<li>En la región norte (region: &#8216;north&#8217;) tenemos únicamente un contenido html decorado por CSS</li>
<li>En la región central (region: &#8216;center&#8217;) hemos colocado un Ext.Panel con layout &#8216;accordion&#8217;, conteniendo a su vez 3 subpaneles</li>
<li>En la región izquierda (region: &#8216;west&#8217;) tenemos un formulario, o lo que es lo mismo un Ext.Panel con layout &#8216;form&#8217;</li>
<li>Por último en la región derecha (region: &#8216;east&#8217;) tenemos un panel con layout column</li>
</li>
<p>Como podéis ver en el <a href="http://appsrv01.desarrollosweb.net/blogs/extjs/ext_panel.html" target="_blank">ejemplo</a> componer la aplicación con Ext.Panel no es más que configurarlos de forma que se adapten a lo que buscamos visualmente</p>
<pre name="code" class="javascript">
Ext.onReady(function() {
	/**
	 * Panel principal, con layout border
	 */
    var backendViewport = new Ext.Panel({
    	layout: 'border',
        renderTo: 'divRender',
        width: 800,
        height: 600,
        items: [{
            region: 'north',
            xtype: 'panel',
            autoHeight: true,
            border: false,
            margins: '0 0 5 0',
            html: '
<div class="x-panel-header">Test</div>

'
        },{
        	/*
        	 * Panel tipo accordion
        	 */
            region: 'center',
            xtype: 'panel',
            layout: 'accordion',
            defaults: {
                bodyStyle: 'padding:15px'
            },
            layoutConfig: {
                titleCollapse: false,
                animate: false,
                activeOnTop: false
            },
            items: [{
                title: 'Subpanel 1',
                html: 'Contenido subpanel 1'
            },{
                title: 'Subpanel 2',
                html: 'Contenido subpanel 2'
            },{
                title: 'Subpanel 3',
                html: 'Contenido subpanel 3'
            }]
        },{
        	/*
        	 * Panel con layout 'form'
        	 */
            region: 'west',
            collapsible: true,
            title: 'Panel izquierdo',
            xtype: 'panel',
            layout: 'form',
            width: 300,
            minWidth: 300,
            autoScroll: true,
            split: true,
            items: [{
                    xtype: 'hidden',
                    id: 'gal_id_hidden',
                    name: 'gal_id'
                },{
                    xtype: 'textfield',
                    name: 'gal_cat_id',
                    anchor: '90%',
                    fieldLabel: 'Categoría'
                },{
                    xtype: 'textfield',
                    name: 'gal_weight',
                    anchor: '90%',
                    fieldLabel: 'Peso Galería'
                },{
                    xtype: 'datefield',
                    name: 'gal_date',
                    anchor: '90%',
                    fieldLabel: 'Fecha Alta'
                },{
                    xtype: 'textfield',
                    name: 'gal_relative_path',
                    anchor: '90%',
                    fieldLabel: 'Directorio'
                },{
                    xtype: 'textfield',
                    name: 'gal_pho_id',
                    anchor: '90%',
                    fieldLabel: 'Imágen representativa'
                }
            ],
            buttons:[{
                text: 'Enviar'},
            {
                text: 'Cancelar'
            }]
        },{
        	/*
             * Panel con layout 'fit'
             */
            region: 'east',
            collapsible: true,
            title: 'Panel derecho',
            xtype: 'panel',
            layout: 'fit',
            width: 250,
            items: [{
            	/*
                * Panel con layout 'column'
                */
            	xtype: 'panel',
                layout:'column',
                items: [{
                    title: 'Col 1',
                    columnWidth: .333
                },{
                    title: 'Col 2',
                    columnWidth: .333
                },{
                    title: 'Col 3',
                    columnWidth: .333
                }]
            }]
        }]
    });
});
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/13/contenedores-en-extjs-extpanel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introduciendo ExtJS (IV): llamadas Ajax</title>
		<link>http://www.desarrollosweb.net/2008/11/13/introduciendo-extjs-llamadas-ajax/</link>
		<comments>http://www.desarrollosweb.net/2008/11/13/introduciendo-extjs-llamadas-ajax/#comments</comments>
		<pubDate>Thu, 13 Nov 2008 07:48:55 +0000</pubDate>
		<dc:creator>Javier Caride</dc:creator>
				<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[llamadas Ajax]]></category>

		<guid isPermaLink="false">http://www.desarrollosweb.net/?p=26</guid>
		<description><![CDATA[ExtJS facilita una colección de objetos para manejar las llamadas Ajax. Su utilización es bastante sencilla y a todos aquellos desarrolladores que hayan utilizado jQuery, les sonará la sintaxis. En el ejemplo podemos ver que a la llamada ajax se le pueden asignar dos funciones, una para cuando la llamada Ajax se ha ejecutado con [...]]]></description>
			<content:encoded><![CDATA[<p>ExtJS facilita una colección de objetos para manejar las llamadas Ajax. Su utilización es bastante sencilla y a todos aquellos desarrolladores que hayan utilizado jQuery, les sonará la sintaxis.</p>
<p>En el ejemplo podemos ver que a la llamada ajax se le pueden asignar dos funciones, una para cuando la llamada Ajax se ha ejecutado con éxito, y otra para cuando hubo algún error en la llamada.</p>
<p>Sobre los errores hay que realizar una pequeña aclaración. Una cosa son los errores de la llamada (un timeout en la llamada, un error 404, un error 500 en el servidor, etc.) de los errores que la lógica de nuestro programa quiera comunicar a la aplicación ExtJS.</p>
<p>Los primeros entrarán a traves de la función asignada a la propiedad failure, mientras que las llamadas que se han realizado correctamente (ya nos quieran comunicar un error o un éxito) a través de la función asignada al &#8216;success&#8217;, pues la llamada ha tenido éxito.</p>
<p>Lo mejor para entender como funcionan las llamadas Ajax en ExtJS es ver el siguiente ejemplo comentado:</p>
<pre name="code" class="javascript">
Ext.onReady(function() {
    /*
     * Funcion que se ejecuta cuando hay un error al realizar la llamada Ajax
     */
    var failureAjaxFn = function(response, request) {
    	// Nos quedamos con el divEjemplo
        var objDiv = Ext.get('divEjemplo');

        // Construimos el mensaje de error con datos de la petición y la respuesta
        var errMessage = '<b>Error en la petición</b> ' + request.url + ' '
                       + ' <b>status</b> ' + response.status + ''
                       + ' <b>statusText</b> ' + response.statusText + ''
                       + ' <b>responseText</b> ' + response.responseText + '';

        // Modificamos el contenido del div
        objDiv.update(errMessage);
        objDiv.replaceClass('backgroundYellow','backgroundRed');
    }

    /*
     * Funcion que se ejecuta cuando la llamada Ajax se realiza correctamente
     */
    var successAjaxFn = function(response, request) {
    	/*
    	 * En response.responseText tenemos la respuesta del script, en este caso
    	 * la salida del script PHP es JSON con lo que tenemos que decodificarlo
    	 */
        var jsonData = Ext.util.JSON.decode(response.responseText);

        // Nos quedamos con el divEjemplo
        var objDiv = Ext.get('divEjemplo');

        /*
         * Al decodificar el JSON, tenemos un objeto javascript. En función del
         * valor de la variable booleana 'success' (que nos indica si la acción
         * que tenía que realizar el script ha tenido éxito o no) realizamos
         * una u otra operación.
         */

        if (true == jsonData.success) {
            objDiv.update(jsonData.data.message + ' desde ' + request.url);
            objDiv.replaceClass('backgroundYellow',jsonData.data.cssClass);
        } else {
            Ext.get('divEjemplo').update(jsonData.data.message + ' desde ' + request.url);
            objDiv.replaceClass('backgroundYellow',jsonData.data.cssClass);
        }
    }

    /*
     * Handler del evento click realizado sobre el div
     */
    var eventHandlerClick = function(event) {
        // Mostramos mensaje de cargando
        var objDiv = Ext.get('divEjemplo');
        if (objDiv.hasClass('backgroundBlue'))
            objDiv.replaceClass('backgroundBlue','backgroundYellow');
        else if (objDiv.hasClass('backgroundRed'))
            objDiv.replaceClass('backgroundRed','backgroundYellow');
        else
            objDiv.replaceClass('backgroundGreen','backgroundYellow');
        Ext.get('divEjemplo').update('Realizando llamada Ajax... por favor espere');

        // Petición básica
        Ext.Ajax.request({
            url: 'loadDataAjax.php',
            method: 'POST',
            success: successAjaxFn,
            failure: failureAjaxFn,
            timeout: 10000,
            headers: {
                'cabecera-propia': 'prueba'
            },
            params: {
                operation: 'load'
            }
        });
    };

    /*
     * Asignación del manejador de evento al DIV
     */
    Ext.get('divEjemplo').on('click',eventHandlerClick);
});
</pre>
<p>El script PHP &#8216;loadDataAjax.php&#8217;, no hace gran cosa. Para que podáis ver los distintos estados de la aplicación con ExtJS, genera aleatoriamente respuestas con la propiedad &#8216;success&#8217; a true, a false y fuerza el vencimiento del timeout de la llamada Ajax. Podéis verlo en funcionamiento <a href="http://appsrv01.desarrollosweb.net/blog/extjs/ext_ajax.html" target="_blank">aquí</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.desarrollosweb.net/2008/11/13/introduciendo-extjs-llamadas-ajax/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
