senchacon 2016: handle real-world data with confidence - fredric berling

83
Handle real world data with confidence. Fredric Berling @FredricBerling [email protected]

Upload: sencha

Post on 12-Jan-2017

79 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Handle real world data with confidence.

Fredric Berling@FredricBerling

[email protected]

Page 2: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

• Reading & saving simple dataWhen we live in a perfect world. Domain model is perfect. We decide everything.

• Real world data and demandsStill simple data but add real world scenarios and demands. Learn how to configure to cope with it.

• AssociationsHow to handle associate data.

• Multi model scenariosLearn about Ext.data.Session to track changes in multiple models and associations.

• Errors

2

Agenda

Page 3: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Reading simple data

Page 4: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Model View ViewModel ViewController

Sencha MVVM recap

Page 5: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Model View ViewModel ViewController

Sencha MVVM recap

Ext.data.Model Ext.Component Ext.app.ViewModel Ext.app.ViewController

Page 6: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Model View ViewModel ViewController

Sencha MVVM recap

Ext.data.Model Ext.Component Ext.app.ViewModel Ext.app.ViewController

View Package

Page 7: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

CRM.view.Viewport CRM.view.ViewportModel CRM.view.ViewportController

Our sample application View Package

Page 8: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling
Page 9: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

resources/getCompanyById.json

• Id field is lowercase

• Date is formatted in a understandable format

{ id : 100, companyName : 'Ikea', address : 'Storgatan 1', city : 'Stockholm', country : 'Sweden',

updatedDate : '1973-11-17 05:30'}

Page 10: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

The data model

• Fields config are not really needed for simple data types.

.. except date

• Simple proxy config

Ext.define('CRM.model.CompanyProfile', { extend: 'Ext.data.Model', fields:[ {

name : 'updatedDate',type : 'date’

} ], proxy: {

url : 'resources/getCompanyById.json' }});

Page 11: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Data loading

• linkTo function

Creates a link to a record by loading data from configured proxy and setting it on the view model with a simple name.

"companyProfile" will be our reference in all bindings referring to company data record.

Ext.define('CRM.view.ViewportController', { extend : 'Ext.app.ViewController', alias : 'controller.viewportcontroller',

init: function() {this.getViewModel().linkTo('companyProfile',{

type : 'CRM.model.CompanyProfile', id : 100 }); }});

Page 12: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Ext.define('CRM.view.Viewport', { extend : 'Ext.Container', viewModel : 'viewportmodel', controller : 'viewportcontroller', padding : 20, items: [ { xtype : 'textfield', fieldLabel : 'Company name', bind : '{companyProfile.companyName}' }, { xtype : 'textfield', fieldLabel : 'Address', bind : '{companyProfile.address}' }

...

]});

Bind fields to view

