• Stars
    star
    154
  • Rank 233,931 (Top 5 %)
  • Language
    JavaScript
  • Created about 13 years ago
  • Updated about 12 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Backbone.js + jQuery Mobile sample app using AMD for separation of modules, Require.js for dependency management + template externalisation and Underscore for templating

#Flickly Mobile

*This app was further refactored 21/12

A complete Backbone.js + jQuery Mobile sample app using AMD for separation of modules, Require.js for dependency management + template externalisation and Underscore for templating.

The app allows you to search for images using the Flickr API, lookup individual photos in more detail, bookmark any state for results or photos, supports pagination and more.

##Uses:

  • Backbone.js to aid application structure, routing
  • Underscore.js for micro-templating and utilities
  • Require.js and AMD for modular separation of components
  • Require.js text plugin to enable external templates
  • jQuery Mobile + jQuery for DOM manipulation, mobile helpers
  • Flickr API for data

Note: This application needs to be run on a HTTP server, local or otherwise. To remove this requirement, simply switch from using external templates via Require.js/the text plugin to inline ones.

##Snippets from the upcoming tutorial for this app: Writing Modular Desktop & Mobile Applications With Backbone.js

(in no particular order)

###Why use Require.js with Backbone?

In case you haven't used it before, Require.js is a popular script loader written by James Burke - a developer who has been quite instrumental in helping shape the AMD module format, which we'll discuss more shortly. Some of Require.js's capabilities include helping to load multiple script files, helping define modules with or without dependencies and loading in non-script dependencies such as text files.

So, why use Require.js with Backbone?. Although Backbone is excellent when it comes to providing a sanitary structure to your applications, there are a few key areas where some additional help could be used:

  1. Backbone doesn't endorse a particular approach to modular-development. Although this means it's quite open-ended for developers to opt for classical patterns like the module-pattern or object literals for structuring their apps (which both work fine), it also means developers aren't sure of what works best when other concerns come into play, such as dependency management.

Require.js is compatible with the AMD (Asynchronous Module Definition) format, a format which was born from a desire to write something better than the 'write lots of script tags with implicit dependencies and manage them manually' approach to development. In addition to allowing you to clearly declare dependencies, AMD works well in the browser, supports string IDs for dependencies, declaring multiple modules in the same file and gives you easy-to-use tools to avoid polluting the global namespace.

  1. Let's discuss dependency management a little more as it can actually be quite challenging to get right if you're doing it by hand. When we write modules in JavaScript, we ideally want to be able to handle the reuse of code units intelligently and sometimes this will mean pulling in other modules at run-time whilst at other times you may want to do this dynamically to avoid a large pay-load when the user first hits your application.

Think about the GMail web-client for a moment. When users initially load up the page on their first visit, Google can simply hide widgets such as the chat module until a user has indicated (by clicking 'expand') that they wish to use it. Through dynamic dependency loading, Google could load up the chat module only then, rather than forcing all users to load it when the page first initializes. This can improve performance and load times and can definitely prove useful when building larger applications.

I've previously written a detailed article covering both AMD and other module formats and script loaders here (http://addyosmani.com/writing-modular-js) in case you'd like to explore this topic further. The takeaway is that although it's perfectly fine to develop applications without a script loader or clean module format in place, it can be of significant benefit to consider using these tools in your application development.

###A brief tutorial on writing AMD modules with Require.js

As discussed above, the overall goal for the AMD format is to provide a solution for modular JavaScript that developers can use today. The two key concepts you need to be aware of when using it with a script-loader are a define() method for facilitating module definition and a require() method for handling dependency loading. define() is used to define named or unnamed modules based on the proposal using the following signature:

define(
    module_id /*optional*/, 
    [dependencies] /*optional*/, 
    definition function /*function for instantiating the module or object*/
);

