references

project setup

sencha  -sdk /usr/local/bin/Sencha/touch-2.4.1 generate app -n $app_name -p $dir
cd $dir
sencha web -p 8282  start

script build nel contesto di una app cordova

cd $dir
sencha app build build/android
cd ..
cp -r $dir/build/android www
cordova build
cordova run

cordova: sessione di esempio

sencha cordova init test0.SenchaTest2 SenchaTest2
sencha app build build/android
# mantenere commenti e indentazione
sencha app build testing
# senza 3 agomento, fa building production
sencha app build
  • The entry point for Sencha Touch is the launch() function of an Ext.application
  • Ext.application() code is located in the app.js
  • by default, it loads app/view/Main.js

basic methods

testting template

  <!DOCTYPE HTML>
  <html manifest="" lang="en-US">
  <head>
      <meta charset="UTF-8">
      <link rel="stylesheet" href="../../../touch/resources/css/base.css">
      <script type="text/javascript" src="../../../touch/sencha-touch-all-debug.js"></script>
  <script type="text/javascript">
  // app def
  Ext.application({
      name: 'Events',
      requires: [ /*...requires files*/ ],
      launch: function() {
          // Display the components
          Ext.create('Ext.Container', {
              fullscreen: true,
              items: [ /**/ ]
          });
      }
  });
  </script>
  </head>
  <body>
  </body>
</html>
Ext.onReady(function() {
    var el = Ext.get('id');// works olso whith xtype, id, css selector, return ext dom wrapper
    var a_pars = Ext.select("p");// multiple elements
    var node = Ext.getDom('title');//return browser node
});

class and layout systems

// create a class,
// dependencies will auto‐matically load through  Ext.Loader()
// $packagename maps to the app folder! app/$packagename/$ClassName.js
Ext.define('$AppName.$packagename.$ClassName', {
    extend: 'Ext.Component',
    config: {
      html: 'Hello World' // getHtml() e setHtml() are automatically generated
    }
    constructor: function(config) {
    },
    myVar: 1,
    myMethod: function(name){
        //console.log("Log: " + name);
    }
},function(){
    // optional callback, after dependecy loaded
});
Ext.create('DemoApp.view.DemoComponent', {
  fullscreen: true
});

singleton:

Ext.define('Utils.common.Logger', {
  singleton: true,
  version: "1.02",
  log: function(msg) {
    console.log(msg);
  }
});

static members:

Ext.define('App.utils.Commons', {
  statics: {
    YELP_API: 'http://test.com/search?',
    getUrl: function() {
      return this.YELP_API + "&q=" + 1;
    },
  }
});

multiple inheritance works (mixins)

Ext.define('App.vehicle.FourWheeler', {
  mixins: {
    canBrake: 'App.mixins.Brake',
  }
});
var c = Ext.create('App.vehicle.FourWheeler');
c.mixinMethod();

showing components+containers:

var c = Ext.create('Ext.Component', {
  html: 'Hello World!'
});
Ext.onReady(function() {
  Ext.require('Ext.Component');
  Ext.create('Ext.Component', {
    html: 'Hello World!',
    style : { background: 'red' },
    cls: 'box',
    width: 300,
    height: 100,
    renderTo: Ext.getBody()
  });
});

layout

Ext.define('App.view.MainInterface', {
  extend: 'Ext.Container',
  requires: ['Ext.layout.HBox'],
  layout: {
    type: 'hbox',  // 'vbox', 'fit', 'card'
    align: 'start',// start (top), center (middle), end (bottom), and stretch
    pack: 'start'
  },
  items: [{
    xtype: 'component',
    html: 'box 1'
  },{
    xtype: 'component',
    html: 'box 2'
  }]
});

A card layout allows you to fit multiple components in one space, and show only one at a time

// stack is a Ext.Container
stack.setActiveItem(2);

effects:

layout: {
  type: 'card',
  animation: {
    type: 'slide',
    direction: 'left'
  }
}

in alternativa esiste, su tutti i componenti

docked: 'top'

templates:

Ext.create('Ext.Component', {
  tpl: Ext.create('Ext.XTemplate','<h1>{name}</h1><p>{description}</p>'),
  data: {
  name: 'Template',
  description: 'This is an example of how to configure a basic template.'
  }
});
 
var c = Ext.create('Ext.Component', {
  tpl: '<h1>{name}</h1><p>{description}</p>',
  data: data,
  styleHtmlContent: true,
  cls: 'box',
  renderTo: Ext.getBody()
});
// run time re-render
c.setData(data);
 
// definizione globale dei templates
Ext.define('Utils.utils.Template', {
  statics: {
    MY_TPL: Ext.create('Ext.XTemplate','<h1>{name}</h1><p>{description}</p>');
  }
});

component selection with Ext.ComponentManager

 
// An alternative way of creating references to components is by using a refs object in the ====
 
