├── .npmignore ├── .gitignore ├── docs ├── public │ ├── fonts │ │ ├── aller-bold.eot │ │ ├── aller-bold.ttf │ │ ├── aller-bold.woff │ │ ├── aller-light.eot │ │ ├── aller-light.ttf │ │ ├── aller-light.woff │ │ ├── roboto-black.eot │ │ ├── roboto-black.ttf │ │ ├── novecento-bold.eot │ │ ├── novecento-bold.ttf │ │ ├── novecento-bold.woff │ │ └── roboto-black.woff │ └── stylesheets │ │ └── normalize.css ├── docco.css └── component.html ├── .travis.yml ├── .editorconfig ├── examples ├── react-and-react-router │ ├── src │ │ ├── htdocs │ │ │ └── index.html │ │ └── javascripts │ │ │ └── global.js │ ├── Brocfile.js │ └── package.json ├── react-router │ ├── public │ │ └── index.html │ ├── src │ │ ├── store.js │ │ ├── models │ │ │ └── person.js │ │ └── main.js │ ├── package.json │ └── gulpfile.js ├── blog │ ├── public │ │ ├── boot.js │ │ ├── collections │ │ │ └── blog.js │ │ ├── index.html │ │ └── components │ │ │ ├── blog.jsx │ │ │ └── blog.js │ ├── package.json │ ├── Gruntfile.js │ ├── README.md │ └── server.js ├── typewriter │ ├── index.html │ └── typewriter.js └── nested │ ├── index.html │ └── nested.jsx ├── .jshintrc ├── test ├── helpers │ └── polyfills.js └── specs │ ├── on-off.js │ └── mixin.js ├── bower.json ├── LICENSE-MIT ├── package.json ├── Gruntfile.js ├── dist ├── backbone-react-component-min.js └── backbone-react-component.js ├── README.md └── lib └── component.js /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | examples 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | 4 | .idea 5 | -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-bold.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-bold.woff -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-light.eot -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-light.ttf -------------------------------------------------------------------------------- /docs/public/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/aller-light.woff -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/roboto-black.eot -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/roboto-black.ttf -------------------------------------------------------------------------------- /docs/public/fonts/novecento-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/novecento-bold.eot -------------------------------------------------------------------------------- /docs/public/fonts/novecento-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/novecento-bold.ttf -------------------------------------------------------------------------------- /docs/public/fonts/novecento-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/novecento-bold.woff -------------------------------------------------------------------------------- /docs/public/fonts/roboto-black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magalhas/backbone-react-component/HEAD/docs/public/fonts/roboto-black.woff -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | notifications: 5 | email: true 6 | before_install: 7 | - npm install -g grunt-cli bower 8 | - bower install -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /examples/react-and-react-router/src/htdocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Backbone React Component App 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/react-router/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | backbone-react-component with react-router 5 | 6 | 7 |

Hello

