Archive for the ‘EXTJS’ category

extjs: remote linked comboboxes in a grid

January 24th, 2010

Dieser Part ist doch recht tricky. nach 2 Tagen nerviger Sucherrei und unzähligen Tuts habe ich eine einfache Möglichkeit gefunden mehrerer Comboboxen zu verlinken.

Wenn die Grid geladen ist sollten alle Abhängigen Comboxen mit den Values der Parentgridcell gefüllt sein.

Dazu braucht man

1) eine Combobox die die die Daten Remote von Server holt. Und dem Server sagt welche Daten. Dazu wird das “bevorequery” im Listener des JsonStores verwendet.

bevorquery wird ausgeführt bevor der Request des Stores auf den Server ausgeführt wird. Der Trick hier : Wir führen den request mit den Parametern aus der Abhängigen Box schon hier aus und kehren mit return false zurück. Damit wird der initialload nicht ausgeführt.

new Ext.form.ComboBox({
    id              :'survey_orga_name',
    typeAhead       :true,
    triggerAction   :'all',
    valueField      :'orgaID',
    editable        :true,
    displayField    :'orga_name',
    selectOnFocus   :true,
    typeAhead       :'true',
    hiddenName      :'orgaID',
    mode            :'remote',
    lazyRender      : true,
    store:
    this.combostore_survey_orga_name=new Ext.data.JsonStore({
        url:'/your url', //gets called after "beforequery" without any parameters from the grid
        root:'data',
        restful:true,
        autoload:false,
        fields:[
        {
            name:'orgaID',
            type:'string'
        },
        {
            name:'orga_name',
            type:'string'
        }
        ]
    }),
    listClass: 'x-combo-list-small',
    listeners :
    {
        beforequery:
        {
            fn:function(qe)
            {
                var company_name = Ext.getCmp('survey_company_name').getValue();
                var orga_name    = Ext.getCmp('survey_orga_name').getValue();
                var orgagroup_name    = Ext.getCmp('survey_orgagroup_name').getValue();
                combostore_survey_orga_name.load({
                    params:{
                        column_name:'orga_name',
                        company_name: company_name,
                        orga_name : orga_name,
                        orgagroup_name : orgagroup_name
                    }
                    });
                return false; // important. return directly to prevent next unconfigured request with standardparams
            }
        },
        select:
        {
            fn:function(combo, value)
            {
              var company_name   = Ext.getCmp('survey_company_name').getValue();
              var orga_name      = Ext.getCmp('survey_orga_name').getValue();
              var orgagroup_name = Ext.getCmp('survey_orgagroup_name').getValue();
            //combostore_survey_orga_name.load({params:{column_name:'orga_name',
            //company_name: company_name, orga_name : orga_name,
            // orgagroup_name : orgagroup_name}});
            }
        }
    }

})

Die Serverantwort sieht so aus.

{"totalCount":2,"data":[{"orga_name":"MySQL","orgaID":"1"},{"orga_name":"MySQLsss","orgaID":"3"}]}

[/php]