// css selectors
Ext.ComponentQuery.query('panel[title="Test"]');
 
Ext.ComponentQuery.query('#settingsview')[0].show();
Ext.ComponentQuery.query('#settingsview')[0].hide();
 
// a string with the xtype
var cars = Ext.ComponentQuery.query('car');
 
// x id settato nel componente
Ext.ComponentMgr.get(component_id) ;

define custom Component

Ext.define('BookTaxiBtn', {
  extend: 'Ext.Button',
  xtype: 'booktaxibtn',
  config: {
    text: 'Book a Taxi - listeners',
    margin: 5,
    listeners: {
      tap: 'bookTaxiEventHandler'
    },
  },
  bookTaxiEventHandler: function(b){
    console.log('You tapped the ' + b.getText() + 'button');
  }
});

Event Listener

var callTaxiBtn3 = Ext.create('Ext.Button', {
  margin: 5,
  text: '3: Call a Taxi - on'
});
callTaxiBtn3.on('tap', callTaxiEventHandler);

Require / Loader

to debug the loader, use build debug custom code: if we add utils/ folder to the app, update the classpath in the hidden .sencha/sencha.cfg file so the App can be built:

app.classpath=${app.dir}/app.js,${app.dir}/app,${app.dir}/utils
  • Ant build script build.xml

MVC

Models

sencha generate model $Model id:auto,name:string,price:float,
// creare un oggetto model
var s = Ext.create('App.model.Setting', { city: "Amsterdam"});
var r = setting.validate();//trigger validation
setting.set('country', 'NL');//setter

ref nel controller:

models:['$Model']

validation:

validations: [
  { field:'fieldName', type: 'presence', message:'provide field' }
]

Stores

store:app/store/$store.js

Ext.define('App.store.$Store', {
  extend: 'Ext.data.Store',
  config: {
    model: 'App.model.Cab',
    autoLoad: true // will load its data automatically after creation
  }
});

Stores can be written without models. You can write the model in- line in the store by just defining a fields array.

sono anche usati per filtrare i records

Ext.data.Store.filter(filters, [value], [anyMatch], [caseSensitive]);

App

incapsulate remote server connection

save your data model server side

Ext.define('SaveTest.model.Car', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      { name: 'id', type: 'int' },
      { name: 'brand', type:'string' },
    ]
  },
   App: {
    type: 'rest',// or ajax(will use POST insted of PUT/DELETE)
    format: 'php',//url extension, sometimes needed by services
    api: {
      create: 'cars/create',
      update: 'cars/update',
      read: 'cars/read',
      destroy: 'cars/delete'
    }
  }
});
// test code
car.save({
  success: function() {
    console.log('The car record is saved');
  },
  failure: function(){
    console.log('The car record could not be saved.');
  }
});

if you dont use a store, request the model via the ModelManager:

Ext.ModelManager.getModel('SaveTest.model.Car').load(1, {
  success: function(car){
    console.log("record: " + car.getId());
  },
  failure: function(){
    console.log("record could not be loaded");
  }
});

attenzione alle cross-domain restrictions @see jsonP

è possibile settare associazioni tra modelli

  • HasOne
  • HasMany
  • BelongsTo

client proxies: data cache use LocalStorage on the model:

 App: {
  type: 'localstorage',
  id: "Setting"
}
Ext.define('App.model.Setting', {
  extend: 'Ext.data.Model',
  requires: ['Ext.data.identifier.Uuid'],
  config: {
    idProperty: 'id',
    identifier: 'uuid',
    fields: [
      { name: 'id', type: 'auto' },
      { name: 'gps', type: 'boolean' },
      { name: 'city', type: 'string' },
      { name: 'country', type: 'string' }
    ],
    validations: [{
        type: 'presence',
        field: 'city',
        message: "Please provide a city."
      }, {
        type: 'presence',
        field: 'country',
        message: "Please provide a country."
    }],
     App: {
      type: 'localstorage',
      id: "Setting"
    }
  }
});

salvataggio su sql DB:

xt.define('App.store.Cabs', {
  extend: 'Ext.data.Store',
  requires: [ 'Ext.data. App.JsonP', 'Ext.data. App.Sql' ],
  config: {
    model: 'App.model.Cab',
    autoLoad: false,
    sorters: [{
      property: "name",
      direction: "ASC"
    }],
    grouper: { //group on the first character of Taxi name
      groupFn: function(record) {
        return record.get('name')[0].toUpperCase();
      }
    },
    //groupField: 'name', groupDir: 'DESC',
    filters: [{ //only display records that contain a phone number
      filterFn: function(item) {
        return item.get("phone").length > 0;
      }
    }],
     App: {
      type: 'sql',
      database: "__appname__",
      table: "Cabs"
    }
  }
});

controller