As you can tell by the inline comments, the module_id is an optional argument which is typically only required when non-AMD concatenation tools are being used (there may be some other edge cases where it's useful too). When this argument is left out, we call the module 'anonymous'. When working with anonymous modules, the idea of a module's identity is DRY, making it trivial to avoid duplication of filenames and code.

Back to the define signature, the dependencies argument represents an array of dependencies which are required by the module you are defining and the third argument ('definition function') is a function that's executed to instantiate your module. A barebone module (compatible with Require.js) could be defined using define() as follows:

// A module ID has been omitted here to make the module anonymous

define(['foo', 'bar'], 
    // module definition function
    // dependencies (foo and bar) are mapped to function parameters
    function ( foo, bar ) {
        // return a value that defines the module export
        // (i.e the functionality we want to expose for consumption)
    
        // create your module here
        var myModule = {
            doStuff:function(){
                console.log('Yay! Stuff');
            }
        }

        return myModule;
});

require() on the other hand is typically used to load code in a top-level JavaScript file or within a module should you wish to dynamically fetch dependencies. An example of its usage is:

// Consider 'foo' and 'bar' are two external modules
// In this example, the 'exports' from the two modules loaded are passed as
// function arguments to the callback (foo and bar)
// so that they can similarly be accessed

require(['foo', 'bar'], function ( foo, bar ) {
        // rest of your code here
        foo.doSomething();
});

Defining AMD Modules Using Require.js

//Todo: explain and demonstrate how to wrap views etc. inside AMD modules and how that all works.

require(['app/myModule'], 
    function( myModule ){
        // start the main module which in-turn
        // loads other modules
        var module = new myModule();
        module.doStuff();
});

Modular desktop applications with Require.js and Backbone

My side-project TodoMVC now includes a modular Backbone example using AMD and Require.js (thanks to Thomas Davis) for anyone wishing to look at a sample without any of the jQuery Mobile code included:

https://github.com/addyosmani/todomvc/tree/master/todo-example/backbone+require

This covers how to wrap your views, models, modules etc. using AMD and also cleanly demonstrates handling dependency management as well as Flickly does.

###External [Underscore/Handlebars/Mustache] templates using Require.js

Moving your [Underscore/Mustache/Handlebars] templates to external files is actually quite straight-forward. As this application makes use of Require.js, I'll discuss how to implement external templates using this specific script loader.

RequireJS has a special plugin called text.js which is used to load in text file dependencies. To use the text plugin, simply follow these simple steps:

  1. Download the plugin from http://requirejs.org/docs/download.html#text and place it in either the same directory as your application's main JS file or a suitable sub-directory.

  2. Next, include the text.js plugin in your initial Require.js configuration options. In the code snippet below, we assume that require.js is being included in our page prior to this code snippet being executed. Any of the other scripts being loaded are just there for the sake of example.

require.config( {
    paths: {
        'backbone':         'libs/AMDbackbone-0.5.3',
        'underscore':       'libs/underscore-1.2.2',
        'text':             'libs/require/text',
        'jquery':           'libs/jQuery-1.7.1',
        'json2':            'libs/json2',
        'datepicker':       'libs/jQuery.ui.datepicker',
        'datepickermobile': 'libs/jquery.ui.datepicker.mobile',
        'jquerymobile':     'libs/jquery.mobile-1.0'
    },
    baseUrl: 'app'
} );
  1. When the text! prefix is used for a dependency, Require.js will automatically load the text plugin and treat the dependency as a text resource. A typical example of this in action may look like..
require(['js/app', 'text!templates/mainView.html'],
	function(app, mainView){
		// the contents of the mainView file will be
		// loaded into mainView for usage.
	}
);
  1. Finally we can use the text resource that's been loaded for templating purposes. You're probably used to storing your HTML templates inline using a script with a specific identifier.

With Underscore.js's micro-templating (and jQuery) this would typically be:

HTML:

<script type="text/template" id="mainViewTemplate">
	<% _.each( person, function( person_item ){ %>
                <li><%= person_item.get("name") %></li>  
     <% }); %>
</script>

JS:

var compiled_template = _.template( $('#mainViewTemplate').html() );

With Require.js and the text plugin however, it's as simple as saving your template into an external text file (say, mainView.html) and doing the following:

require(['js/app', 'text!templates/mainView.html'],
	function(app, mainView){
		
		var compiled_template = _.template( mainView );
	}
);

That's it!. You can then go applying your template to a view in Backbone doing something like:

collection.someview.el.html( compiled_template( { results: collection.models } ) );

All templating solutions will have their own custom methods for handling template compilation, but if you understand the above, substituting Underscore's micro-templating for any other solution should be fairly trivial.

Note: You may also be interested in looking at https://github.com/ZeeAgency/requirejs-tpl. It's an AMD-compatible version of the Underscore templating system that also includes support for optimization (pre-compiled templates) which can lead to better performance and no evals. I have yet to use it myself, but it comes as a recommended resource.

###Backbone and jQuery Mobile: Resolving the routing conflicts

The first major hurdle developers typically run into when building Backbone applications with jQuery Mobile is that both frameworks have their own opinions about how to handle application navigation.

Backbone's routers offer an explicit way to define custom navigation routes through Backbone.Router, whilst jQuery Mobile encourages the use of URL hash fragments to reference separate 'pages' or views in the same document. jQuery Mobile also supports automatically pulling in external content for links through XHR calls meaning that there can be quite a lot of inter-framework confusion about what a link pointing at '#/photo/id' should actually be doing.

Some of the solutions that have been previously proposed to work-around this problem included manually patching Backbone or jQuery Mobile. I discourage opting for these techniques as it becomes necessary to manually patch your framework builds when new releases get made upstream.

There's also https://github.com/azicchetti/jquerymobile-router, which tries to solve this problem differently, however I think my proposed solution is both simpler and allows both frameworks to cohabit quite peacefully without the need to extend either. What we're after is a way to prevent one framework from listening to hash changes so that we can fully rely on the other (e.g. Backbone.Router) to handle this for us exclusively.

Using jQuery Mobile this can be done by setting:

$.mobile.hashListeningEnabled = false;

prior to initializing any of your other code.

I discovered this method looking through some jQuery Mobile commits that didn't make their way into the official docs, but am happy to see that they are now covered here http://jquerymobile.com/test/docs/api/globalconfig.html in more detail.

The next question that arises is, if we're preventing jQuery Mobile from listening to URL hash changes, how can we still get the benefit of being able to navigate to other sections in a document using the built-in transitions and effects supported?. Good question. This can now be solve by simply calling $.mobile.changePage() as follows:

var url = '#about',
    effect = 'slideup',
    reverse = false,
    changeHash = false;

$.mobile.changePage( url , { transition: effect}, reverse, changeHash );

In the above sample, url can refer to a URL or a hash identifier to navigate to, effect is simply the transition effect to animate the page in with and the final two parameters decide the direction for the transition (reverse) and whether or not the hash in the address bar should be updated (changeHash). With respect to the latter, I typically set this to false to avoid managing two sources for hash updates, but feel free to set this to true if you're comfortable doing so.

Note: For some parallel work being done to explore how well the jQuery Mobile Router plugin works with Backbone, you may be interested in checking out https://github.com/Filirom1/jquery-mobile-backbone-requirejs.

###Getting started

Once you feel comfortable with the Backbone fundamentals (http://msdn.microsoft.com/en-us/scriptjunkie/hh377172.aspx) and you've put together a rough wireframe of the app you may wish to build, start to think about your application architecture. Ideally, you'll want to logically separate concerns so that it's as easy as possible to maintain the app in the future.

Namespacing

For this application, I opted for the nested namespacing pattern. Implemented correctly, this enables you to clearly identify if items being referenced in your app are views, other modules and so on. This initial structure is a sane place to also include application defaults (unless you prefer maintaining those in a separate file).

window.mobileSearch = window.mobileSearch || {
    views: {
        appview: new AppView
    },
    routers:{
        workspace:new Workspace()
    },
    utils: utils,
    defaults:{
        resultsPerPage: 16,
        safeSearch: 2,
        maxDate:'',
        minDate:'01/01/1970'
    }
}

Models

In the Flickly application, there are at least two unique types of data that need to be modelled - search results and individual photos, both of which contain additional meta-data like photo titles. If you simplify this down, search results are actually groups of photos in their own right, so the application only requires:

  • A single model (a photo or 'result' entry)
  • A result collection (containing a group of result entries) for search results
  • A photo collection (containing one or more result entries) for individual photos or photos with more than one image

Views

The views we'll need include an application view, a search results view and a photo view. Static views or pages of the single-page application which do not require a dynamic element to them (e.g an 'about' page) can be easily coded up in your document's markup, independant of Backbone.

Routers

A number of possible routes need to be taken into consideration:

  • Basic search queries #search/kiwis
  • Search queries with additional parameters (e.g sort, pagination) #search/kiwis/srelevance/p7
  • Queries for specific photos #photo/93839
  • A default route (no parameters passed)

###jQuery Mobile: Going beyond mobile application development

The majority of jQM apps I've seen in production have been developed for the purpose of providing an optimal experience to users on mobile devices. Given that the framework was developed for this purpose, there's nothing fundamentally wrong with this, but many developers forget that jQM is a UI framework not dissimilar to jQuery UI. It's using the widget factory and is capable of being used for a lot more than we give it credit for.

If you open up Flickly in a desktop browser, you'll get an image search UI that's modelled on Google.com, however, review the components (buttons, text inputs, tabs) on the page for a moment. The desktop UI doesn't look anything like a mobile application yet I'm still using jQM for theming mobile components; the tabs, date-picker, sliders - everything in the desktop UI is re-using what jQM would be providing users on mobile devices. Thanks to some media queries, the desktop UI can make optimal use of whitespace, expanding component blocks out and providing alternative layouts whilst still making use of jQM as a component framework.

The benefit of this is that I don't need to go pulling in jQuery UI separately to be able to take advantage of these features. Thanks to the recent ThemeRoller my components can look pretty much exactly how I would like them to and users of the app can get a jQM UI for lower-resolutions and a jQM-ish UI for everything else.

The takeaway here is just to remember that if you're not (already) going through the hassle of conditional script/style loading based on screen-resolution (using matchMedia.js etc), there are simpler approaches that can be taken to cross-device component theming.

More Repositories

1

critical

Extract & Inline Critical-path CSS in HTML pages
JavaScript
9,959
star
2

backbone-fundamentals

๐Ÿ“– A creative-commons book on Backbone.js for beginners and advanced users alike
JavaScript
9,294
star
3

essential-js-design-patterns

Repo for my 'Learning JavaScript Design Patterns' book
HTML
4,254
star
4

es6-tools

An aggregation of tooling for using ES6 today
3,954
star
5

basket.js

A script and resource loader for caching & loading files with localStorage
JavaScript
3,362
star
6

es6-equivalents-in-es5

WIP - ES6 Equivalents In ES5
2,533
star
7

puppeteer-webperf

Automating Web Performance testing with Puppeteer ๐ŸŽช
JavaScript
1,775
star
8

a11y

Accessibility audit tooling for the web (beta)
JavaScript
1,705
star
9

tmi

TMI (Too Many Images) - discover your image weight on the web
JavaScript
1,639
star
10

timing.js

Navigation Timing API measurement helpers
JavaScript
1,500
star
11

critical-path-css-tools

Tools to prioritize above-the-fold (critical-path) CSS
1,141
star
12

getUserMedia.js

Shim for getUserMedia(). Uses native implementation for modern browsers and a Flash fallback for everyone else.
JavaScript
908
star
13

critical-path-css-demo

Above-the-fold CSS generation + inlining using Critical & Gulp
ApacheConf
532
star
14

backbone-boilerplates

Backbone.js stack boilerplates demonstrating integration with Express, Ruby, PHP, Grails and more.
JavaScript
488
star
15

webpack-lighthouse-plugin

A Webpack plugin for Lighthouse
JavaScript
290
star
16

sublime-fixmyjs

SublimeText package for FixMyJS
Python
250
star
17

learning-jsdp

Learning JavaScript Design Patterns: 2nd Edition - The Examples
HTML
240
star
18

predictive-fetching

Improve performance by predictively fetching pages a user is likely to need
238
star
19

storage-on-the-web

๐Ÿ—ƒ Comparing storage options for the open web in 2016
225
star
20

visibly.js

A cross-browser Page Visibility API shim
JavaScript
221
star
21

sublime-build-systems

Sublime Text build systems
202
star
22

yeoman-examples

A repo of up to date examples using Yeoman
JavaScript
202
star
23

oust

Extract URLs to stylesheets, scripts, links, images or HTML imports from HTML
JavaScript
176
star
24

cssprettifier-bookmarklet

A bookmarklet for prettifying your CSS
JavaScript
174
star
25

polymer-boilerplate

A Polymer.js template for building fast, robust web apps using Web Components
JavaScript
166
star
26

pubsubz

Another Pub/Sub implementation
JavaScript
164
star
27

prism-js

A Polymer element for syntax highlighting with Prism.js
HTML
149
star
28

starter

A simple, git-clone friendly starting point for personal projects.
JavaScript
145
star
29

memoize.js

A faster JavaScript memoizer
JavaScript
143
star
30

largescale-demo

Scalable JS architecture demo for #jqcon
JavaScript
138
star
31

psi-gulp-sample

Sample Gulp project using PSI
JavaScript
126
star
32

preact-hn

๐Ÿ—ž Preact Hacker News
JavaScript
121
star
33

network-emulation-conditions

Network emulation / throttling conditions (2G, 3G, 4G, Wifi etc) โ˜Ž๏ธ
JavaScript
108
star
34

bubblesort

Bubble Sort implementation with O(n^2) complexity.
JavaScript
106
star
35

polymer-filters

Polymer filters for formatting values of expressions.
JavaScript
105
star
36

angular1-dribbble-pwa

Angular 1 Dribbble Progressive Web App demo
JavaScript
102
star
37

ember-progressive-webapp

Ember.js Zuperkulblog PWA (built with FastBoot and ember-cli)
JavaScript
97
star
38

memory-mysteries

V8 memory mysteries (sample app)
CSS
84
star
39

smaller-pictures-app

Smaller Pics Progressive Web App
JavaScript
82
star
40

x-instagram

[Deprecated] A Polymer element for querying the Instagram API (Note: not yet updated to Polymer 0.5.x)
JavaScript
76
star
41

x-imager

Responsive images using Imager.js and Polymer
74
star
42

backbonejs-gallery

A Backbone, Underscore and jQuery Templates based image gallery (early early beta)
JavaScript
72
star
43

todomvc-angular-4

Angular 4.x TodoMVC implementation
TypeScript
66
star
44

socketchat

SocketChat - a beginners chat app using SocketStream
CSS
63
star
45

github-watchers-button

An Embeddable GitHub 'Watchers' Button For External Pages
JavaScript
63
star
46

gulp-uncss-task

[Deprecated] Use gulp-uncss instead please.
JavaScript
63
star
47

yt-jukebox

A YouTube Jukebox element built with Polymer & Yeoman
JavaScript
61
star
48

critical-path-angular-demo

Above-the-fold CSS generation + inlining using Critical, Gulp & Angular
JavaScript
60
star
49

native-media-resizing

Draft proposal for browser-level media resizing
59
star
50

catclock

Polymer + Material Timer/Countdown/Countdown app (alpha)
JavaScript
56
star
51

recursive-binarysearch

Recursive Binary Search with O(log N) complexity
JavaScript
56
star
52

selectionsort

Selection sort with O(n^2) time complexity
JavaScript
56
star
53

polymer-grunt-example

Polymer + Grunt
JavaScript
56
star
54

microtemplatez

Another compact micro-templating solution
JavaScript
55
star
55

page-er

A Polymer element for paginating model data
CSS
53
star
56

google-slides

โšก An offline-enabled Polymer slide-deck
HTML
53
star
57

flickly-wireframe

The jQuery mobile wireframe for Flickly
52
star
58

grunt-uncss-sass-example

An example of using grunt-uncss on a Sass project
JavaScript
52
star
59

sparkle-trail

<sparkle-trail> Polymer element - useful as a pre-loader
CSS
51
star
60

cssdiet

(WIP) - A DevTools extension for multi-page unused CSS auditing
JavaScript
46
star
61

backbone-koans-qunit

Backbone Koans for QUnit
JavaScript
44
star
62

github-client

Angular GitHub client for Firefox OS
JavaScript
44
star
63

a11y-webapp

A11y WebApp built with Polymer (WIP)
JavaScript
44
star
64

video-js

A Polymer element for Video.js
CSS
42
star
65

generator-webapp-uncss

Yeoman generator with grunt-uncss
JavaScript
42
star
66

lottie-animation-demo

Network-aware adaptive loading with Lottie Web
JavaScript
41
star
67

spine.bitly

(Demo app) A Spine.js Bit.ly client for shortening URLs and archiving references to these links offline.
JavaScript
39
star
68

backbone-aura

Backbone Aura
38
star
69

es2015-todomvc-chrome

ES2015 TodoMVC app that works without a transpiler
JavaScript
38
star
70

critical-css-weather-app

Critical-path CSS optimized weather app
JavaScript
37
star
71

polymer-blog

A tutorial app for generator-polymer
JavaScript
33
star
72

generator-boilerplate

A simple Yeoman generator using Git submodules to clone over a boilerplate hosted elsewhere on GitHub
JavaScript
31
star
73

npm-and-polymer-demo

Demo of Polymer + Paper elements working off npm3
HTML
31
star
74

jquery-roundrr

A jQuery plugin for plotting interactive content galleries in a circle form
JavaScript
30
star
75

polymer-localforage

A Polymer element for Mozilla's localForage (async storage via IndexedDB or WebSQL)
HTML
30
star
76

devtools-timeline-model-browser

Browser-friendly helper for parsing DevTools Timeline traces into structured profiling data models
JavaScript
29
star
77

mustache-for-chromeapps

A special build of mustache that works in Chrome Apps under CSP
JavaScript
28
star
78

addyosmani

GitHub README
27
star
79

tmdb-viewer-load-more

Accessibility-friendly version of TMDB Viewer (load-more)
JavaScript
25
star
80

active-route

Active view routing for Polymer extending <template>
CSS
25
star
81

webapp-scaffold

Polymer webapp scaffold element
CSS
25
star
82

typeahead-country

A Polymer element for autocompleting country names
24
star
83

react-interop

React + Polymer + X-Tag interop
JavaScript
24
star
84

vue-cli-todomvc

TodoMVC built using the Vue.js 2.0 CLI ๐Ÿฐ
JavaScript
22
star
85

polymer-browserify-vulcanize

Polymer + Browserify + Vulcanize
JavaScript
22
star
86

es6-starter

A minimal starting point for using ES6 today.
JavaScript
21
star
87

faster-video

A Polymer element for <video> with playback speed controls
JavaScript
21
star
88

js-shapelib

A minimalist JavaScript library for drawing objects around a Circle or Ellipse
JavaScript
19
star
89

element-query

Element queries with Polymer (experimental fork)
CSS
19
star
90

clientside-sample-buildfile

A Client-side ANT Build File Example
19
star
91

parsely

A small utility for parsing URLs of all types.
JavaScript
18
star
92

video-player

A themeable Polymer video element
JavaScript
16
star
93

page-router

Declarative URL routing for Polymer elements.
CSS
16
star
94

polymer-eventemitter

A Polymer event emitter element with support for wildcards, many and once.
JavaScript
15
star
95

lighthouse-reports

Quick module for getting Lighthouse reports in JSON form
JavaScript
15
star
96

generator-es6

An ES6.now project generator for Yeoman.
JavaScript
14
star
97

aura

A scalable, event-driven JavaScript architecture for developing widget-based applications. Works with Backbone.js and other frameworks.
14
star
98

jquery-googleviewer-plugin

A compact Google Viewer plugin
14
star
99

medium-backups

HTML
13
star
100

css3-transition-fallbacks

CSS3 Transition Fallback demos
13
star