Build List / Grid Searchfield

Donnerstag, 20. Februar 2014
I want you to show a little tutorial how to create a searchfield to filter a list, grid or something else which has a relation to a store. I want to show this to you by using a list.

If you need you can download the Architect 3 project from here: https://mega.co.nz/#!9NFyRCLL!98dkUmISgGO_p0crDI8YExINmf5ORCglkDrWjuRyu7Y


First we create a Store with a model and some data in it. If you want to take a look, you'll have a link here: https://gist.github.com/difa1/9115899

Second we need to create a simple list in a panel:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.Panel',
    requires: [
        'Ext.dataview.List',
        'Ext.XTemplate'
    ],
    config: {
        layout: 'vbox',
        items: [
            {
                xtype: 'list',
                flex: 1,
                itemTpl: ['{name}, {namelast}'],
                store: 'persons'
            }
        ]
    }
});

Third we would like to implement the searchfield. You can place it however you want.

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.Panel',
    requires: [
        'Ext.field.Search',
        'Ext.dataview.List',
        'Ext.XTemplate'
    ],
    config: {
        layout: 'vbox',
        items: [
            {
                xtype: 'searchfield',
                docked: 'top',
                itemId: 'storeSearchField',
                label: 'Field'
            },
            {
                xtype: 'list',
                flex: 1,
                itemId: 'mylist',
                itemTpl: ['{name}, {namelast}'],
                store: 'persons'
            }
        ]
    }
});

Now that the basics a build we build a new controller where all the logic is stored. So lets go to Step 4 and create the controller with 2 listeners: keyup and clearicontap:

Ext.define('MyApp.controller.logicController', {
    extend: 'Ext.app.Controller',
    config: {
        control: {
            "searchfield#storeSearchField": {
                clearicontap: 'onStoreSearchFieldClearicontap',
                keyup: 'onStoreSearchFieldKeyup'
            }
        }
    },
    onStoreSearchFieldClearicontap: function(textfield, e, eOpts) {
        // Logic
    },
    onStoreSearchFieldKeyup: function(textfield, e, eOpts) {
        // Logic
    }
});

The logic for the clearicontap is pretty simple... All we want is to clear all filters. All we need is the clearfilter method :

    onStoreSearchFieldClearicontap: function(textfield, e, eOpts) {
        Ext.getStore('persons').clearFilter();
    }


The keyup is a bit more tricky, why I like to use a basic function for it. I tried to explain all steps in the coding itself. If anything were unclear, you could contact me on Google+ ;)

onStoreSearchFieldKeyup: function(textfield, e, eOpts) {
    this.filterList(textfield, Ext.getStore('persons'));
},