sencha generate controller $controller
Ext.define('App.controller.MyController', {
  extend: 'Ext.app.Controller',
  config: {
    models: ['$Model'],
    stores: ['$Store'],
    // ref to a component, equivalent to Ext.ComponentQuery.query('somextype');
    // access with this.getRefMain()
    refs: { refMain: 'somextype' },
    // event binding
    control: {
      'somextype' : {
        initialize: 'onInitMain',
        tap: 'onTapMain',
      }
    },
    onInitMain: function() {
      console.log("Initialize mainview");
    },
    onTapMain: function() {
      console.log("Tapped a button in mainview");
    }
  },
  launch: function(app) {}, // called when the application is launched
  init: function(){} // called during the application initialization
});

Components

docs

dialogs

Ext.Msg.alert('Title', 'The quick brown fox jumped over the lazy dog.',
    function(){ console.log(arguments);
});
 
Ext.Msg.prompt('Welcome', 'Please enter your name', function(btn, val){
    console.log(btn, val);
});
 
Ext.Msg.confirm('Reload', 'Do you want to reload the page?', function(buttonId) {
    if (buttonId === 'yes') {
      window.location.reload();
    }
});
 
var myBox1 = Ext.Msg.show({
  title: 'Address',
  message: 'Please enter your address:',
  width: 500,
  // Ext.MessageBox.OK (OK button with action UI skin)
  // Ext.MessageBox.YES (Yes button with action UI skin)
  // Ext.MessageBox.NO (No button)
  // Ext.MessageBox.CANCEL (Cancel button)
  // Ext.MessageBox.YESNOCANCEL (Cancel button, No button, and Yes button with action UI skin)
  // Ext.MessageBox.OKCANCEL (Cancel button, and OK button with action UI skin)
  // Ext.MessageBox.YESNO (No button, and Yes button with action UI skin)
  buttons: Ext.MessageBox.YESNOCANCEL,
  // Ext.MessageBox.ERROR (a round error sign)
  // Ext.MessageBox.INFO (a round info sign)
  // Ext.MessageBox.QUESTION (a question mark)
  // Ext.MessageBox.WARNING (a warning sign)
  iconCls: Ext.MessageBox.QUESTION,
  multiLine: true,
  prompt : { maxlength : 180, autocapitalize : true },
  fn: function(buttonId) {
      alert('You pressed the "' + buttonId + '" button.');
  }
});
 
 
var myBox2 = Ext.Msg.show({
  title: 'Ahoy!',
  message: 'Stop pirate, would ye like t\' proceed?',
  iconCls: Ext.MessageBox.ERROR,
  width: 200,
  buttons: [
    {text: 'Aye', itemId: 'yes', ui: 'action'},
    {text: 'Avast', itemId: 'no', ui: 'decline'}
  ],
  fn: function(buttonId) {
    alert('You pressed the "' + buttonId + '" button.');
  }
});
Ext.Viewport.setMasked({
  xtype: 'loadmask',
  indicator: true,
  message: 'Fetching Data...'
});
Ext.Viewport.unmask();

detail view:

Ext.define('App.view.DetailView', {
  extend: 'Ext.Container',
  xtype: 'detailview',
  requires: [ 'Ext.TitleBar', 'Ext.Button' ],
  config: {
    items: [
      {
        xtype: 'titlebar',
        ui: 'light',
        docked: 'top',
        title: 'App',
        items: [{
          iconCls: 'settings',
          ui: 'plain',
          align: 'right'
        }]
      }
    ],
    html: 'detail view'
  }
});

lists

list:

{
  xtype: 'list',
  store: {
    fields: ['name'],
    data: [
      {name: 'Leonardo'},
      {name: 'Donatello'},
      {name: 'Michelangelo'},
      {name: 'Raphael'}
    ]
  },
  itemTpl: '{name}',
  listeners: {
    refresh: function(list, index){},
    select: function(list, index){},
    deselect: function(list, index){
      Ext.Msg.alert('Selected!', 'You selected ' + record.get('name'));
    }
  }
}
var ref = Ext.Viewport.add({
    xtype: 'panel',
    centered: true,
    modal: true
});

any changes to the store are immediately reflected to the list on the screen

forms

sencha generate form -name MyForm -fields field,field2:textfield

use Ext.form.Panel as container

validation: usare il model

var model = Ext.create("MyApp.model.MyModel");
myForm.updateRecord(model);
var validationObj = model.validate();
if (!validationObj.isValid()) {
    validationObj.each(function(errorObj) {
        //loop through all errors
    }
} else {
    //form is valid, now do something!
    myForm.submit();
}

POST data through an AJAX call:

Ext.Ajax.request({
  url: 'data.php',
  jsonData : { city:"Amsterdam", country:"Netherlands" },
  success: function(response) {
    myForm.reset();
  },
  failure: function(response) {
    Ext.Msg.alert('Oops',result.responseText);ΓÇ¿
  }
});

themes + SASS/CSS

app.json file > css array > theme object
force a style: ~http://localhost/app/?platform=ie10