new Ext.form.ComboBox({
        id              :'survey_company_name',
        typeAhead       :true,

        triggerAction   :'all',
        valueField      :'companyID',
        editable        :true,
        displayField    :'company_name',
        selectOnFocus   :true,
        typeAhead       :'true',
        hiddenName      :'companyID',
        mode            :'local',
        lazyRender      : true,
        store:
        this.combostore_survey_company_name=new Ext.data.SimpleStore({

            data: [['165','Deutsche Factoring Bank'],['159','Frontend-Development.ch'],['168','Kreis- und Stadtsparkasse Kaufbeuren '],['116','Kreissparkasse Esslingen-Nürtingen'],['162','Kreissparkasse Halle (Westf.)'],['174','Kreissparkasse Ostalb'],['163','Kreissparkasse Schwalm-Eder'],['95','Kreissparkasse Stendal'],['171','Kreissparkasse Walsrode'],['153','Müritz-Sparkasse'],['170','Sparkasse Bodensee'],['164','Sparkasse Deggendorf'],['90','Sparkasse Donnersberg'],['85','Sparkasse Emsland'],['173','Sparkasse Freising'],['92','Sparkasse Gifhorn-Wolfsburg'],['106','Sparkasse Harburg-Buxtehude'],['166','Sparkasse Hohenlohekreis'],['161','Sparkasse Jerichower Land'],['141','Sparkasse Langen-Seligenstadt'],['169','Sparkasse LeerWittmund'],['79','Sparkasse Leipzig'],['104','Sparkasse Meissen'],['100','Sparkasse Oberlausitz Niederschlesien'],['172','Sparkasse Rotenburg-Bremervörde'],['80','Sparkasse Rottal-Inn'],['160','Sparkasse Schopfheim-Zell'],['167','Sparkasse Sonneberg']],
            fields:[
                    {name:'companyID',type:'string'},
                    {name:'company_name',type:'string'}

                    ]
            }),
        listClass: 'x-combo-list-small',

            listeners :
                {
                    select:
                    {
                        fn:function(combo, value)
                        {
                             var company_name = Ext.getCmp('survey_company_name').getValue();
                            alert(company_name);
                             var orga_name    = Ext.getCmp('survey_orga_name').getValue();
                             var orgagroup_name    = Ext.getCmp('survey_orgagroup_name').getValue();
                             combostore_survey_orga_name.load({params:{column_name:'company_name', company_name: company_name, orga_name : orga_name, orgagroup_name : orgagroup_name}});

                        }
                    }

                }

     })
                        },{
                            id          : 'orga_name',
                            header      : 'Abteilung',
                    inputType:'textfield',
                            dataIndex   : 'orga_name',
                            sort        : 'orga_name',

                            sortable    : true,
                            width       :100,
                            editor      :
        new Ext.form.ComboBox({
        id              :'survey_orga_name',
        typeAhead       :true,

        allQuery: 'dfdsfdsf'+Ext.getCmp('survey_company_name').getStore(),
        triggerAction   :'all',
        valueField      :'orgaID',
        editable        :true,
        displayField    :'orga_name',
        selectOnFocus   :true,
        typeAhead       :'true',
        hiddenName      :'orgaID',
        mode            :'remote',
        lazyRender      : true,
        store:
        this.combostore_survey_orga_name=new Ext.data.JsonStore({

            url:'/cocominZend/0.0.1/public/survey/store/getcombo/columnname/orga_name/'
                    ,root:'data'
                    ,restful:true,autoload:false
            ,
            fields:[
                    {name:'orgaID',type:'string'},
                    {name:'orga_name',type:'string'}

                    ]
            }),
        listClass: 'x-combo-list-small',

            listeners :
                {
                    beforequery:
                    {
                        fn:function(qe)
                        {
                             var company_name = Ext.getCmp('survey_company_name').getValue();
                             var orga_name    = Ext.getCmp('survey_orga_name').getValue();
                             var orgagroup_name    = Ext.getCmp('survey_orgagroup_name').getValue();
                             combostore_survey_orga_name.load({params:{column_name:'orga_name', company_name: company_name, orga_name : orga_name, orgagroup_name : orgagroup_name}});
                             return false;
                        }
                    },
                    select:
                    {
                        fn:function(combo, value)
                        {
                            alert('called select');
                             var company_name = Ext.getCmp('survey_company_name').getValue();
                             var orga_name    = Ext.getCmp('survey_orga_name').getValue();
                             var orgagroup_name    = Ext.getCmp('survey_orgagroup_name').getValue();
                             //combostore_survey_orga_name.load({params:{column_name:'orga_name', company_name: company_name, orga_name : orga_name, orgagroup_name : orgagroup_name}});
                        }
                    }
                }

     })
                        }

Extjs : Remotesort. Serverseitiges Paging und die vielgesuchte Serverresponse

January 17th, 2010

Ich bastle seit Tagen an einer Groupinggrid mit Paging, Search, Exportfunktion, Inlineediting mit Roweditor, RSA Verschlüsslung und remote Sortierung auf alle Spalten.
Gleich ein wenig viel für den Anfang.

Also beschränke ich mich vorerst auf das Remotesorting, Grouping, RSA  und eben das Paging.

Wichtig zu wissen ist hier:
Der JsonReader verarbeitet die wesentlichen Serverresponse-Values. Wie die Gesamtzahl(totalProperty) der Datensätze. “totalProperty” ist wichtig für das Paging. Ohne die Zahl werden die Navigationsbuttons nicht aktiviert und paging suxx.
root: ‘data’, Enthält die Daten zur Anwort. Ohne Daten kein View. Klar.

Die Response muss allso mindestens 3 JS-Objekte liefern. “totalCOunt, sort und data”.
Die Resonse zu der Grid mit 2 Datensätzen oben sieht also folgendermassen aus:

