JavaScript for Flex Devs
-
Upload
aaronius -
Category
Technology
-
view
9.054 -
download
1
description
Transcript of JavaScript for Flex Devs
JavaScript
Software Engineer, Adobe Digital Marketing Suite We enable management, measurement, execution, and
optimization of digital advertising and marketing.
HELLO my name is
@AARONIUS AARONHARDY.COM ·
No hidden agenda
Single-Page Apps
DataGrid
DataGroup
List
CurrencyFormatter TabBar
Slider Panel
Accordion ViewStack
ToggleButtonBar
EventDispatcher ArrayCollection
Bindable
NumericStepper
DragManager
Effects
Main (Application)
Header (Group)
AccountInfo (Group)
Music (ViewStack)
Artist (Group)
Menu (ToggleButtonBar)
Discography (DataGroup)
Discussion (DataGroup)
Biography (Group)
Similar Artists (List)
Basic Form Elements Div
Span
Links
? ?
? ?
? Image
?
Bulleted Lists
<html> <head> <script> buckets of vomit buckets of vomit buckets of vomit buckets of vomit buckets of vomit </script> </head> <body> buckets of vomit buckets of vomit buckets of vomit buckets of vomit buckets of vomit </body> </html>
• IDEs • MVC • Widgets/Components • Dependency management • DOM manipulation • Templating • Testing • Utilities • JavaScript + HTML + CSS
Quality ide
Libraries
Rolling your own
jquery underscore.js
backbone.js requirejs
All MIT license (+ other options) and on GitHub
Learn Javascript
Never build large apps The secret to building large apps is NEVER build large apps. Break up your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application. – Justin Meyer
Admit what you don’t know
The key is to acknowledge from the start that you have no idea how this will grow. When you accept that you don’t know everything, you begin to design the system defensively. – Nicholas Zakas
Add "use strict"; before code or at beginning of function to help decrapify your code.
• Eliminates pitfalls by raising errors • Improves JS engine optimization • Prohibits syntax reserved for future JS versions * Won’t be used in examples to save slide space
JSLint or JSHint. • Run in IDE. • Run on command line. • Run in build scripts. • Copy-paste into web interface. • Use flags for configuration.
General community style: • Names of directories and files shall be lowercase and
hyphens shall be used to separate words. • Variable names shall be camelCase. • Classes (functions) to be instantiated shall start with a
capital letter. • Members intended to be private shall be prefixed with
an underscore.
Loose typing == more testing! • JsTestDriver • QUnit • Jasmine • Vows • …and bucket-loads more
jquery
• Not an application framework! • Abstracts the DOM
• Style • Position • Behavior • Animation • Creation/Removal
• AJAX requests • Utilities for objects, arrays, strings, etc. • “Framework” for UI widgets and utilities
Usage by Top Sites
http://trends.builtwith.com/javascript/jQuery
• Performance-tuned servers • Allows browser to have more concurrent connections • Cross-site caching
Toolbox of array, object, and function manipulation utilities.
var names = _.pluck( [{name: 'moe', age: 40}, {name: 'larry', age: 50}], 'name'); var person = _.extend({name: 'moe'}, {age: 50}); var uniques = _.union([1, 2, 3], [101, 2, 10], [2, 1]);
And bucket-loads more…
Old-school var tweetTemplate = '' + '<div>' + '<div style="float: left">' + '<img src="' + avatar + '"/>' + '</div>' + '<div style="margin-left: 60px">' + '<p>' + username + '</p>' + '<p>' + text + '</p>' + '</div>' + '<div>';
Old-school • HTML buried in logic (hard to re-skin) • Concatenation/escaping takes time and is error-prone • IDE might not code-hint or color-code properly • Boring and tedious
New-school <script type="text/template" id="tweet-template"> <div> <div style="float: left"> <img src="<%= avatar %>"/> </div> <div style="margin-left: 60px"> <p><%= username %></p> <p><%= text %></p> </div> </div> </script> <script> var tweet = {avatar: 'aaronius.jpg', alias: '@Aaronius', text: 'Honey roasted peanuts rock my sox.'}; var template = _.template($('#tweet-template').html()); var html = template(tweet); var element = $(html); </script>
• Mustache.js • Handlebars.js • Jade • Eco • …and bucket-loads more. Underscore.js templating is one of the best (though not as full-featured) and is a dependency of Backbone.js. You can use any templating engine.
backbone
Object A passes a function to object B and tells object B to call the function when X occurs. When X occurs, object B calls the function.
backbone.events
var baby = new Baby(); var changeDeuceTrap = function() { baby.deuces = 0; baby.cakeHoleShut = true; }; baby.on('turdDropped', changeDeuceTrap); Baby:
this.trigger('turdDropped');
Dad:
Mix into any object or class var baby = new Baby(); baby = _.extend(baby, Backbone.Events); baby.on(…); baby.trigger(…);
backbone.Model
Plain Old JavaScript Object var book = { title: 'A Tale of Two Cities', author: 'Charles Dickens', genre: 'historical', publisher: 'Chapman & Hall' };
How can another object know when one of book’s values changes? It can’t!
Eventful Backbone.Model var book = new Backbone.Model({ title: 'A Tale of Two Cities', author: 'Charles Dickens', genre: 'historical', publisher: 'Chapman & Hall' }); book.on('change:genre', function(model, genre) { alert('genre for ' + model.get('title') + ' changed to ' + genre); }); book.set({genre: 'social criticism'});
Extending Backbone.Model var Book = Backbone.Model.extend({ …custom model logic… }); var book = new Book({ title: 'A Tale of Two Cities', author: 'Charles Dickens', publisher: 'Chapman & Hall' });
Persistence HTTP Method URL Action
GET /books Retrieve all books
GET /books/10 Retrieve book with id == 10
POST /books Add a book
PUT /books/10 Update book with id == 10
DELETE /books/10 Delete book with id == 10
Persistence (save) var Book = Backbone.Model.extend({ urlRoot: '/books' }); var book = new Book({title: 'The Tragedy of the Commons'}); book.save();
Persistence (fetch) var Book = Backbone.Model.extend({ urlRoot: '/books' }); var book = new Book({id: 2}); var fetchSuccess = function() { alert(book.get('title')); }; book.fetch({success: fetchSuccess});
MODEL layersOpen
characterOpen magicWandOpen
colorOpen infoOpen
And more… • Validation • Extracting native object • Determine which attributes have changed • And bucket-loads more…
backbone.collection
Plain Old JavaScript Array: var books = [book1, book2, book3, book4];
How can another object know when an item is added or removed from this array? It can’t!
var goodEarth = new Backbone.Model(); var books = new Backbone.Collection(); books.on('add', function(book, books, options) { alert('Book ' + book.get('title') + ' added at index ' + options.at + '. ' + 'The collection now contains ' + books.length + ' models.'); }; books.on('remove', function(book, books, options) { alert('Book ' + book.get('title') + ' removed from index ' + options.index); } books.add(goodEarth); books.remove(goodEarth);
var books = new Backbone.Collection([taleOfTwoCities, goodEarth]); books.on('change:title', function(book, title) { alert('Book title changed from ' + book.previous('title') + ' to ' + title); }); goodEarth.set({title: 'Good Earth, The'});
Event Pass-through
Persistence (fetch) var Books = Backbone.Collection.extend({ model: Book, url: '/books' }); var books = new Books(); books.fetch();
Underscore mix-ins var titles = books.pluck('title');
each(), reduce(), find(), filter(), reject(), shuffle(), without() and bucket-loads more!
And more… • Sorting • Resetting • Retrieving model by id or index • Custom loading and parsing • …and bucket-loads more!
Backbone view
Tweet Model
Tweet Collection
Tweet
Tweet
Tweet
Tweet
Tweet
Tweet
Stats Model
var Tweet = Backbone.Model.extend({ …view logic… }); var TweetRow = Backbone.View.extend({ …view logic… }); var tweet = new Tweet({ avatar: 'aaronius.jpg', alias: '@Aaronius', text: 'Honey roasted peanuts rock my sox.' }); var row = new TweetRow({ model: tweet });
var TweetRow = Backbone.View.extend({ _template: _.template($('#tweet-row-template').html()), initialize: function() { this.render(); }, render: function() { this.$el.html(this._template(this.model.toJSON())); return this; } });
<script type="text/template" id="tweet-row-template"> <div style="float: left"><img src="<%= avatar %>"/></div> <div style="margin-left: 60px"> <p><%= username %></p> <p><%= text %></p> </div> </script>
Backbone router
backbone extensions
requirejs
Must we be restricted to working inside a few monstrous spaghetti files? NO! Then why are they so prevalent? • “That’s the way JavaScript has been done in the past.” • “Loading many JavaScript files requires many HTTP
requests resulting in longer load times.” • “Dependency management is hard in JavaScript.” We can do better!
// What are the dependencies here!? // What if a new employee had to re-order for some reason!? <script src="script3.js"></script> <script src="script1.js"></script> <script src="script7.js"></script> <script src="script6.js"></script> <script src="script4.js"></script> <script src="script5.js"></script> <script src="script9.js"></script> <script src="script8.js"></script> <script src="script10.js"></script> <script src="script2.js"></script>
Old school
script4.js
script5.js script6.js script9.js
script10.js script7.js
script8.js script1.js
script2.js
script3.js
ServerJS CommonJS Module Async module definition (AMD) RequireJS
define({ title: "My Sister's Keeper", publisher: "Atria" });
book.js
define([ 'book' ], function(book) { return { listBook: function() { alert(book.title); } }; });
bookshelf.js
<!DOCTYPE html> <html> <head> <title>RequireJS Example</title> <script data-main="js/main“ src="js/libs/require.js"></script> </head> <body/> </html>
index.html
require([ 'bookshelf' ], function(bookshelf) { bookshelf.listBook(); });
main.js
<span class="label">Title:</span> <span class="value"><%= title %></span><br/> <span class="label">Author:</span> <span class="value"><%= author %></span><br/> <span class="label">Genre:</span> <span class="value"><%= genre %></span>
templates/book.tpl.html
define([ 'backbone', 'underscore', 'text!templates/book.tpl.html' ], function(Backbone, _, template) { return Backbone.View.extend({ _template: _.template(template), initialize: function() { this.render(); }, render: function() { var nativeBook = this.model.toJSON(); var html = this._template(nativeBook); this.$el.html(html); return this; } }); });
book-view.js
define([ 'backbone', 'underscore', 'book-view' ], function(Backbone, _, BookView) { return Backbone.View.extend({ … create child book views as necessary … … new BookView() … }); });
bookshelf-view.js
requirejs optimization
Thank you!
@Aaronius aaronhardy.com
encore (stuff I fit in if time allows)
Persistence with custom url pattern: var Book = Backbone.Model.extend({ urlRoot: '/books', url: function() { return this.urlRoot + '?id=' + this.get('id'); } }) var book = new Book({id: 2}); book.fetch();
var App = Backbone.View.extend({ initialize: function() { this.render(); }, render: function() { var tweet = new Tweet({ avatar: 'avatar.jpg', username: '@Aaronius', text: 'Honey roasted peanuts rock my sox.' }); var row = new TweetRow({ model: tweet }); this.$el.append(row.$el); return this; } }); var app = new App({el: $('body')});
var DocumentView = Backbone.View.extend({ events: { "dblclick" : "open", "click .icon.doc" : "select", "mouseover .title .date" : "showTooltip" }, render: function() { ... }, open: function() { ... }, select: function() { ... }, showTooltip: function() { ... }, });
<script type="text/javascript"> $(function() { var AppRouter = Backbone.Router.extend({ routes: { "shirt/id/:id": "showShirt" }, showShirt: function(id) { alert('Show shirt with id ' + id); } }); var appRouter = new AppRouter(); Backbone.history.start(); }); </script> <a href="#shirt/id/5">Shirt with id of 5</a><br> <a href="#shirt/id/10">Shirt with id of 10</a><br> <a href="#shirt/id/15">Shirt with id of 15</a><br>
<script type="text/javascript"> $(function() { var AppRouter = Backbone.Router.extend({ routes: { ":product/:attribute/:value": "showProduct" }, showProduct: function(product, attribute, value) { alert('Show ' + product + ' where ' + attribute + ' = ' + value + '.'); } }); var appRouter = new AppRouter(); Backbone.history.start(); }); </script> <a href="#shoe/size/12">Size 12 shoes</a><br> <a href="#shirt/id/5">Shirt with id of 5</a><br> <a href="#hat/color/black">Black hats</a>
var AppRouter = Backbone.Router.extend({ routes: { ":product/:attribute/:value": "showProduct" } }); var MyView = Backbone.View.extend({ initialize: function(options) { options.router.on('route:showProduct', this._showProduct); } _showProduct: function(product, attribute, value) { alert('Update to show ' + product + ' where ' + attribute + ' = ' + value + '.'); } }); var appRouter = new AppRouter(); var view = new MyView({router: appRouter}); Backbone.history.start();
Fragment Identifier • Using URLs in unintended ways • Dependent on JavaScript • Controversial • Used by many popular sites pushState aka HTML5 History • The bee’s knees • Allows changing of core url without changing pages • Long-term solution • Browser support lacking (guess which browser)