8 | 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/react-router/src/store.js: -------------------------------------------------------------------------------- 1 | import {Person, Persons } from './models/person' 2 | 3 | let people = new Persons(); 4 | 5 | people.add(new Person({id: 1, name: "Eugen ", description: "The greatest"})); 6 | people.add(new Person({id: 2, name: "Adrian", description: "The second greatest"})); 7 | 8 | export default people; 9 | -------------------------------------------------------------------------------- /examples/react-router/src/models/person.js: -------------------------------------------------------------------------------- 1 | import {Model, Collection} from 'backbone'; 2 | 3 | class Person extends Model { 4 | defaults() { 5 | return { 6 | id: -1, 7 | name: '', 8 | description: 'Your average Joe' 9 | } 10 | } 11 | } 12 | 13 | class Persons extends Collection { 14 | model:Person 15 | } 16 | 17 | exports = {Person, Persons}; 18 | 19 | export default exports; 20 | -------------------------------------------------------------------------------- /examples/blog/public/boot.js: -------------------------------------------------------------------------------- 1 | (function (BlogCollection, BlogComponent) { 2 | 'use strict'; 3 | $.get('components/blog', function (componentAndData) { 4 | document.body.innerHTML = componentAndData.component; 5 | var blogCollection = new BlogCollection(componentAndData.data); 6 | React.renderComponent(BlogComponent({collection: blogCollection}), document.body); 7 | }); 8 | }(this.BlogCollection, this.BlogComponent)); 9 | -------------------------------------------------------------------------------- /examples/blog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "backbone": "^1.1.2", 4 | "backbone-react-component": "../../", 5 | "express": "^3.18.3", 6 | "react": "^0.13.0-beta.1", 7 | "underscore": "^1.7.0" 8 | }, 9 | "devDependencies": { 10 | "grunt": "^0.4.2", 11 | "grunt-contrib-watch": "^0.5.3", 12 | "grunt-jsx": "^0.1.5", 13 | "grunt-react": "^0.10.0" 14 | }, 15 | "main": "server" 16 | } 17 | -------------------------------------------------------------------------------- /examples/react-and-react-router/Brocfile.js: -------------------------------------------------------------------------------- 1 | var esTranspiler = require('broccoli-babel-transpiler'); 2 | var webpackify = require('broccoli-webpack'); 3 | var mergeTrees = require('broccoli-merge-trees'); 4 | 5 | var scripts = esTranspiler('src/javascripts'); 6 | 7 | var js_bundler = webpackify(scripts, { 8 | entry: './global', 9 | output: {filename: 'global.js'}, 10 | }); 11 | 12 | module.exports = mergeTrees(['src/htdocs',js_bundler]); -------------------------------------------------------------------------------- /examples/blog/public/collections/blog.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof module !== 'undefined') { 3 | var Backbone = require('backbone'); 4 | module.exports = factory(Backbone); 5 | } else 6 | root.BlogCollection = factory(this.Backbone); 7 | }(this, function (Backbone) { 8 | 'use strict'; 9 | var BlogCollection = Backbone.Collection.extend({ 10 | url: 'resources/blog' 11 | }); 12 | 13 | return BlogCollection; 14 | })); 15 | -------------------------------------------------------------------------------- /examples/blog/Gruntfile.js: -------------------------------------------------------------------------------- 1 | exports = module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | react: { 4 | main: { 5 | files: [ 6 | { 7 | expand: true, 8 | cwd: './public', 9 | src: ['**/*.jsx'], 10 | dest: './public', 11 | ext: '.js' 12 | } 13 | ] 14 | } 15 | }, 16 | watch: { 17 | files: ['./public/**/*.jsx'], 18 | tasks: ['react'], 19 | options: { 20 | spawn: false 21 | } 22 | } 23 | }); 24 | grunt.loadNpmTasks('grunt-contrib-watch'); 25 | grunt.loadNpmTasks('grunt-react'); 26 | }; -------------------------------------------------------------------------------- /examples/blog/README.md: -------------------------------------------------------------------------------- 1 | # Backbone.React.Component.Blog 2 | A Node.js application that uses a Backbone.React.Component and a Backbone.Collection that are also used on the client side. This is just an example of how to setup the first rendering of a component on the server and also keeping the collection updated with the same data both on the services and on the client sides. 3 | 4 | ## How to run 5 | Go through the command line to the path where this example is located and run: 6 | ```shell 7 | npm install 8 | node server 9 | ``` 10 | Then open localhost:8888 on your browser. 11 | 12 | ## How to setup development environment 13 | ```shell 14 | npm install -g grunt-cli 15 | grunt watch 16 | ``` -------------------------------------------------------------------------------- /examples/react-and-react-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BackboneReactDemo", 3 | "version": "0.3.0", 4 | "description": "Sample react, react-router and backbone-react-component application", 5 | "dependencies": { 6 | "backbone": "^1.1.2", 7 | "backbone-react-component": "git+ssh://git@github.com:ieugen/backbone-react-component.git#programatic-models-and-collections", 8 | "react": "^0.12.2", 9 | "react-router": "^0.12.4" 10 | }, 11 | "devDependencies": { 12 | "broccoli": "^0.15.1", 13 | "broccoli-babel-transpiler": "^4.0.1", 14 | "broccoli-browserify": "^0.1.0", 15 | "broccoli-merge-trees": "^0.2.1", 16 | "broccoli-webpack": "^0.1.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/react-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-usage", 3 | "version": "0.1.0", 4 | "description": "Backbone-react-component usage with react-router", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Ioan Eugen Stan", 10 | "license": "MIT", 11 | "dependencies": { 12 | "backbone": "^1.1.2", 13 | "react": "^0.12.2", 14 | "react-router": "^0.12.4" 15 | }, 16 | "devDependencies": { 17 | "babelify": "^5.0.3", 18 | "browser-sync": "^2.2.1", 19 | "browserify": "^9.0.3", 20 | "gulp": "^3.8.11", 21 | "gulp-util": "^3.0.4", 22 | "vinyl-source-stream": "^1.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/typewriter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Typewriter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/nested/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nested Models Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi": false, 3 | "bitwise": true, 4 | "browser": false, 5 | "curly": false, 6 | "eqeqeq": true, 7 | "eqnull": true, 8 | "esnext": true, 9 | "immed": true, 10 | "latedef": false, 11 | "newcap": false, 12 | "noarg": true, 13 | "nonew": true, 14 | "quotmark": false, 15 | "undef": true, 16 | "unused": "vars", 17 | "strict": false, 18 | "trailing": false, 19 | "validthis": true, 20 | "globals": { 21 | "define": true, 22 | "module": true, 23 | "require": true, 24 | "exports": true, 25 | "console": true, 26 | "Backbone": true, 27 | "React": true, 28 | "beforeEach": true, 29 | "afterEach": true, 30 | "describe": true, 31 | "it": true, 32 | "expect": true, 33 | "jasmine": true, 34 | "setInterval": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/helpers/polyfills.js: -------------------------------------------------------------------------------- 1 | // Removing phantomjs polyfill because it's not working properly 2 | delete Function.prototype.bind; 3 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind 4 | Function.prototype.bind = function (oThis) { 5 | if (typeof this !== "function") { 6 | // closest thing possible to the ECMAScript 5 internal IsCallable function 7 | throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 8 | } 9 | 10 | var aArgs = Array.prototype.slice.call(arguments, 1), 11 | fToBind = this, 12 | FNOP = function () {}, 13 | fBound = function () { 14 | return fToBind.apply(oThis, aArgs.concat(Array.prototype.slice.call(arguments))); 15 | }; 16 | 17 | FNOP.prototype = this.prototype; 18 | fBound.prototype = new FNOP(); 19 | 20 | return fBound; 21 | }; -------------------------------------------------------------------------------- /examples/react-router/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var util = require('gulp-util'); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | var babelify = require('babelify'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var b = browserify('./src/main.js', {debug: true}).transform(babelify); 10 | 11 | gulp.task('browserify', function () { 12 | return b.bundle() 13 | .on('error', util.log) 14 | .pipe(source('main.js')) 15 | .pipe(gulp.dest('./public')); 16 | }); 17 | 18 | gulp.task('browser-sync', function () { 19 | browserSync({ 20 | server: "./public" 21 | }) 22 | }); 23 | 24 | gulp.task('watch', function () { 25 | gulp.watch('src/*.js', ['browserify']); 26 | gulp.watch('public/**').on('change', browserSync.reload) 27 | }); 28 | 29 | gulp.task('default', ['browser-sync', 'watch']); 30 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone-react-component", 3 | "version": "0.10.0", 4 | "description": "Backbone.React.Component is an extendable wrapper for React.Component and brings all the power of Facebook's React to Backbone.js", 5 | "homepage": "https://github.com/magalhas/backbone-react-component", 6 | "authors": [ 7 | "magalhas " 8 | ], 9 | "main": "lib/component.js", 10 | "dependencies": { 11 | "backbone": "^1.2.0", 12 | "react": "^15.3.0", 13 | "underscore": "^1.8.3" 14 | }, 15 | "ignore": [ 16 | "*.yml", 17 | "bower_components", 18 | "Gruntfile.js", 19 | "npm-shrinkwrap.json", 20 | "examples", 21 | "dist", 22 | "docs", 23 | "node_modules", 24 | "test" 25 | ], 26 | "keywords": [ 27 | "backbone", 28 | "react", 29 | "component", 30 | "data", 31 | "binding", 32 | "template", 33 | "engine", 34 | "view", 35 | "views", 36 | "model", 37 | "collection" 38 | ], 39 | "license": "MIT" 40 | } 41 | -------------------------------------------------------------------------------- /examples/blog/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blog 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 "Magalhas" José Magalhães 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone-react-component", 3 | "version": "1.0.0", 4 | "filename": "backbone-react-component.js", 5 | "description": "Backbone.React.Component is a wrapper for React.Component and brings all the power of Facebook's React to Backbone.js", 6 | "author": { 7 | "name": "José Magalhães", 8 | "email": "magalhas@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/magalhas/backbone-react-component.git" 13 | }, 14 | "peerDependencies": { 15 | "backbone": "^1.2.3", 16 | "react": "^15.3.0" 17 | }, 18 | "dependencies": { 19 | "underscore": "^1.8.3", 20 | "prop-types": "^15.6.2" 21 | }, 22 | "devDependencies": { 23 | "backbone": "^1.2.3", 24 | "grunt": "^0.4.5", 25 | "grunt-cli": "^1.2.0", 26 | "grunt-contrib-clean": "^0.6.0", 27 | "grunt-contrib-copy": "^0.8.1", 28 | "grunt-contrib-jasmine": "^0.9.1", 29 | "grunt-contrib-uglify": "^0.9.2", 30 | "grunt-docco": "^0.4.0", 31 | "grunt-gh-pages": "^0.10.0", 32 | "jquery": "^2.1.4", 33 | "react": "^15.3.0", 34 | "react-dom": "^15.3.0" 35 | }, 36 | "scripts": { 37 | "test": "grunt test", 38 | "prepublish": "grunt build && grunt doc" 39 | }, 40 | "keywords": [ 41 | "backbone", 42 | "react", 43 | "data binding", 44 | "models", 45 | "collections", 46 | "server", 47 | "client", 48 | "react-component" 49 | ], 50 | "main": "lib/component", 51 | "engines": { 52 | "node": ">=0.10" 53 | }, 54 | "licenses": [ 55 | { 56 | "type": "MIT", 57 | "url": "https://github.com/magalhas/backbone-react-component/blob/master/LICENSE-MIT" 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /examples/blog/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('underscore'); 3 | var BlogCollection = require('./public/collections/blog'); 4 | var BlogComponent = require('./public/components/blog'); 5 | var express = require('express'); 6 | var React = require('react'); 7 | // Collection setup without dummy daya 8 | var blogCollection = new BlogCollection(); 9 | // Httpd setup 10 | var httpd = express(); 11 | httpd.use(express.json()); 12 | httpd.use(express.urlencoded()); 13 | // Get component first render and collection 14 | httpd.get('/components/blog', function (req, res) { 15 | res.send({ 16 | component: React.renderToString(BlogComponent({collection: blogCollection})), 17 | data: blogCollection.toJSON() 18 | }); 19 | }); 20 | // Get collection 21 | httpd.get('/resources/blog', function (req, res) { 22 | res.send(blogCollection.toJSON()); 23 | }); 24 | // Post new model 25 | httpd.post('/resources/blog', function (req, res) { 26 | var post = req.body; 27 | post.id = _.uniqueId(); 28 | blogCollection.add(post); 29 | res.send(post); 30 | }); 31 | // Update existing model 32 | httpd.put('/resources/blog/:id', function (req, res) { 33 | var id = req.params.id; 34 | var post = blogCollection.get(id); 35 | if (post) { 36 | post.set(req.body); 37 | res.send(post); 38 | } else { 39 | res.send(404); 40 | } 41 | }); 42 | // Delete existing model 43 | httpd.delete('/resources/blog/:id', function (req, res) { 44 | var id = req.params.id; 45 | var post = blogCollection.get(id); 46 | if (post) { 47 | blogCollection.remove(post); 48 | res.send(post); 49 | } else { 50 | res.send(404); 51 | } 52 | }); 53 | httpd.use(express.static(__dirname + '/public')); 54 | httpd.listen(8888); 55 | -------------------------------------------------------------------------------- /examples/react-and-react-router/src/javascripts/global.js: -------------------------------------------------------------------------------- 1 | // Init Backbone jquery object 2 | import $ from 'jquery'; 3 | import Backbone from 'backbone'; 4 | Backbone.$ = $; 5 | 6 | import React from 'react'; 7 | import Router from 'react-router'; 8 | import BackboneReactMixin from 'backbone-react-component'; 9 | 10 | let {Route, RouteHandler, Link, State, Navigation} = Router; 11 | 12 | class Person extends Backbone.Model { 13 | default() { 14 | return { 15 | id: 1, 16 | name: '' 17 | } 18 | } 19 | } 20 | 21 | class Persons extends Backbone.Collection { 22 | model: Person 23 | } 24 | 25 | 26 | let persons = new Persons(); 27 | 28 | let Ioan = new Person({id: 0, name:'Ioan Eugen'}) 29 | persons.add(Ioan); 30 | persons.add(new Person({id: 1, name: 'Jose'})); 31 | persons.add(new Person({id: 3, name: 'Eugen'})) 32 | 33 | let App = React.createClass({ 34 | mixins:[BackboneReactMixin], 35 | render(){ 36 | return ( 37 |
38 | 44 | 45 |