{ "totalCount":206, "sort":"company_name", "message":"ewqeqwewqe", "data":
 [
     { "copyID":"227", "survey_name":"Mitarbeiterumfrage", "orga_name":"Trainer", "company_name":"Test23 AG", "created":"2009-06-16 19:19:13",    "description":"Test" },

     { "copyID":"226", "survey_name":"Mitarbeiterumfrage2", "orga_name":"Trainer2", "company_name":"Test AG 22", "created":"2009-06-16 19:19:13",    "description":"Test" },

] }

Groupstore: Beinahe noch wichtiger ist der Initialload der ersten page. Der Datastore wird beim Rendern mit der page 0 geladen

gridedtorstore.load({params:{start: 0, limit: 20}});

Der Code für die Grid dann so. Nicht über die RSA Sachen wundern. Die verschlüsslung ist hier Standar

var columnobjectsurvey = Ext.data.Record.create([{name: 'copyID',type: ''},{name: 'company_name',type: 'string'},{name: 'orga_name',type: 'string'},{name: 'created',type: 'string'},{name: 'description',type: 'string'},{name: 'survey_name',type: 'string'}]);

        //proxi
        surveyproxy = new Ext.data.HttpProxy({
            url: '/cocominZend/0.0.1/public/survey/store/get'
        });
        //reader
        this.surveyreader = new Ext.data.JsonReader({
        totalProperty: 'totalCount',
        successProperty: 'success',
        idProperty: 'id',
        root: 'data',
        messageProperty: 'message',
        fields: columnobjectsurvey
        });
        //writer
        // The new DataWriter component.
        this.surveywriter = new Ext.data.JsonWriter({
            encode: false   // <-- don't return encoded JSON -- causes Ext.Ajax#request to send data using jsonData config rather than HTTP params
        });

        this.gridedtorstore = new Ext.data.GroupingStore({
        id: 'survey',
        proxy: surveyproxy,
        idProperty: 'copyID',
        remoteSort: true,
        reader:surveyreader,
        writer:surveywriter,
        restful: true,     // <-- This Store is RESTful
        groupField:'survey_name',
        sortInfo: {field: 'company_name', direction: 'ASC'},
    });
        gridedtorstore.load({params:{start: 0, limit: 20}});

         var roweditorsurvey = new Ext.ux.grid.RowEditor({
                saveText: 'Update',
                listeners :
                {
                    afteredit : function(row) {

        var secure = true;
        var secureParam = secure ? 't' : 'f';
        val='copyID###'+row.record.data.copyID+'||'+'company_name###'+row.record.data.company_name+'||'+'orga_name###'+row.record.data.orga_name+'||'+'created###'+row.record.data.created+'||'+'description###'+row.record.data.description+'||'+'survey_name###'+row.record.data.survey_name+'||';
        alert(val);
        if(secure){
            val = rsajax.encrypt(val);
        }

        $.ajax({
            url: '/cocominZend/0.0.1/public/admin/pgp/decrypt',
            data: {val: val, secure: secureParam},
            dataType: 'json',
            success: function(data)
            {
                var val = data.val;
                if(secure)
                {
                    val = rsajax.decrypt(val);
                    Ext.MessageBox.alert('encrypted answer:',val);
                }
                else
                {
                    Ext.MessageBox.alert('dsds','Anybody who happened to be monitoring your network activity could have seen that data.');
                }
          },
            error: function(e)
            {
                Ext.MessageBox.alert('plain answer:',e.responseText);
            }
        });
            row.record.commit();
                    },
                }
            });

        var checkboxSelection = new Ext.grid.CheckboxSelectionModel({});
        var editorgridsurvey = new Ext.grid.GridPanel({
        store: gridedtorstore,
        region:'center',
        viewConfig: {forceFit: true},
        stripeRows:	true,
        stripeCols:	true,
        enableColLock: false,
loadMask: true,
plugins: [roweditorsurvey],
        view: new Ext.grid.GroupingView({
            markDirty: true
        }),
        tbar: [
{
text:		'list selected',
handler:	function(){
var seltext = '';
var sels = editorgridsurvey.getSelectionModel().getSelections();
for( var i = 0; i < sels.length; i++ ) {
seltext += sels[i].get('username');
}
Ext.Msg.alert('Selected', seltext);
}
}
                        ,
       {
            iconCls: 'icon-user-add',
            text: 'addSurvey',
            handler:
            function(){
                var e = new columnobjectsurvey({
            copyID:'newUser',company_name:'company_name',orga_name:'orga_name',created:'01/01/2010',description:'description',survey_name:'survey_name'
                });

            }

        },
        {
            ref: '../removeBtn',
            iconCls: 'icon-user-delete',
            text: 'removeSurvey',
            disabled: true,
            handler: function(){
                roweditorsurvey.stopEditing();
                var selections = editorgridsurvey.getSelectionModel().getSelections();

        var secure = true;
        var secureParam = secure ? 't' : 'f';
        val='userID###'+selections[0].data.userID+'||delete###true';
        alert(val);
        if(secure){
            val = rsajax.encrypt(val);
        }

        $.ajax({
            url: '/cocominZend/0.0.1/public/admin/pgp/decrypt',
            data: {val: val, secure: secureParam},
            dataType: 'json',
            success: function(data)
            {
                var val = data.val;
                if(secure)
                {
                    val = rsajax.decrypt(val);
                    Ext.MessageBox.alert('encrypted answer:',val);
                }
                else
                {
                    Ext.MessageBox.alert('dsds','Anybody who happened to be monitoring your network activity could have seen that data.');
                }
          },
            error: function(e)
            {
                Ext.MessageBox.alert('plain answer:',e.responseText);
            }
        });
                for(var i = 0, r; r = selections[i]; i++){
                    gridedtorstore.remove(r);

                }
            }
        },
        {
            text   : 'printSurvey',
            iconCls: 'icon-user-print',
            handler: function() {
            Ext.ux.GridPrinter.print(editorgridsurvey);
            },
        },
        {
            text   : 'printSurvey',
            iconCls: 'icon-user-print',
            handler: function() {
            document.location = '/cocominZend/0.0.1/public/survey/export/excel';
            }
       },

     new Ext.PagingToolbar({
            pageSize: 10,
            store: gridedtorstore,
            displayInfo: true,
            emptyMsg: "No topics to display"

        })
        ],
        columns: [new Ext.grid.RowNumberer(),{
                        id: 'company_name',
                        header: 'company_name',
                        inputType:'textfield',
                        dataIndex: 'company_name',
                        sort: 'company_name',

                        sortable: true,
                        width:200,
                        editor: {
                        xtype: 'textfield',
                        allowBlank: false,
                        inputType:'textfield',
                        disabled:false
                       }},{
                        id: 'orga_name',
                        header: 'orga_name',
                        inputType:'textfield',
                        dataIndex: 'orga_name',
                        sort: 'orga_name',

                        sortable: true,
                        width:100,
                        editor: {
                        xtype: 'textfield',
                        allowBlank: false,
                        inputType:'textfield',
                        disabled:false
                       }},{
                        id: 'created',
                        header: 'created',
                        inputType:'textfield',
                        dataIndex: 'created',
                        sort: 'created',

                        sortable: true,
                        width:120,
                        editor: {
                        xtype: 'textfield',
                        allowBlank: false,
                        inputType:'textfield',
                        disabled:true
                       }},{
                        id: 'description',
                        header: 'description',
                        inputType:'textfield',
                        dataIndex: 'description',
                        sort: 'description',

                        sortable: true,
                        width:200,
                        editor: {
                        xtype: 'textfield',
                        allowBlank: false,
                        inputType:'textfield',
                        disabled:false
                       }},{
                        id: 'survey_name',
                        header: 'survey_name',
                        inputType:'textfield',
                        dataIndex: 'survey_name',
                        sort: 'survey_name',

                        sortable: true,
                        width:200,
                        editor: {
                        xtype: 'textfield',
                        allowBlank: false,
                        inputType:'textfield',
                        disabled:false
                       }}]
    });

        this.surveyEditorgridPanel = new Ext.Panel({
        title: 'registeredUser',
        layout: 'border',
        autoScroll:true,
        width:1200,
        height: 500,
        items: [editorgridsurvey]
    });

            editorgridsurvey.getSelectionModel()
            .on('selectionchange', function(sm){
                editorgridsurvey.removeBtn.setDisabled(sm.getCount() < 1);
            });
             var categoryStore = new Ext.data.JsonStore({
            fields:['sum','categories'],data:[
                { categories:'Sonstiges',
                  sum: 42
                },
                { categories:'Coaching',
                  sum: 162
                }]
            }); var countryStore = new Ext.data.JsonStore({
            fields:['sum','countries'],data:[
                { countries:'Deutschland',
                  sum: 197
                },
                { countries:'Schweiz',
                  sum: 5
                },
                { countries:'Österreich',
                  sum: 2
                }]
            }); var companyCategoriesStore = new Ext.data.JsonStore({
            fields:['sum','companyCategory'],data:[
                { companyCategory:'Finanzdienstleistung',
                  sum: 199
                },
                { companyCategory:'Automobilindustrie',
                  sum: 5
                }]
            }); var companySizeStore = new Ext.data.JsonStore({
            fields:['sum','companySize'],data:[
                { companySize:'Mehr als 500 Mitarbeiter',
                  sum: 126
                },
                { companySize:'Mehr als 1000 Mitarbeiter',
                  sum: 70
                },
                { companySize:'50 -100 Mitarbeiter',
                  sum: 1
                },
                { companySize:'20 - 50 Mitarbeiter',
                  sum: 2
                },
                { companySize:'weniger als 10 Mitarbeiter',
                  sum: 5
                }]
            });
 var SurveyWithAveragesStore = new Ext.data.JsonStore({
        fields:['name', 'surveys', 'average'],
        data: [{name:'02', surveys: 32, average:'16'},{name:'03', surveys: 23, average:'11.5'},{name:'04', surveys: 30, average:'15'},{name:'05', surveys: 7, average:'3.5'},{name:'06', surveys: 39, average:'19.5'},{name:'07', surveys: 4, average:'2'},{name:'08', surveys: 4, average:'2'},{name:'09', surveys: 16, average:'8'},{name:'10', surveys: 24, average:'12'},{name:'11', surveys: 25, average:'12.5'}]});  //gets dynamicly replaced with content
        var xg = Ext.grid;

        // shared reader
        var reader = new Ext.data.ArrayReader({}, [

            {name: 'title'},
            {name: 'username'},
            {name: 'lastChange', type: 'date'},
            {name: 'desc',type:'text',selectable:true}
        ]);

    // row expander
    var expander = new Ext.ux.grid.RowExpander({
        tpl : new Ext.Template(
            '<p><b>Notiz:</b> {desc}</p>'
        )
    });