Page 13: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Ext.define('CRM.view.Viewport', { extend : 'Ext.Container', viewModel : 'viewportmodel', controller : 'viewportcontroller', padding : 20, items: [ '''

]});

• viewModel- connects this view to the viewmodel with

alias "viewportmodel"

• controller- connects this view to the viewController

with alias "viewportcontroller"

View configs

Page 14: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoSimple read

Page 15: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Saving simple data

Page 16: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Save buttonExt.define('CRM.view.ViewportController', { extend : 'Ext.app.ViewController', alias : 'controller.viewportcontroller', onSave:function(){ this.getViewModel().get('companyProfile').save(); }});

Ext.define('CRM.view.Viewport', { extend : 'Ext.Panel', viewModel : 'viewportmodel', controller : 'viewportcontroller', padding : 20, buttons:[ { text:'Save', handler:'onSave' } ], items: [ { xtype : 'textfield', fieldLabel : 'Company name', bind : '{companyProfile.companyName}' } ...

Save function

Page 17: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoSimple Read/Save

Page 18: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

HTTP Post{ "id": 100, "companyName": "Ikea AB", "address": "Storgatan 1", "city": "Malmö", "country": "Sweden", "updatedDate": "2016-08-23 19:47"}

{ id:100, companyName:"Ikea AB"}

Required response

Page 19: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Noteworthy

• Only dirty field data was sent.

• Id was always sent

• Same URL as read

• Response data was automatically realized in view

19

Page 20: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Real world data and demands

Page 21: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Real world data from Customer

• Observations- id field is not "id"

{ _id : "57bb5d639baeb676ced2b0de", companyName : 'Ikea', address : {

street : 'Storgatan 1',city : 'Stockholm',country : 'Sweden'

}, updatedDate : '2016-08-23T21:26:08.358Z'

}

Page 22: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Real world data from Customer

• Observations- id field is not "id"

- Some data resides is in objects

{ _id : "57bb5d639baeb676ced2b0de", companyName : 'Ikea', address : {

street : 'Storgatan 1',city : 'Stockholm',country : 'Sweden'

}, updatedDate : '2016-08-23T21:26:08.358Z'

}

Page 23: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Real world data from Customer

• Observations- id field is not "id"

- Some data resides is in objects

- Date format

{ _id : "57bb5d639baeb676ced2b0de", companyName : 'Ikea', address : {

street : 'Storgatan 1',city : 'Stockholm',country : 'Sweden'

}, updatedDate : '2016-08-23T21:26:08.358Z'

}

Page 24: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Scenarios/Customer demands

• Id must be "_id". We are using Mongo DB.

• Updated date should ignore timezones

• The exact same JSON structure must be sent on save.

• Always send all data on save.

• Save must be on separate url.

24

Page 25: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Specify your own id field

idPropertyConsider doing this in abstract base class or even override Ext.data.Model.

Ext.define('CRM.model.CompanyProfile', { extend: 'Ext.data.Model', idProperty:'_id',

fields:[ {

name : 'updatedDate',type : 'date’

} ], proxy: {

url : 'resources/getCompanyById.json' }});

Page 26: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Read data from nested objects

mappingSpecify a dot notated path in the fields array of data model.

fields:[ { name : 'street', mapping : 'address.street', type : 'string' }, { name : 'city', mapping : 'address.city', type : 'string' }, { name : 'country', mapping : 'address.country', type : 'string' },

... ],

Page 27: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Send all fields on save

writeAllFieldsProxy writer configs to ensure all fields will be sent.

proxy: {type : 'ajax',url : '/companies',reader:{

type:'json'},

writer:{type : 'json',writeAllFields : true

}}

Page 28: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Keep object nesting on save { _id : "57bb5d639baeb676ced2b0de", companyName : 'Ikea', address : {

street : 'Storgatan 1',city : 'Stockholm',

country: 'Sweden' },

updatedDate : '2016-08-23T21:26:08.358Z'}

Page 29: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Keep object nesting on save

expandDataMakes sure the mapped field data sent back is in a nested format.

namePropertyProperty used to read the key for each value that will be sent to the server.

proxy: {type : 'ajax',url : '/companies',reader:{

type:'json'},

writer:{type : 'json',writeAllFields : true,nameProperty : 'mapping',expandData : true

}}

Page 30: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoReal world data

Page 31: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Date should not add timezone

By default you will get a localized date in the current browsers timezone

{ _id : "57bb5d639baeb676ced2b0de", companyName : 'Ikea', address : {

street : 'Storgatan 1',city : 'Stockholm',

country: 'Sweden' },

updatedDate : '2016-08-23T21:26:08.358Z'}

Page 32: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

dateFormat

Adding the exact date format will make sure date is not altered.

fields:[ ''' { name:'updatedDate', type:'date', dateFormat:'Y-m-d\\TH:i:s.u\\Z' }],

Page 33: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Read/Update on separate Url´s

Instead of using the proxy standard url config, we change to the more versatile api config

proxy: { type : 'ajax', url : '/companies' reader:{ type:'json' }, writer:{ type : 'json', nameProperty : 'mapping', writeAllFields : true, expandData : true } }

Page 34: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Read/Update on separate Url´s

Instead of using the proxy standard url config, we change to the more versatile api config

proxy: { type : 'ajax', api : { read: '/companies', update: '/updateCompanies' }, reader:{ type:'json' }, writer:{ type : 'json', nameProperty : 'mapping', writeAllFields : true, expandData : true } }

Page 35: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Associations

Page 36: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Associated licenses{ "_id": "57bf32fe9baeb676ced2b0e1", "companyName": "Ikea", "address": { "street": "Storgatan 3", "city": "Malmö", "country": "Sweden" }, "updatedDate": "2016-08-25T18:51:39.671Z", "licenses": [ { "id": 1, "product": "ExtJS 2.1", "amount": 5 }, { "id": 2, "product": "ExtJS 4.1", "amount": 5 }, .......

]

Page 37: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

License references Company Ext.define('CRM.model.License', { extend: 'Ext.data.Model', idgen:'uuid', idProperty:'_id', fields:[ {name:'product', type:'string'}, {name:'amount', type:'float'}, {

name: 'companyId',reference: {

parent : 'CRM.model.CompanyProfile',inverse : {

role : 'licenses'}

} }

Company

License

License

License

Page 38: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

License references Company Ext.define('CRM.model.License', { extend: 'Ext.data.Model', idgen:'uuid', idProperty:'_id', fields:[ {name:'product', type:'string'}, {name:'amount', type:'float'}, {

name: 'companyId',reference: {

parent : 'CRM.model.CompanyProfile',inverse : {

role : 'licenses'}

} }

Company

License

License

License

Page 39: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

License references Company Ext.define('CRM.model.License', { extend: 'Ext.data.Model', idgen:'uuid', idProperty:'_id', fields:[ {name:'product', type:'string'}, {name:'amount', type:'float'}, {

name: 'companyId',reference: {

parent : 'CRM.model.CompanyProfile',inverse : {

role : 'licenses'}

} }

Company

License

License

License

Page 40: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Useful?

• Automatically created stores.Company model function licenses() will return accociated licenses.

var viewModel = this.getViewModel(),companyProfile = viewModel.get('companyProfile'),store =

companyProfile.licenses();

Page 41: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Useful?

• Automatically created stores.Company model function licenses() will return accociated licenses.

• License function getCompany() will return Company model.

var viewModel = this.getViewModel(),companyProfile = viewModel.get('licenses'),store =

companyProfile.licenses(),firstLicense = store.first(),

console.log(firstCompany.getCompany());

// Returns companyProfile

Page 42: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Useful?

• Automatically created stores.Company model function licenses() will return accociated licenses.

• License function getCompany() will return Company model.

• Nice bindings. companyProfile.licenses references the store

{xtype : 'grid',plugins : 'cellediting',columnLines:true,bind : {

store:'{companyProfile.licenses}'},columns:[

{text: 'Licence', dataIndex:'product'}, {text: 'Amount', dataIndex:'amount'} ]}

Page 43: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

• Inline associationsLicence data is array in company JSON and should be saved in same call as company profile.

• Proxy/Remote associations Licence data is fetched from its own web service and CRUD operations are handled here

43

Possible business scenarios

Page 44: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Inline associations{ "_id": "57bf32fe9baeb676ced2b0e1", "companyName": "Ikea", "address": { "street": "Storgatan 3", "city": "Malmö", "country": "Sweden" }, "updatedDate": "2016-08-25T18:51:39.671Z", "licenses": [ { "id": 1, "product": "ExtJS 2.1", "amount": 5 }, { "id": 2, "product": "ExtJS 4.1", "amount": 5 }, .......

]

GET

Page 45: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Inline associations{ "_id": "57bf32fe9baeb676ced2b0e1", "companyName": "Ikea", "address": { "street": "Storgatan 3", "city": "Malmö", "country": "Sweden" }, "updatedDate": "2016-08-25T18:51:39.671Z", "licenses": [ { "id": 1, "product": "ExtJS 2.1", "amount": 5 }, { "id": 2, "product": "ExtJS 4.1", "amount": 5 }, .......

]

POST

Page 46: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

allDataOptions

Proxy writer needs to be told to save associated data.

.

proxy: { type : 'ajax',

api : { read: '/companies',

update: '/updateCompanies'},

writer:{ type : 'json', nameProperty : 'mapping', writeAllFields : true, expandData : true, allDataOptions :{ associated:true } } }

Company model

Page 47: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Associated store data is not realized on save.

Live example

47

Page 48: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Saving associated data in a big JSON is generally a bad idea

The associated data is probably saved in a table in a database

Empty arrays will have to force delete

48

Page 49: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

3 Possible solutions

• Re-load all data after successful save- extra call

- safe

onSave:function(){this.getViewModel().get('companyProfile').save({

callback:function(){this.getViewModel().linkTo('companyProfile',{

type : 'CRM.model.CompanyProfile', id : '57bf32fe9baeb676ced2b0e1' });

},scope:this

}); }

Page 50: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

3 Possible solutions

• Re-load all data after successful save- extra call

- safe

• Forced commitChanges()- no extra call

- response data is ignored.

onSave:function(){var store = this.getViewModel().get('companyProfile.licenses')this.getViewModel().get('companyProfile').save({

callback:function(){store.commitChanges(); },

},scope:this

});}

Page 51: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

3 Possible solutions

• Re-load all data after successful save- extra call

- safe

• Forced commitChanges()- no extra call

- response data is ignored.

• Code around it- complex

Page 52: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

What happens if license array is missing?

Demo!

52

Page 53: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Associated stores will default to a ajax proxy.

Avoid remote reads by changing this to a memory proxy.

Ext.define('CRM.model.License', { extend: 'Ext.data.Model', fields:[ {name:'product', type:'string'}, {name:'amount', type:'float'},

{ name: 'companyId', reference: { parent : 'CRM.model.CompanyProfile', inverse : { role:'licenses', storeConfig:{ proxy:{ type:'memory' } } }

} . . .

Store config

Page 54: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Proxy/Remote associationsExt.define('CRM.view.ViewportController', { extend : 'Ext.app.ViewController', alias : 'controller.viewportcontroller',

onSave:function(){var vm = this.getViewModel();

vm.get('companyProfile').save(); vm.get('companyProfile.licenses').sync(); },

...

• Call save() on model.

• Call sync() on stores.

Page 55: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Proxy/Remote associationsExt.define('CRM.model.License', {

extend: 'Ext.data.Model',idProperty:'_id',fields:[..],proxy: {

type : 'ajax',api : {

read: '/licenses',create : '/addLicenses',update : '/updateLicenses',destroy : '/deleteLicenses'

}, writer:{

type : 'json', writeAllFields : true } }});

• Specify all api´s on associated data model

Page 56: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Proxy/Remote associations{

xtype: 'grid',plugins: 'cellediting',columnLines: true,tbar: [{text: 'Add', handler: 'addLicense'}],bind: {

store: '{companyProfile.licenses}'}

},

• Associated data will only load if store is used in a binding

Page 57: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Proxy/Remote associations

filter:[{

"property":"companyId",

"value":"57bcbaa29baeb676ced2b0e0","exactMatch":true

}]

• On read, a filter will be posted.- property is the reference name,

- value is the reference Id

Backend must honor this filter and return the correct subset.

• Live Example

HTTP GET /licenses

Page 58: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Multi models scenarios

Page 59: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

59

Page 60: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Ext.data.Session

The primary job of a Session is to manage a collection of records of many different types and their associations. This often starts by loading records when requested and culminates when it is time to save to the

60

Page 61: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

View with multiple models

61

Company

Licenses

Pool cars

Feedback

Page 62: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

View with multiple models

62

Company

Licenses

Pool cars

Feedback

Data model + association

Page 63: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

View with multiple models

63

Company

Licenses

Pool cars

Feedback

Data model + association

Store

Page 64: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

View with multiple models

64

Company

Licenses

Pool cars

Feedback

Data model + association

Store

Data model

Page 65: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

• getChanges()- Returns an object describing all of the modified fields, created or dropped records

maintained by this session.

- Used to track if ANY data is dirty

• getSaveBatch()- Returns an Ext.data.Batch containing the Ext.data.operation.Operation instances that are

needed to save all of the changes in this session

65

Session features

Page 66: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

One point save onSave:function(){if(this.getSession().getChanges()){

this.getSession().getSaveBatch().start();}

},

Page 67: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Enable session Ext.define('CRM.view.Viewport', { extend : 'Ext.Panel', viewModel : 'viewportmodel', controller : 'viewportcontroller', bodyPadding : 20, session:true, buttons:[ { text:'Save', handler:'onSave' } ], layout:{ type:'vbox', align:'stretch' }, . . . .

• Enable session in view

Page 68: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Enable session var store = this.getViewModel().getStore('cars');

store.setSession(this.getSession());• Enable session in view

• Add stores to session

Page 69: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Enable session CRM.model.Feedback.load('57d072659baeb676ced2b0e5',{

success:function(record){this.getViewModel().set('feedback', record);

},scope:this

}, this.getSession());

• Enable session in view

• Add stores to session

• Provide session in model load

Page 70: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Enable session this.getViewModel().linkTo('companyProfile',{type : 'CRM.model.CompanyProfile',id : '57bcbaa29baeb676ced2b0e0'

});• Enable session in view

• Add stores to session

• Provide session in model load

• Use LinkTo- uses session from viewModel

Page 71: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoMulti Model

Page 72: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Sending extra parameters

Page 73: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Extra proxy parameters proxy: { type : 'ajax', extraParams:{ appName:'CRM' }, api : {

read: '/companies', update: '/updateCompanies' }, reader:{ type:'json' }, writer:{ type : 'json', } }

• Set in proxy using extraParams config

Page 74: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Extra proxy parameters var proxy = Ext.ClassManager.get(<class>).getProxy();

proxy.setExtraParam('appName', 'CRM'); • Set in proxy using extraParams config

• Set from controller using the setExtraParam function

Page 75: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Operation parameters vm.get('companyProfile').save({params:{

appHeight:this.getView().getHeight();}

});vm.get('companyProfile.licenses').sync({

params:{appHeight:this.getView().getHeight();

}});

• Send params config into operational methods,

Page 76: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoExtra Parameters

Page 77: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Errors with messages

Page 78: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Handle error response { "IsSuccess": false, "ErrorMessage": "Missing company"}Error returned from the server in a json

response when logic fails or data is missing.

Page 79: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Configure the proxy reader proxy: {type : 'ajax',api : {

read: '/companies',update: '/updateCompanies'

},reader:{

type:'json',successProperty:'IsSuccess',messageProperty:'ErrorMessage'

}

. . .

• successProperty

The property indicating that the operation was successful or not.

• messageProperty

The property where the error message is returned.

Page 80: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Listen for proxy exception Ext.mixin.Observable.observe(Ext.data.proxy.Server);

Ext.data.proxy.Server.on('exception',function(proxy, resp, operation){

Ext.Msg.alert('Error', operation.getError());

});

• exception- fires when "success" is false

- fires on HTTP exceptions

• operation.getError()- returns messageProperty content

Page 81: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Live demoError handling

Page 82: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling

Please Take the Survey in the Mobile App

• Navigate to this session in the mobile app

• Click on “Evaluate Session”

• Respondents will be entered into a drawing to win one of five $50 Amazon gift cards

Page 83: SenchaCon 2016: Handle Real-World Data with Confidence - Fredric Berling