filterList: function(textfield, store) {
    // Get the searchfield content
    var value = textfield.getValue();

    // Clear previous filters
    store.clearFilter();

    // Check for search value, if not present we don't need to to anything...
    if (value) {

        // Split search value: Space --> 2 custom values!
        var searches = value.split(' '),
        regexps = [],
        i;

        // Loop search values and build regex
        for (i = 0; i < searches.length; i++) {

            // Ignore spaces
           if (!searches[i]) continue;

           // Regex array
           regexps.push(new RegExp(searches[i], 'i'));
            
       }

       // Filter the store
       store.filter(function(record) {

           // Clear matches first
           var matched = [];

           // Loop all regex expressions
           for (i = 0; i < regexps.length; i++) {
               var search = regexps[i];
               var didMatch;

              didMatch = record.get('name').match(search) ||
                         record.get('namelast').match(search);
                    // You can add as many model fields as you want! --> record.get('city1').match(search) ||


              // Write match to array
              matched.push(didMatch);
          }

          // Nothing found? Do not return anything ;)
          if (regexps.length > 1 && matched.indexOf(false) != -1) {

               return false;

          } else {

               // Found something? Just return to store ;)
               return matched[0];

          }
     });

     // Finally load store
     store.load();
}



Read more ...

Part 3: Send file attachments by email

Montag, 3. Februar 2014
In this last part I want you to show how you can send an attachment by email. Generally you can add any file from the device you have in your sandbox. I will show you how to add the csv file which we created in Part 1 and Part 2

First we need a Phonegap / Cordova plugin. If you didnt know how to setup your project for use with phonegap, you would have to take a look at Part 2.

1. Add emailComposer Plugin
Open the terminal and cd to your Phonegap / Cordova folder and execute the following command:

cordova plugin add https://github.com/katzer/cordova-plugin-email-composer.git

This is the call structure from the developer website:

/*
 * Opens an email draft pre-filled with the passed properties.
 */
window.plugin.email.open({
    to:          Array, // contains all the email addresses for TO field
    cc:          Array, // contains all the email addresses for CC field
    bcc:         Array, // contains all the email addresses for BCC field
    attachments: Array, // contains all full paths to the files you want to attach
    subject:    String, // represents the subject of the email
    body:       String, // represents the email body (could be HTML code, in this case set isHtml to true)
    isHtml:    Boolean, // indicats if the body is HTML or plain text
});

For further informations about the plugin take a look to the developer website: https://github.com/katzer/cordova-plugin-email-composer

2. Send the email

The function should be self explaining. You can either pass the recipient, cc, bbc as string or as Array. Of course you can send any kind of attachments using this function. If an example is needed how to read file from LocalFileSystem and send them please ask for it and I'm willing to prove an example.

FILE2MAIL: function(recipient, cc, bcc, subject, body, attachementURL) {
    if (!Ext.isArray(recipient)) {
        recipient = new Array(recipient);
    }

    if (!Ext.isArray(cc)) {
        cc = new Array(cc);
    }

    if (!Ext.isArray(bcc)) {
        bcc = new Array(bcc);
    }

    if (!Ext.isArray(attachementURL)) {
        attachementURL = new Array(attachementURL);
    }

    window.plugin.email.open({
        to:          recipient, // contains all the email addresses for TO field
        cc:          cc, // contains all the email addresses for CC field
        bcc:         bcc, // contains all the email addresses for BCC field
        attachments: attachementURL, // contains all full paths to the files you want to attach
        subject:    subject, // represents the subject of the email
        body:       body, // represents the email body (could be HTML code, in this case set isHtml to true)
        isHtml:    true // indicats if the body is HTML or plain text
   });
}

3. Combining Part 1 & Part 2 & Part 3

var dummyStore = Ext.getStore('dummyStore');

STORE2CSV(dummyStore,null, stringSuccess);

function stringSuccess(csv){
    DATA2FILE('export.csv',csv,fileSavedSuccess)
}

function fileSavedSuccess(file){
    FILE2MAIL('dummyEmail@gmail.com','','','hallo test file','Test Attachement',file.fileName);
}


If you have any kind of questions or improvements please leave a comment or contact me. I appreciate any help or hints!


Read more ...

Sencha Touch Grid Action Column

Montag, 3. Februar 2014
As you may know the current grid version (2.3.0.2) in the Sencha Touch framework doesn't support the action column at the moment.

I this post, I want you to show how you can add clickable "button" in a column. In fact is it not a real button, but it will allow you to take track which cell is clicked and to take actions then.

First we need a grid with some columns in it. The column which needs action should be a template column so you can style it like you want. Something like this:

Ext.define('SiteCockpit.view.ExampleGrid', {
    extend: 'Ext.grid.Grid',
    alias: 'widget.examplegrid',

    requires: [
        'Ext.grid.column.Template',
        'Ext.XTemplate',
        'Ext.grid.plugin.MultiSelection',
        'Ext.grid.plugin.Editable'
    ],

    config: {
        itemId: 'exampleGrid',
        store: 'dummyStore',
        title: 'Example Grid',
        columns: [
            {
                xtype: 'templatecolumn',
                tpl: [
                    '<tpl>',
                    '    <div class="button1-field"></div>',
                    '<tpl>'
                ],
                width: 50
            },
            {
                xtype: 'templatecolumn',
                tpl: [
                    '<tpl>',
                    '    <div class="button2-field"></div>',
                    '<tpl>'
                ],
                width: 50
            },
            {
                xtype: 'column',
                fdname: 'exidv',
                width: 150,
                align: 'center',
                dataIndex: 'exidv',
                text: 'exidv'
            }
       ]
    }
})

As you can see the first and the second column do not have any text just a class. If you want some text just add by yourself.

Now we need to declare these classes in the sass to get a button like style. It is up to you how you want to style it. I just added 2 icons.
//
//    
// .button1-field { content: ""; height: 2.5em; width: 2.5em; background-image: url(data:image/png;base64,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); background-size: 1.5em; background-size: 1.5em; display: block; background-repeat: no-repeat; background-position: bottom; } .button2-field { content: ""; height: 2.5em; width: 2.5em; background-image: url(data:image/png;base64,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); display: block; background-size: 2em; background-size: 2em; background-repeat: no-repeat; background-position: bottom; }

Next we need to know when and more important which cell was tapped! I took this logic in a separate controller. We use the classes to know which one was tapped. It sounds pretty simple and it even is :)

