Search code examples
extjsdynamic-datasencha-cmd

Sencha ExtJS MVC - data source specified at run time


I want to write a proof-of-concept app along these lines:

View

- a URL text input field at top with Go button
- a big DIV underneath consisting of the rest of the view

Controller

- upon Go pressed, validate the URL text
- set up the URL to the data source
- read data from the data source
- create a nested DIV element for each data, apply CSS rules
- add the element to the big DIV

Model

- define the fields
- define the default ordering

CSS

- define the styles

First, does what I have written above work within ExtJS or will I be fighting the framework? In particular, inserting plain HTML as element nodes.

Second, does anyone know of an existing project under GPL which could act as starting point? So far what I've seen are flashy examples with URLs hard-coded and set to auto-load.


Solution

  • There's nothing scary or otherwise disturbing in what you've written.

    Although not much advertised, ExtJS handles custom HTML & CSS pretty well. You can set some using the html or tpl config options. The latter is powered by XTemplates, so you can do loops, etc. When using these options and/or custom CSS, Ext will calculate its layouts around the rendered result, accounting for your custom style automatically. Now, in practice, that's a whole lot of work for the framework, and it doesn't always work as expected, and it won't work at all on some browsers (like not so old IE, of course). One big pitfall you should be aware of is that you should always use integer value in px for your CSS, since if a dimension resolve to a decimal value in px, Ext will choke on that.

    That said, since you're apparently going to back your data with a model, you should probably use a dataview. That's a component that let you use custom HTML over a regular Ext store. It then provides goodies for item selection, paging, etc. It's the base class of other advanced data views, like Ext grids.

    Regarding URLs, you don't necessarily have to hardcode them in the model's proxy (or elsewhere). You can pass an URL to an existing store's load method (as documented here).

    Finally, I don't know of existing projects, but your POC is really straightforward, so here's a fiddle to get you started. The code is not 100% clean, in particular defining everything in a single file, and thus missing all the requires... But it illustrates most of the points you've asked about. Read the docs about the components / methods that are used to learn how to go beyond this.

    Here's the fiddle's code:

    Ext.define('Foo.model.Item', {
        extend: 'Ext.data.Model',
        fields: ['name']
    });
    
    Ext.define('Foo.view.MainController', {
        extend: 'Ext.app.ViewController',
    
        alias: 'controller.main',
    
        onGo: function() {
            var view = this.getView(),
                url = view.down('textfield').getValue(),
                dataview = view.down('dataview'),
                store = dataview.getStore();
            if (this.isValidUrl(url)) {
                store.load({url: url});
            } else {
                Ext.Msg.alert(
                    "Invalid URL",
                    "This URL cannot be loaded here: " + url
                );
            }
        },
    
        // private
        isValidUrl: function(url) {
            return ['data1.json', 'data2.json'].indexOf(url) !== -1;
        }
    });
    
    Ext.define('Foo.view.Main', {
        extend: 'Ext.Panel',
    
        xtype: 'main',
        controller: 'main',
    
        layout: {
            type: 'vbox',
            align: 'stretch'
        },
    
        items: [{
            xtype: 'container',
            layout: 'hbox',
            margin: 3,
            defaults: {
                margin: 3
            },
            items: [{
                flex: 1,
                xtype: 'textfield',
                emptyText: "Valid inputs are 'data1.json' or 'data2.json'",
                listeners: {
                    specialkey: function(field, e) {
                        if (e.getKey() === e.ENTER) {
                            // custom event, for the fun of it
                            field.fireEvent('enterkey', field, e);
                        }
                    },
                    // the custom can be bound to controller in "modern ext" way
                    enterkey: 'onGo'
                }
            },{
                xtype: 'button',
                text: "Go",
                handler: 'onGo'
            }]
        },{
            flex: 1,
            xtype: 'dataview',
            margin: '0 6 6 6',
            cls: 'my-dataview', // for CSS styling
            store: {
                model: 'Foo.model.Item',
                autoLoad: false
                // default proxy is ajax and default reader is json,
                // which is cool for us
            },
            tpl: '<tpl for=".">' + '<div class="item">{name}</div>' + '</tpl>',
            itemSelector: '.item'
        }]
    });
    
    Ext.application({
        name : 'Foo',
        mainView: 'Foo.view.Main'
    });
    

    Some CSS for the data view:

    .my-dataview .item {
        padding: 1em;
        border: 1px solid cyan;
        color: magenta;
        float: left;
        margin: 6px;
        width: 100px;
    }
    

    And an example JSON response (this is the bare minimum to be functional... read about proxies & reader to go further):

    [{
        name: 'Foo'
    },{
        name: 'Bar'
    },{
        name: 'Baz'
    }]