und der Gridpanel noch zu guterletzt

new Ext.Panel({
                id              :'surveyEditorgridPanel',
                title           :'surveyEditorgridPanel',
                region          :'center',
                deferredRender  :false,
                layout          :'table',
                frame           :true,
                padding         :10,
                autoScroll      :true,
                layoutConfig    :{columns:1},
                defaults        :{frame:true, width:900, height: 450},
                items:[

         {
                    title:'User: cocomin',
                    items:[{}],
                    colspan:2,
                    width:900,
                    height:70,
                    margin:10,
                    html: '<h1>Hallo cocomin heute ist der #date#. Willkommen in Deiner Umfrageadministration</h1><p>Im folgenden findest Du die neuesten Daten aus Deinen Umfragen. <input type="button" value="export"></p>'
                }
        ,
                    surveyEditorgridPanel
                ]
            })

RSA Verschlüsselung von Requests Client und Serverseitig für Zend mittels RC4 per Ajax

January 8th, 2010

Die einfachste Lösung seine Daten verschlüsselt zu übertragen ist SSL.
Wer an Opensource Webprojekten schraubt kann aber weder den User zwingen teure Zertifikate zu kaufen oder entsprechende Module wie mycrypt für AES oder openSSL bei seinem Hoster nachzurüsten.