Ext.define('SiteCockpit.controller.gridController', {
    extend: 'Ext.app.Controller',

    config: {
        control: {
            "grid": {
                itemtap: 'onDetailGridTap'
            }
        }
    },

    onDetailGridTap: function(dataview, index, target, record, e, eOpts) {
        var me = this;

        switch (e.target.className) {

            case 'button1-field':

                // First row was tapped!    
                break;

            case 'button2-field':

                // Second row was tapped!
                break;
        }
    }
})

I hope it is all clear and will help you a bit until the action column is supported :)
Read more ...

Part 2: Sencha Store to local CSV file

Sonntag, 2. Februar 2014
Hey guys,

This is Part 2 of 3 How to send a Sencha Store as CSV file by email. If you missed the first part, you would like to take a look here http://abitofcoding.blogspot.com/2014/01/part-1-sencha-touch-store-to-csv-string.html

1. Get Phonegap / Cordova ready
Firstly we will need our app wrapped with Phonegap / Cordova. There are several ways to do, since Touch 2.3.0 Sencha offers a pretty nice integration for Phonegap and Cordova. Since the you can setup your project with a single Sencha CMD command:



// FOR PHONEGAP
// sencha phonegap init [APP IDENTIFIER] [APP NAME]
sencha phonegap init com.mycompany.MyApp MyApp

// FOR CORDOVA
// sencha cordova init [APP IDENTIFIER] [APP NAME]
sencha cordova init com.mycompant.MyApp MyApp


Take a look at the Sencha Docs for more options and information: http://www.sencha.com/blog/leveraging-phonegap-within-sencha-touch/

2. Install requirements
As we need access to the device filesystem we need to install the file plugin for Phonegap / Cordova.
If you use Phonegap / Cordova 3.* it's pretty simple:

// FOR CORDOVA
//same for phonegap as for cordova
cordova plugin add org.apache.cordova.file
// the transfer plugin is not really needed but nice to avoid errors :)
cordova plugin add org.apache.cordova.file-transfer


That's it :D

3. Building and saving

As mentioned Part 1 is about building the CSV string. This one we will use now to safe on file.
You can create and add any content to any kind of file you want.
Take the CSV string from Part 1 as data and take a filename which is something like *.csv and you will have a CSV file created on your device!
Questions or improvements are welcome!

// FOR CORDOVA
DATA2FILE: function(filename, data, callback) {
        // default filename
        var defaultFileName =  'export-file.txt';

        if (filename === undefined || filename === null) {
            filename = defaultFileName;
        }

        // Request the file system
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);

        // Access to filesystem is OK
        function gotFS(fileSystem) {
            fileSystem.root.getFile(filename, {create: true}, gotFileEntry, fail);
        }

        // File is ready
        function gotFileEntry(fileEntry) {
            fileEntry.createWriter(gotFileWriter, fail);
        }

        // Write file content
        function gotFileWriter(writer) {
            writer.onwriteend = function(evt) {
                console.log('finished writing');
                if (callback !== undefined) {
                    callback(writer);
                }
            };
            writer.write(data);
        }

        function fail(error) {
            console.log('Error: ', error.code);
        }
    }

To combine the CSV string from Part 1 and this method something like this is possible:

var duSto = Ext.getStore('dummyStore');
STORE2CSV(duSto,null,
 function(csvData){
  DATA2FILE('export.csv',csvData, function( // PART 3 HOW TO SEND COMING SOON){}
        }
) 



Read more ...