Route is rendered here:

46 | 47 |
48 | ) 49 | } 50 | }); 51 | 52 | let Hello = React.createClass({ 53 | mixins: [BackboneReactMixin, State], 54 | render(){ 55 | // debugger; 56 | return ( 57 |
hello {this.state.model.name}
58 | ); 59 | } 60 | }); 61 | 62 | let routes = ( 63 | 64 | 65 | 66 | ); 67 | 68 | // main application 69 | const content = document.body; 70 | let router = Router.create({routes}); 71 | router.run((Handler) => React.render(, content)); 72 | -------------------------------------------------------------------------------- /examples/typewriter/typewriter.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | /* globals document:true */ 3 | (function () { 4 | 'use strict'; 5 | var Typewriter = React.createClass({ 6 | mixins: [Backbone.React.Component.mixin], 7 | createParagraph: function (paragraph, index) { 8 | return

{paragraph.content}

; 9 | }, 10 | render: function () { 11 | return ( 12 |
13 |

{this.props.title}

14 | {this.props.collection ? this.props.collection.map(this.createParagraph) : void 0} 15 |
16 | ); 17 | } 18 | }); 19 | 20 | var createParagraph = function () { 21 | return new Backbone.Model({content: ''}); 22 | }; 23 | 24 | var paragraph = createParagraph(); 25 | var collection = new Backbone.Collection([paragraph]); 26 | 27 | var typewriter = ; 28 | 29 | React.render(typewriter, document.body); 30 | 31 | var loremipsum = 'Maecenas at lorem turpis. Maecenas elementum interdum ornare. Praesent ut lobortis tellus, et luctus eros. Curabitur id tristique justo. Morbi ultrices sapien at neque volutpat pulvinar. Vestibulum fringilla scelerisque justo, ac lacinia diam interdum et. Fusce id dolor in dui dapibus elementum in condimentum arcu. Praesent cursus fermentum porttitor. Praesent et imperdiet orci, lacinia sollicitudin tortor. Ut aliquet semper turpis quis facilisis. Aenean diam odio, malesuada ut velit dapibus, pellentesque egestas mauris. Vestibulum sit amet purus a diam laoreet varius. Donec condimentum pulvinar enim quis molestie. Phasellus tristique, augue ut eleifend fringilla, arcu leo mollis urna, egestas vestibulum lacus mi eu neque. Sed vulputate orci odio, eu egestas est mattis nec. Donec porta dolor vel iaculis fringilla.'; 32 | var caret = 0; 33 | setInterval(function () { 34 | paragraph.set('content', paragraph.get('content') + loremipsum[caret]); 35 | caret++; 36 | if (caret === loremipsum.length) { 37 | caret = 0; 38 | paragraph = createParagraph(); 39 | collection.add(paragraph); 40 | } 41 | }, 1); 42 | }()); 43 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | exports = module.exports = function (grunt) { 3 | grunt.initConfig({ 4 | clean: { 5 | doc: ['.tmp', '.grunt'] 6 | }, 7 | uglify: { 8 | options: { 9 | compress: {} 10 | }, 11 | build: { 12 | files: { 13 | 'dist/backbone-react-component-min.js': ['lib/component.js'] 14 | } 15 | } 16 | }, 17 | copy: { 18 | build: { 19 | files: [ 20 | {src: ['lib/component.js'], dest: 'dist/backbone-react-component.js'} 21 | ] 22 | }, 23 | doc: { 24 | files: [ 25 | {cwd: 'docs', src: ['**', '!component.html'], dest: '.tmp', expand: true}, 26 | {src: ['docs/component.html'], dest: '.tmp/index.html'} 27 | ] 28 | } 29 | }, 30 | docco: { 31 | doc: { 32 | src: ['lib/component.js'], 33 | options: { 34 | layout: 'parallel' 35 | } 36 | } 37 | }, 38 | 'gh-pages': { 39 | doc: { 40 | options: { 41 | base: '.tmp' 42 | }, 43 | src: ['**'] 44 | } 45 | }, 46 | jasmine: { 47 | dev: { 48 | src: ['lib/**/*.js'], 49 | options: { 50 | specs: 'test/specs/**/*.js', 51 | vendor: [ 52 | 'test/helpers/polyfills.js', 53 | 'node_modules/underscore/underscore.js', 54 | 'node_modules/backbone/backbone.js', 55 | 'node_modules/react/dist/react.js', 56 | 'node_modules/react-dom/dist/react-dom.js' 57 | ] 58 | } 59 | } 60 | } 61 | }); 62 | grunt.loadNpmTasks('grunt-contrib-clean'); 63 | grunt.loadNpmTasks('grunt-contrib-copy'); 64 | grunt.loadNpmTasks('grunt-contrib-jasmine'); 65 | grunt.loadNpmTasks('grunt-contrib-uglify'); 66 | grunt.loadNpmTasks('grunt-docco'); 67 | grunt.loadNpmTasks('grunt-gh-pages'); 68 | grunt.registerTask('default', ['build']); 69 | grunt.registerTask('build', ['copy:build', 'uglify:build']); 70 | grunt.registerTask('doc', ['clean:doc', 'docco:doc']); 71 | grunt.registerTask('publish-doc', ['doc', 'copy:doc', 'gh-pages:doc', 'clean:doc']); 72 | grunt.registerTask('test', ['jasmine:dev']); 73 | }; 74 | -------------------------------------------------------------------------------- /examples/react-router/src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Router from 'react-router'; 3 | import BackboneReactMixin from './../../../dist/backbone-react-component'; 4 | 5 | import PeopleStore from './store'; 6 | 7 | let {RouteHandler, Route, DefaultRoute, Link, State, Navigation} = Router; 8 | 9 | let App = React.createClass({ 10 | mixins: [BackboneReactMixin], 11 | render() { 12 | return ( 13 |
14 |

A simple person list

15 |
    16 |
  • 17 | Home 18 |
  • 19 |
  • 20 | List people 21 |
  • 22 |
23 |
24 | 25 |
26 |
27 | ); 28 | } 29 | }); 30 | 31 | let PersonList = React.createClass({ 32 | mixins: [BackboneReactMixin], 33 | overrideCollection() { 34 | // supply `this.props.collection` programmatically 35 | return PeopleStore; 36 | }, 37 | renderPersonList() { 38 | return this.props.collection.map((person, index) => { 39 | return ( 40 |
  • 41 | 42 | {person.get('name')} 43 |
  • 44 | ) 45 | }) 46 | }, 47 | render() { 48 | return ( 49 |
    50 |
      51 | {this.renderPersonList()} 52 |
    53 |
    54 | ); 55 | } 56 | }); 57 | 58 | let PersonDetail = React.createClass({ 59 | mixins: [State, Navigation, BackboneReactMixin], 60 | // supply a model programmatically, since we don't know beforehand which model we need to bind 61 | // we could perform an Ajax request here to get the model from the server 62 | overrideModel() { 63 | let modelId = this.getParams().id; 64 | return PeopleStore.get(modelId); 65 | }, 66 | render() { 67 | return ( 68 |
    69 | this.goBack()}>Back 70 | 71 |
    72 |

    Details for {this.props.model.get('name')}

    73 |

    {this.props.model.get('description')}

    74 |
    75 |
    76 | ) 77 | } 78 | }); 79 | 80 | // _Declare_ routes and what views they should render 81 | let routes = ( 82 | 83 | 84 | 85 | 86 | ); 87 | 88 | // build the routers and render the handlers (only top level gets actually mounted in the supplied DOM element ) 89 | let content = document.getElementById('content'); 90 | Router.run(routes, (Handler) => React.render(, content)); 91 | -------------------------------------------------------------------------------- /examples/nested/nested.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | /* globals _:true, document:true */ 3 | (function () { 4 | 'use strict'; 5 | // Our implementation of `Backbone.Model` 6 | var Model = Backbone.Model.extend({ 7 | set: function (attrs) { 8 | var self = this; 9 | if (attrs.collection) { 10 | // Bind any child collection changes to the model by triggering `change`. 11 | // This allows the root components to be aware that we need to change the 12 | // DOM. 13 | attrs.collection.on('add remove change', function () { 14 | self.trigger('change'); 15 | }); 16 | } 17 | Backbone.Model.prototype.set.apply(this, arguments); 18 | } 19 | }); 20 | // Our implementation of `Backbone.Collection` 21 | var Collection = Backbone.Collection.extend({ 22 | model: Model 23 | }); 24 | // Create a `Collection` with a `Model` that contains a `Collection` 25 | var collection = new Collection([{ 26 | foo: 'tar', 27 | collection: new Collection([{ 28 | foo: 'bar', 29 | collection: new Collection() 30 | }]) 31 | }]); 32 | // Component that represents a `Collection`. It's composed by `ModelComponent`s. 33 | var CollectionComponent = React.createClass({ 34 | mixins: [Backbone.React.Component.mixin], 35 | createModel: function (model) { 36 | // We'll use `foo` as our id. Normally you would use `model.id`. 37 | return ( 38 |
  • 39 | 40 |
  • 41 | ); 42 | }, 43 | render: function () { 44 | // Using `this.getCollection` to grab model instances instead of JSON through `this.props` 45 | return ( 46 |
    47 | Collection 48 | 49 |
      50 | {this.getCollection().map(this.createModel)} 51 |
    52 |
    53 | ); 54 | }, 55 | addToCollection: function (event) { 56 | this.getCollection().add({ 57 | foo: _.uniqueId('new'), 58 | collection: new Collection() 59 | }); 60 | } 61 | }); 62 | // Component that represents a `Model`. 63 | var ModelComponent = React.createClass({ 64 | mixins: [Backbone.React.Component.mixin], 65 | createCollection: function () { 66 | if (this.props.collection) { 67 | return ; 68 | } 69 | }, 70 | render: function () { 71 | return ( 72 |
    73 | {this.props.foo} 74 | 75 |
      76 |
    • {this.createCollection()}
    • 77 |
    78 |
    79 | ); 80 | }, 81 | removeModel: function (event) { 82 | this.getModel().destroy(); 83 | } 84 | }); 85 | // Render a `CollectionComponent` with the `collection` into `document.body` 86 | React.render(, document.body); 87 | }()); 88 | -------------------------------------------------------------------------------- /test/specs/on-off.js: -------------------------------------------------------------------------------- 1 | /* global _:true, document:true */ 2 | describe('Mixinless component', function () { 3 | 'use strict'; 4 | 5 | var el, component, mountedComponent, spy; 6 | 7 | beforeEach(function () { 8 | el = document.createElement('div'); 9 | }); 10 | 11 | afterEach(function () { 12 | ReactDOM.unmountComponentAtNode(el); 13 | spy = void 0; 14 | }); 15 | 16 | describe('with a model', function () { 17 | var model1 = new Backbone.Model({hello: 'world!'}); 18 | 19 | var Component = React.createFactory(React.createClass({ 20 | componentWillMount: function () { 21 | Backbone.React.Component.mixin.on(this, {models: model1}); 22 | }, 23 | componentDidMount: function () { 24 | if (spy) spy.call(this); 25 | }, 26 | componentWillUnmount: function () { 27 | Backbone.React.Component.mixin.off(this); 28 | }, 29 | componentDidUpdate: function () { 30 | if (spy) spy.call(this); 31 | }, 32 | getInitialState: function () { 33 | return {}; 34 | }, 35 | render: function () { 36 | return React.DOM.div({}, this.state.hello); 37 | } 38 | })); 39 | 40 | it('renders', function (done) { 41 | spy = jasmine.createSpy().and.callFake(function () { 42 | expect(this.state.model.hello).toEqual('world!'); 43 | done(); 44 | }); 45 | 46 | component = Component(); 47 | mountedComponent = ReactDOM.render(component, el); 48 | }); 49 | 50 | it('binds to a model', function (done) { 51 | component = Component(); 52 | mountedComponent = ReactDOM.render(component, el); 53 | spy = jasmine.createSpy().and.callFake(function () { 54 | expect(this.state.model.hello).toEqual('hell!'); 55 | done(); 56 | }); 57 | model1.set('hello', 'hell!'); 58 | }); 59 | }); 60 | 61 | describe('with a collection', function () { 62 | var collection1 = new Backbone.Collection([{hello: 1}, {hello: 2}]); 63 | 64 | var Component = React.createFactory(React.createClass({ 65 | componentWillMount: function () { 66 | Backbone.React.Component.mixin.on(this, {collections: collection1}); 67 | }, 68 | componentDidMount: function () { 69 | if (spy) spy.call(this); 70 | }, 71 | componentWillUnmount: function () { 72 | Backbone.React.Component.mixin.off(this); 73 | }, 74 | componentDidUpdate: function () { 75 | if (spy) spy.call(this); 76 | }, 77 | getInitialState: function () { 78 | return {}; 79 | }, 80 | render: function () { 81 | return React.DOM.div({}, this.state.collection.map(function (model) { 82 | return model.hello; 83 | })); 84 | } 85 | })); 86 | 87 | it('renders', function (done) { 88 | spy = jasmine.createSpy().and.callFake(function () { 89 | expect(this.state.collection[0].hello).toEqual(1); 90 | expect(this.state.collection[1].hello).toEqual(2); 91 | done(); 92 | }); 93 | 94 | component = Component(); 95 | mountedComponent = ReactDOM.render(component, el); 96 | }); 97 | 98 | it('binds to a collection', function (done) { 99 | component = Component(); 100 | mountedComponent = ReactDOM.render(component, el); 101 | spy = jasmine.createSpy().and.callFake(function () { 102 | expect(this.state.collection[0].hello).toEqual(3); 103 | expect(this.state.collection[1].hello).toEqual(2); 104 | done(); 105 | }); 106 | collection1.at(0).set('hello', 3); 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /examples/blog/public/components/blog.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | (function (root, factory) { 3 | if (typeof module !== 'undefined') { 4 | var _ = require('underscore'); 5 | var Backbone = require('backbone'); 6 | var React = require('react'); 7 | module.exports = factory(_, Backbone, React, require('backbone-react-component')); 8 | } else 9 | root.BlogComponent = factory(this._, this.Backbone, this.React, this.Backbone.React.Component.mixin); 10 | }(this, function (_, Backbone, React, backboneMixin) { 11 | 'use strict'; 12 | // In a better implementation this would be splited into multiple components. 13 | // Keeping this under one component for the sake of the example. Remember composition :) 14 | var BlogComponent = React.createClass({ 15 | mixins: [backboneMixin], 16 | getInitialState: function () { 17 | return { 18 | id: null, 19 | title: '', 20 | content: '' 21 | }; 22 | }, 23 | // Form rendering 24 | createForm: function () { 25 | return ( 26 |
    27 |

    {this.state.id ? 'Edit Post' : 'Create Post'}

    28 | 29 | 30 |