Eine symetrische Verschlüsselung mit AES scheidet wegen dem “man in the middle” Problem aus. Also muss eine Methode her, die bei Netzwerküberwachung keine Daten preisgibt die einfach zu entschlüsseln wären.

Ich habe mich auf Anraten eines Commuitymitglieds (Danke an goosejan für die Nerven) entschieden, das asymetrische PKI Verfahren zu verwenden.
Auf der Clientseite werden die Formularinhalte mittels Javascript RC4 mit einem öffentlichen Schlüssel verschlüsselt, den der Server liefert und per Ajax an den Server übertragen. Das Ganze funktioniert 2 Way. Die Antwort des Servers wird natürlich auch verschlüsselt und clientseitig wieder entschlüsselt.

Als Privatekey auf dem Server wird eine 40 Zeichen lange Zeichenkette verwendet.

Als Grundlage habe ich die Implementierung verschiedener Programmierkollegen verwendet (siehe Sourcecomments).
Das Ganze habe ich auf Zend Framework 1.9.6 mit jquery (jquery-min) portiert und als Zend Modul “Zend_RSAjax” für Zend-Framework Projekte flott gemacht.

Hier sind die Sourcen dazu.
library: liegt bei mir unter library im Zend_Framework Projektordner, damit sie automatisch geladen werden
library:Zend_RSAjax
public: javascripte im publicordner
public:javascripts

