view models and binding

Post on 17-Aug-2015

136 Views

Category:

Software

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Sencha Inc. ©2015

ViewModelsand Data Binding

Evan TrimboliSr. Software Engineer, Sencha

Sencha Inc. ©2015

Sencha Inc. ©2015

Why ViewModels

•Automate connection between data and view• Keep view in sync with data• Update data with user input

•Declarative instead of imperative

Sencha Inc. ©2015

MVVM

ViewViewMode

l Model

Business LogicandData

Presentation and view logic

Binding

Sencha Inc. ©2015

Binding

Sencha Inc. ©2015

Binding Essentials

•The “bind descriptor” is a value that describes the desired data.•The “bound value” is the desired data delivered by the ViewModel once it becomes available.•The bound value will be delivered again whenever it changes.

Sencha Inc. ©2015

Direct Binding

var vm = new Ext.app.ViewModel();

var b = vm.bind('{foo}', function (v) { console.log(v);});

vm.set('foo', 42); // b.setValue(‘42’);

b.destroy();

1

2

3

Bind descripto

r

Bound value

> 42

Binding instance

4

Marks dependencies as dirty

Sencha Inc. ©2015

Negated Binding

vm.bind('{!x}', function (v) { console.log(v);});

vm.set('x', 'world');

Bind descripto

r

> false

Sencha Inc. ©2015

Binding Templates

vm.bind('Hello {x:capitalize}', function (v) { console.log(v);});

vm.set('x', 'world');

Bind descripto

r

> Hello World

Sencha Inc. ©2015

Objects and Arrays

vm.bind({ x: 'x={x}', y: ['{y}'] }, function (v) { console.log(v);});

vm.set('x', 42);// wait for it...

vm.set('y', 13);

Bind descripto

r

> { x: 'x=42', y: [13] }

Sencha Inc. ©2015

Bind Optionsvm.set(‘foo’, {bar: 1});

vm.bind('{foo}', this.onFoo, this, { deep: true, single: true, twoWay: false});

// Without deep, foo won’t changevm.set(‘foo.bar’, 100);

Deliver changes to child object properties

Deliver just the initial

value (once available)

Don’t write-back changes, mark binding

readOnly

Sencha Inc. ©2015

Bind Concepts in Depth

•Bind descriptor – the data you want.•Bind token – the replaceable parts (“{foo}”) of a bind descriptor (single name or dot path).•Dot path – the traversal of objects and properties starting at the root of the ViewModel.•Bound value – the value delivered by the ViewModel as the result of a bind request.

Sencha Inc. ©2015

Bind Descriptor

•String with one token (“{foo}”) or direct bind.•String template (“Hello {foo}”).•Object whose values are bind descriptors. {foo: “{bar}”}•Array whose elements are bind descriptors.[“{foo}”, “{bar}”]

Sencha Inc. ©2015

Bind Tokens

•Simple identifiers (“{foo}”) to select top-level values in the ViewModel.•A “dot-path” to the desired value (such as “{ticket.reporter.name}”).•Formatters such as “Hi {foo:capitalize}” or “Amount {num:round(2)}”.•Negated token (“{!foo}”).

Sencha Inc. ©2015

Dot Path

•Normal object property.• Field from a data record (“{user.name}” not “{user.data.name}”).•The associated record of a “to-one” association (“{order.user.name}”).•The associated store of a “to many” association (“{user.orders}”).

Sencha Inc. ©2015

Bound Value

•The exact value from the VM if a “direct bind” (just one token).•The boolean negation of the value if a negated direct bind (such as “{!foo}”).•A string with all tokens replaced and formatted (such as “{num:round(2)}”).•An object or array with all property values or elements replaced by their bound value.•A store or record instance.

Sencha Inc. ©2015

Componentsand

ViewModels

Sencha Inc. ©2015

Config Binding

Ext.define('App.view.Main', { ...

items: [{ xtype: 'button', bind: { text: 'Sign out, {user.name}' } }]});

bind config

Sencha Inc. ©2015

Config Binding Options

Ext.define('App.view.Main', { ... items: [{ xtype: 'button', bind: { text: { bindTo: '{foo}', single: true } } }]});

Bind descripto

r

Bind options

Sencha Inc. ©2015

defaultBindProperty

