sencha class system, jacky nguyen
DESCRIPTION
Ext JS 4.x and Sencha Touch 2.0 went through a huge refactoring from the ground up with the new class system. It combines the familiar class-based programming style with the dynamic nature of JavaScript into a robust architecture which stands behind every single class written in the frameworks. This session will give you a complete picture of everything our new foundation has to offer as well as their benefits and best practices.Jacky Nguyen is one of the core engineers behind Sencha frameworks. With his well-rounded knowledge and experiences on both server-side and client-side development as well as industry best practices, he is responsible for designing and improving Sencha frameworks’ overall architectures. He was the author of MooTouch, which has become a part of Sencha Labs. Jacky is passionate about bringing innovation to software development and developers.TRANSCRIPT
Wednesday, November 2, 11
Jacky Nguyen, SenchaThe Sencha Class [email protected] @nktpro
Wednesday, November 2, 11
Best User Experience
Wednesday, November 2, 11
BetterDeveloper Experience
Wednesday, November 2, 11
Class System
Event System
Data Package
Widgets & Layouts
...
Wednesday, November 2, 11
Predictability
Programmer Familiarity
Flexibility
Class-basedPrototype-based
SenchaClass System =
Wednesday, November 2, 11
Learn
Develop
Deploy
✓Consistent✓Familiar
✓DRY✓Debuggable✓Testable
✓Automatic dependency resolution
Developer ExperienceBetter Wednesday, November 2, 11
Coding Convention
Wednesday, November 2, 11
‣ Ext.chart.Label -> Ext/chart/Label.js‣ Ext.data.writer.Xml -> Ext/data/writer/Xml.js‣ MyApp.field.Password -> MyApp/field/Password.js
Namespacing & Code Organization
1. OrganizationName.group[.subgroup].ClassName2. One class per file3. File name matches class name
Wednesday, November 2, 11
Class Definition
Wednesday, November 2, 11
Ext.ns('My.sample');
My.sample.Person = Ext.extend(Object, { constructor: function(name) { this.name = name; },
walk: function(steps) { alert(this.name + ' is walking ' + steps + ' steps'); }});
The Good Old Ext.extend()
Wednesday, November 2, 11
Ext.define('My.sample.Person', { constructor: function(name) { this.name = name; },
walk: function(steps) { alert(this.name + ' is walking ' + steps + ' steps'); }});
var tommy = new My.sample.Developer('Tommy');tommy.walk(5); // alerts 'Tommy is walking 5 steps'
Introducing Ext.define()
Wednesday, November 2, 11
‣Class definition is more than just inheritance‣Automatic namespace allocation‣Classes are now aware of their own names‣Asynchronous vs. synchronous
Ext.define() vs Ext.extend()Ext.define('My.sample.Person', { constructor: function(name) { this.name = name; },
walk: function(steps) { alert(this.name + ' is walking ' + steps + ' steps'); }});
Wednesday, November 2, 11
Inheritance
Wednesday, November 2, 11
Ext.define('My.sample.Person', { constructor: function(name) { this.name = name; }, walk: function(steps) { alert(this.name + ' is walking ' + steps + ' steps'); }});
Ext.define('My.sample.Developer', { extend: 'My.sample.Person', code: function(language) { alert(this.name + ' is coding in ' + language); }});
var tommy = new My.sample.Developer('Tommy');tommy.walk(5); // 'Tommy is walking 5 stepstommy.code('JavaScript'); // 'Tommy is coding in JavaScript'
Inheritance
Wednesday, November 2, 11
Ext.define('My.sample.Developer', { extend: 'My.sample.Person', code: function(language) { /* ... */ }, walk: function(steps) { if (steps > 100) { alert('Are you serious? That's too far! I'm lazy...'); } else { } }});
var tommy = new My.sample.Developer('Tommy');tommy.walk(50); // alerts "Tommy is walking 50 steps"tommy.walk(101); // alerts "Are you serious? That's too far! I'm lazy..."
My.sample.Developer.superclass.walk.apply(this, arguments);this.callParent(arguments);
Inheritance
Wednesday, November 2, 11
Mixins
Wednesday, November 2, 11
Compose Songs
Play Instruments
Sing
Singer-songwriter
Composer
Performer
Mixins
Wednesday, November 2, 11
Ext.define('My.ability.Sing', { sing: function(songName) { alert("I'm singing " + songName); }});
Mixins
Ext.define('My.ability.PlayInstruments', { play: function(instrument) { alert("I'm playing " + instrument); }});
Ext.define('My.ability.ComposeSongs', { compose: function(songName) { alert("I'm composing " + songName); }});
Wednesday, November 2, 11
Ext.define('My.sample.Performer', { extend: 'My.sample.Person', mixins: { canSing: 'My.ability.Sing', canPlay: 'My.ability.PlayInstruments' }});
Mixins
Ext.define('My.sample.Composer', { extend: 'My.sample.Person', mixins: { canPlay : 'My.ability.PlayInstruments', canCompose: 'My.ability.ComposeSongs' }});
Ext.define('My.sample.SingerSongwriter', { extend: 'My.sample.Person', mixins: { canSing : 'My.ability.Sing', canPlay : 'My.ability.PlayInstruments', canCompose: 'My.ability.ComposeSongs' }});
Wednesday, November 2, 11
Ext.define('My.sample.Performer', { extend: 'My.sample.Person', mixins: { canSing: 'My.ability.Sing', canPlay: 'My.ability.PlayInstruments' },
sing: function() { alert("Here we go!");
this.mixins.canSing.sing.call(this);
alert("I'm done singing!"); }});
var jamie = new My.sample.Performer("Jamie");jamie.sing("November Rain"); // alerts "Here we go!" // alerts "I'm singing November Rain" // alerts "I'm done singing!";
Mixins
prototype of
Wednesday, November 2, 11
Dependency Management
Wednesday, November 2, 11
<!-- ... --><head> <!-- ... -->
<script>
</script></head><!-- ... -->
<script src="My/sample/Developer.js"></script><script src="My/sample/Location.js"></script><script src="My/sample/Person.js"></script>
var tommy = new My.sample.Developer('Tommy');var jamie = new My.sample.Performer('Jamie');
<script src="My/ability/Sing.js"></script><script src="My/ability/PlayInstruments.js"></script>
<script src="My/sample/Performer.js"></script>
var tommy = Ext.create('My.sample.Developer', 'Tommy');var jamie = Ext.create('My.sample.Performer', 'Jamie');
Dependency Management
<script src="ext-all-debug.js"></script><script src="ext.js"></script>
Wednesday, November 2, 11
<!-- ... --><head> <!-- ... --> <script src="ext.js"></script> <script>
</script></head><!-- ... -->
Ext.require([ 'My.sample.Developer', 'My.sample.Performer']);
Ext.onReady(function() { var tommy = new My.sample.Developer('Tommy'); var jamie = new My.sample.Performer('Jamie');});
Dependency Management
Wednesday, November 2, 11
Config
Wednesday, November 2, 11
Ext.define('My.sample.Person', { name: 'Anonymous', age: 0, gender: 'Male',
constructor: function(config) { config = config || {};
Ext.apply(this, config); }});
var robert = new My.sample.Person({ name: 'Robert', age: 21});
Config
Wednesday, November 2, 11
Ext.define('My.sample.Person', { name: 'Anonymous', age: 0, gender: 'Male',
constructor: function(config) { if (Ext.isObject(config)) { if (config.hasOwnProperty('name')) { this.name = config.name; } if (config.hasOwnProperty('age')) { this.age = config.age; } if (config.hasOwnProperty('gender')) { this.gender = config.gender; } } }});
Config
Wednesday, November 2, 11
Ext.define('My.sample.Person', { name: 'Anonymous', age: 0, gender: 'Male',
constructor: function(config) { if (Ext.isObject(config)) { if (config.hasOwnProperty('name')) { this.setName(config.name); } if (config.hasOwnProperty('age')) { this.setAge(config.age); } if (config.hasOwnProperty('gender')) { this.setGender(config.gender); } } },});
Config
Wednesday, November 2, 11
setName: function(name) { this.name = name; return this; }, getName: function() { return this.name; },
setAge: function(age) { this.age = age; return this; }, getAge: function() { return this.age; },
setGender: function(gender) { this.gender = gender; return this; }, getGender: function() { return this.gender; }
Wednesday, November 2, 11
Ext.define('My.sample.Person', { config: { name: 'Anonymous', age: 0, gender: 'Male' },
constructor: function(config) { this.initConfig(config); }});
var robert = new My.sample.Person({ name: 'Robert', age: 21});
robert.setName('Rob');alert(robert.getAge()); // alerts '21'robert.setAge(22);alert(robert.getAge()); // alerts '22'
Config
Wednesday, November 2, 11
Ext.define('My.sample.Developer', { extend: My.sample.Person, config: { level: 'Junior', location: { city: 'Unknown', state: 'Unknown', country: 'Unknown' } }});
var edward = new My.sample.Developer({ age: 26, level: 'Senior', location: { city: 'Palo Alto', state: 'CA', country: 'USA' }});
edward.setName('Edward');alert(edward.getLocation().city); // alerts 'Palo Alto'
Config: Deep Merging
Wednesday, November 2, 11
Before
Set
After
✓Validation✓Filtering✓Transformation
✓Actual assignment
✓Notification✓Updating dependencies
Config: Setter’s Process
Wednesday, November 2, 11
Config: Magic MethodsgetFoo()
setFoo()
processedValue = applyFoo(newValue, oldValue)
updateFoo(processedValue, oldValue)
this.foo = processedValueSet
Before
After
return this.foo
Wednesday, November 2, 11
Ext.define('My.sample.Person', { /* ... */ applyAge: function(age) { if (typeof age != 'number' || age < 0) { alert("Invalid age, must be a positive number"); return; } return age; }, updateAge: function(newAge, oldAge) { alert("Age has changed from " + oldAge + " to " + newAge); }});
var aaron = new My.sample.Person({ name: 'Aaron', age: -100}); // alerts "Invalid age, must be a positive number"
alert(aaron.getAge()); // alerts 0alert(aaron.setAge(35)); // alerts "Age has changed from 0 to 35"alert(aaron.getAge()); // alerts 35
Config
Wednesday, November 2, 11
Dependency Injection with Ext.factory()
Wednesday, November 2, 11
Ext.define('My.sample.Location', { config: { city: 'Unknown', state: 'CA', country: 'USA' }});
var nicolas = new My.sample.Developer({ location: { city: 'Palo Alto' }}); alert(nicolas.getLocation() instanceof My.sample.Location); //alerts truealert(nicolas.getLocation().getCity()); // alerts 'Palo Alto'
var nicolas = new My.sample.Developer({ location: new My.sample.Location({ city: 'Austin', state: 'TX' })});
alert(nicolas.getLocation().getState()); // alerts 'TX'
Dependency Injection
Wednesday, November 2, 11
nicolas.setLocation({ city: 'Austin'}); // nicolas.getLocation().setCity('Austin');
nicolas.setLocation(new My.sample.Location({ city: 'Austin', state: 'TX'}));
nicolas.setLocation(new My.sample.DetailedLocation({ longitude: -122.18235, latitude: 37.44923}));
Dependency Injection
Wednesday, November 2, 11
Ext.define('My.sample.Developer', { extend: 'My.sample.Person',
config: { level: 'Junior', location: {} },
applyLocation: function(location, currentLocation) { return Ext.factory( location, My.sample.Location, currentLocation ); }}); given value from setLocation()default Class existing instance
Ext.factory()
Wednesday, November 2, 11
Debugging
Wednesday, November 2, 11
Debugging: Nightmare
Wednesday, November 2, 11
Debugging: Dream Comes True
Wednesday, November 2, 11
Debugging: Dream Comes True
Wednesday, November 2, 11
Deployment
Wednesday, November 2, 11
Deployment
Ext.Loader.history === [ 'My.sample.Person', 'My.sample.Location', 'My.sample.Developer', 'My.ability.Sing', 'My.ability.PlayInstruments', 'My.ability.ComposeSongs', 'My.sample.Performer', 'My.sample.Composer', 'My.sample.SingerSongwriter', /* ... */ ];
Concatenate Minify app.js
http://www.sencha.com/products/sdk-tools
Wednesday, November 2, 11
Summary
Wednesday, November 2, 11
Thank you!
Wednesday, November 2, 11