Lizenzen: Bitte die Lizenzen MIT und GPL der einzelnen Implementierungen beachten.

Hier das Tutorial:
1) Controller anlegen, der die Requests entgegennimmt. Bei mir liegt der im Modul Admin also :( Admin_PgpController)

<?php
/**
 * @desc implements the serverside RAS RC4
 * @author: Peter Boethig
 */
class Admin_PgpController extends Zend_Controller_Action {

    public function init() {

        $this->RSAWrapper = new Zend_RSAjax_Wrapper();
    }

    public function indexAction() {
        ;
    }
    //disable layout for Ajaxcalls
    public function preDispatch() {
        $this->_helper->layout()->disableLayout();
        $this->_helper->viewRenderer->setNoRender(true);
    }

    //return the pKey for clientsite
    public function getpublickeyAction() {
        $json = array('n'=>$this->RSAWrapper->rsajax_server_n, 'e'=>$this->RSAWrapper->rsajax_server_e);
        print(json_encode($json));
    }

    //saves PK into session
    public function setsessionAction() {
        if($this->_request->getParam('key') !=null) {
            if(!session_id){session_start();}
            $_SESSION['s_rsajax_client_key'] = $this->RSAWrapper->decrypt($this->_request->getParam('key'));
        }
    }

    //decodes Value and returns encoded
    public function getkeyAction() {
        if($this->_request->getParam('secure')=='t') {
            $val = $this->RSAWrapper->encrypt($this->RSAWrapper->decrypt($this->_request->getParam('val')));
        }
        header('Content-Type: text/html;charset=utf-8');
        print(json_encode(array('val'=>utf8_encode($val))));
    }
}

2) RC4 und RAS Javscripte für die Clientseite einhängen
Hier im Downloadlink sind die Sourcen dazu. einfach in den html Head einhängen.

3) In dem Viewscript oder in einem Helper, welches das Formular enthält zb. index.phtml wird ein Javascript ausgeben, welches die Secure Session startet.

$SecureSession = new Zend_RSAjax_SecureSession();
$this->getController     = $this->baseURL."/admin/pgp/getpublickey";  // Pfad auf  die Controlleraction (bei mir admin)
$this->sessionController = $this->baseURL."/admin/pgp/setsession";
echo $SecureSession->create();

4) Das Formular übertragen

<input type=’submit’ onclick=’sendForm()’>

function sendForm(){
        var secure = true; //should be tested
        var secureParam = secure ? 't' : 'f';
        val='testpass';  // your formvalues

        if(secure){
            val = rsajax.encrypt(val);
        }

        $.ajax({
            url: '/cocominZend/0.0.1/public/admin/pgp/getkey', //your requestcontroller in Zend
            data: {val: val, secure: secureParam},
            dataType: 'json',
            success: function(data)
            {
                var val = data.val;
                if(secure)
                {
                    val = rsajax.decrypt(val);
                    alert(val + 'As you can see, your data was encrypted. Nobody could have read it by monitoring your network activity.');
                }
                else
                {
                    alert('Anybody who happened to be monitoring your network activity could have seen that data.');
                }
          },
            error: function(e)
            {
                alert('error:'+e.responseText);
            }
        });
}

Extjs: RowEditor. Remove Event an die Datenbank weitergeben.

January 7th, 2010

Die Doku zu dem Thema ist umfangreich. Leider wird überall nur die clientseitige Methode beschrieben die Gridrow zu löschen.
Wie man die Zeile auch der DB löscht ist ein wenig Tricky..(Massive Attack ftw), wenn man localstores verwendet.

Hierzu muss man wie beschrieben einen Deletebutton in eine tbar hängen und als Handler eine function() registrieren.
Anschliessend holt man sich über das Selectionmodel des Gridpanels die Selection, welche die markierte Row darstellt.
Das Ganze ist ein Array. Ueber den 0ten Index kommt man an das Dataobjekt ran, über das man dann auf die einzelnen Cells zugreifen kann. (z.B um die id des zu löschenden Datensatzen über eine Ext.data.connection zu submitten)