[{ xtype: ‘button’, bind: ‘{theText}’ // text}, { xtype: ‘textfield’, bind: ‘{theValue}’ // value}, { xtype: ‘grid’, bind: ‘{theStore}’ // store}]

•Components have a default bind property, which maps to the most common usage for that component

Sencha Inc. ©2015

Two Way Binding

var p = new Ext.panel.Panel({ bind: { title: ‘{theTitle}’ }, viewModel: { data: {theTitle: ‘Foo’} }}); // Not twoWayBindable, won’t update VMp.setTitle(‘Bar’);

•Configs that cannot be changed by the user are typically not two way bindable by default.

Sencha Inc. ©2015

What ViewModel does that bind use?

Sencha Inc. ©2015

Component Tree

viewport panel toolbar button grid panel panel toolbar button

viewport panel toolbar button grid panel panel toolbar button

ViewModel

ViewModel

Sencha Inc. ©2015

ViewModel Inheritance

viewport

panelpanel

toolbar

button { bind: '{foo}'}

ViewModel

...

Sencha Inc. ©2015

ViewModel

Sencha Inc. ©2015

ViewModel Hierarchy

ViewModel

ViewModel

{ }

{ }

data

prototype

data

parent

Sencha Inc. ©2015

Populating ViewModels

•Direct set() calls•Configs• data• stores• formulas• links

Sencha Inc. ©2015

Data

Sencha Inc. ©2015

ViewModel – Data

Ext.define('App.view.main.Main’,{ extend: 'Ext.panel.Panel', xtype: 'main',

viewModel: { type: 'main', data: { foo: 42 } }});

Ext.define('App.view.main.MainModel', { extend: 'Ext.app.ViewModel',

alias: 'viewmodel.main',

data: { foo: 427, bar: ‘baz’ }});

Sencha Inc. ©2015

ViewModel – Data (2)

Ext.create({ xtype: 'main',

viewModel: { data: { foo: 42 } }});

ViewModel type comes

from the class

Data is merged, “bar” still

exists

Sencha Inc. ©2015

Stores

Sencha Inc. ©2015

ViewModel – Stores

Ext.define('App.view.main.MainModel’, { extend: 'Ext.app.ViewModel', alias: 'viewmodel.main',

stores: { bar: { model: 'User’, filters: [{ property: 'name', value: '{form.search}' }] }...

Object is a bind

descriptor

Sencha Inc. ©2015

ViewModel – Store Types

Ext.define('App.view.main.MainModel', { extend: 'Ext.app.ViewModel', alias: 'viewmodel.main',

stores: { bar: { type: 'personnel', ... } }});

Ext.define('App.store.Personnel', { extend: 'Ext.data.Store',

alias: 'store.personnel',

proxy: ...});

Sencha Inc. ©2015

Formulas

Sencha Inc. ©2015

ViewModel – Formulas

Ext.define('App.view.main.MainModel', { ...

formulas: { twice: function (get) { return 2 * get('x'); }, quad: function (get) { return 2 * get('twice'); }

Use the getter

Formulas can use each

other

Sencha Inc. ©2015

ViewModel – Two-Way Formulas

Ext.define('App.view.main.MainModel', { ...

formulas: { twice: { get: function (get) { return 2 * get('x'); }, set: function (v) { this.set('x', v / 2); } }

Called when

dependency

changes

this == VM

Sencha Inc. ©2015

Links

Sencha Inc. ©2015

ViewModel – Links

Ext.define('App.view.main.MainModel', { ...

links: { ticket: { type: 'Ticket', id: 427 }

Load record or request

from Session

Sencha Inc. ©2015

ViewModel – Links

Ext.create('App.view.form.Form', { ... viewModel: { links: { ticket: theTicketInstance, user: { type: ‘User’, create: { name: ‘Foo Bar’ } } } }}

Already loaded but may copy

Create a new instance.

May also be true to

create empty record

Sencha Inc. ©2015

ViewModel – Links (Ext JS 6)

Ext.define('App.view.main.MainModel', { ...

links: { foo: { a: '{some.object.foo}', b: '{other.thing.bar}' } }}

Object is a bind

descriptor

Sencha Inc. ©2015

The Scheduler

Sencha Inc. ©2015

Shared Scheduler

ViewModel

ViewModel

parent

Scheduler

Sencha Inc. ©2015

Scheduler

Binding[0]

items

Binding[X]

…Data dependencies

Execution

Scheduler

Sencha Inc. ©2015

Running The Scheduler

vm.bind(’Hello {type} {thing:capitalize}', function (v) { console.log(v);});

vm.set(‘thing', 'world');vm.set(‘type’, ‘cool’);

vm.notify(); Update all dirty bound values

Questions & Answers

Thank YOU!

Sencha Inc. ©2015

top related