Hier ist der Code welcher in die tbar des GridPanels eingehangen wird

{
            ref: '../removeBtn',
            iconCls: 'icon-user-delete',
            text: '{$this->removeButton}',
            disabled: true,
            handler: function(){
                roweditor{$this->gridid}.stopEditing();
                var selections = editorgrid{$this->gridid}.getSelectionModel().getSelections();
                if (selections[0] != null) {
                        var conn = new Ext.data.Connection();
                        conn.request({
                                    url : '{$this->saveController}',
                                    params : {
                                        userID:selections[0].data.userID,
                                        delete:'true',
                                    },
                                    failure : function(resp, opt) {
                                        Ext.MessageBox.alert('Error', response.responseText);
                                    }
                                });
                }
}

Extjs combo listeners onchange vs. select

January 2nd, 2010

Nach 1 Stunde Sucherrei nach dem richtigen Event, um onchange Verhalten auf Comboboxen umzusetzen bin ich auf select gestossen.

Das changeverhalten wird erst ausgeführt wenn das Combofeld den focus verliert. Das ist lästig, weil der User ein echte Onchangeverhalten erwartet.

Hier die Lösung. Statt change einfach select

{listeners:{select:function(){alert("test")}}

Das feuert auf Klick auf die Combo Option

Extjs: Checkboxvalues unchecked übertragen

December 30th, 2009

Das leidige Thema checkboxen im Submit macht auch in Extjs keine Ausnahme.
Checkboxen und Radiobuttons werden beim Submit nicht übertragen wenn sie nicht gesetzt sind. In HTML ist das ganze recht einfach.

In Extjs habe ich die einfachste Lösung gefunden. Man überschreibt das Defaultverhalten und setzt seinen eigenen unchecked-Value.

Hier iist der Code dazu.

Ext.lib.Ajax.serializeForm = function(F){
	if(typeof F==\"string\"){
		F=(document.getElementById(F)||document.forms[F])
	}
	var G,E,H,J,K=\"\",M=false;
	for(var L=0;L<F.elements.length;L++){
		G=F.elements[L];
		J=F.elements[L].disabled;
		E=F.elements[L].name;
		H=F.elements[L].value;
		if(!J&&E){
			switch(G.type){
				case\"select-one\":
				case\"select-multiple\":
					for(var I=0;I<G.options.length;I++){
						if(G.options[I].selected){
							if(Ext.isIE){
								K+=encodeURIComponent(E)+\"=\"+encodeURIComponent(G.options[I].attributes[\"value\"].specified?G.options[I].value:G.options[I].text)+\"&\"
							}else{
								K+=encodeURIComponent(E)+\"=\"+encodeURIComponent(G.options[I].hasAttribute(\"value\")?G.options[I].value:G.options[I].text)+\"&\"
							}
						}
					}
					break;
				case\"radio\":
				case\"checkbox\":
					if(G.checked){
						K+=encodeURIComponent(E)+\"=\"+encodeURIComponent(H)+\"&\"
					} else {
						K+=encodeURIComponent(E)+\"=\"+encodeURIComponent('false')+\"&\"
					}
					break;
				case\"file\":
				case undefined:
				case\"reset\":
				case\"button\":
					break;
				case\"submit\":
					if(M==false){
						K+=encodeURIComponent(E)+\"=\"+encodeURIComponent(H)+\"&\";M=true
					}
					break;
				default:
					K+=encodeURIComponent(E)+\"=\"+encodeURIComponent(H)+\"&\";
					break
			}
		}
	}
	K=K.substr(0,K.length-1);
	return K
}

Extjs- Propertygrid. Daten submitten

December 24th, 2009

Leider verhalten sich die Propertygrids nicht wie normale Extjs Formulare obwohl sie eigentlich nur eine spezielle Form davon sein sollten.

Eine Möglichkeit über this.getForm().submit() die Daten zu senden gibts leider nicht.

Hier ist es am einfachsten, einen Button zu adden und einen Event zu registrieren, der einen Jsonstring generiert und überträgt.
Hier ist dieser Event. Einfach an die Propertygrid anhängen.
Wichtig ist zu wissen, dass die Propertygrid die Daten in einer “source” Property vorhält. Die kann man aber schlecht iterieren um die Variablen zu ermitteln.
Die Jsonencodierung dieser Source liefert jedoch einen verwendbaren “Pseudo”-Jsonstring in dem die ” mit \ escaped sind. Die muss man nach dem Request einfach entfernen. Dann kann man mit json_decode() sauber die Vars extrahieren.

buttons:
                [{
                    text: 'speichern',
                    handler: function(){
                    var values = Ext.util.JSON.encode(ID_der_Propertygrid.source)  ;
                    Ext.Ajax.request({
                                url       : 'index.jsp',
                                method    : 'POST',
                                params    : {
                                        values : values
                                },
                                scope     : this,
                                callback  : function(options, success, response) {
                                        if (success) {
                                                this.el.unmask();
                                                Ext.MessageBox.alert('Success!', response.responseText);
                                        }else
                                        {
                                            Ext.MessageBox.alert('Failsure!', 'Suxx');
                                        }
                                }
                        })

                    }
                }]

Extjs “Combo -Boxen” und der geliebte “selected” status auf die aktive Option.

December 22nd, 2009

Manche Dinge, die in Standard – HTML so gewohnt einfach und selbstverständlich daherkommen, dass die Erstellung für einen Programmierer ein paar Sekunden dauern, können einen Einsteiger in DOM und speziell in Extjs 3.1 Tage abverlangen.
Ein Selectfield in HTML ist schnell geschrieben. Die selected-Option auf ein bestimmtes Value noch schneller gesetzt.

in HTML sag ich einfach

<select  id ="" name="" >
<option value="id" selected>ein optionvalue</option>
</select>

fertig. Das Value ist voreingestellt.

In Extjs gibts die Komponente “combo” dafür. Sieht echt toll aus und man hat viele Ajaxoptions dafür.
Sie ist auch leicht erstellt.

{
            xtype: 'combo',
            id: 'username',
            fieldLabel: 'Username',
            hiddenName: 'userID',
            emptyText: 'Select a username...',
            store:
              new Ext.data.SimpleStore({
                fields: ['userID','username'],
                data: [['1','cocomin'],['2','testuser']]

            }), // end of Ext.data.SimpleStore
            displayField: 'username',
            valueField: 'userID',
            selectOnFocus: true,
            mode: 'local',
            typeAhead: true,
            editable: false,
            triggerAction: 'all',
            valueField: 'userID',
            value: '1',
          }

Ja, ist doch easy. Klar. Leicht und verständlich.
Ja. Wenn man folgendes berücksichtigt schon.

1) Wenn man weis, dass man für die Options einen Datastore erstellen muss

2) das forceSelection auf true stehen muss

3) das der hiddenvalue (zb userid). der variablenname im request ist

4) das man den Diplayvalue explizit setzen muss
(in diesem Fall displayField: ‘,valueField: ‘username’,)

5) Das die Verwendung eines lokalen Datastores immer mode:local benötigt. (mode:remote versucht einen jsonstring vom server in den Store zu laden)

6) und das ist das wichtigste in Bezug auf vorselectierte Options
value:’Herr Kannengiesser’ setzt einerseits einfach einen Direktvalue. Tut man das so wird jedoch nur der Value übertragen (”Herr Kannengiesser”), weil er ja keine dazugehörige ID kennt. Klar soweit.
Verwendet man jedoch eine ID (z.B.valueField: ‘userID’, value=’1′) aus dem Datastore zeigt er nicht etwa die ID an. Nein, gut erkannt, er sucht im Datastore zuerst eine ID zum Value=’1′. Exsitiert diese ID im Datastorte zeigt er statt “1″ (value:’1′) den value unter “value:’1′” an. Hierzu muss man den Valuefield auf den Key im Store setzen (valueField: ‘userID’),
Dieser ist dann auch fein selected. Im Request kommt dann brav unter $_REQUEST[userID] (hiddenvalue:’userID’) die “1″ als userID an.
Die Doku und das Forum dazu dazu ist so umfangreich, dass ich von Hacks bis zum dynamischen Nachladen bis über eingeharkte HTML Selectfields in Panelkonstrukten alles gefunden und ausprobiert habe.

Ein “selectedValueInStore” wäre eine feine Sache gewesen.. Nunja. Ich freue ich mich nun über die gefundene Lösung und dass ich vielleicht jemandem die 2 Tage Suche sparen kann.

KISS. Keep it short and simple………..