From dfc56a95cfe5fbc579c5a512e6b7f009ad79bc9a Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Wed, 21 Jan 2015 16:12:19 -0800 Subject: [PATCH 01/36] Move spa-index to the root of client --- client/{spa/index.html => spa-index.html} | 0 server/boot/spa.js | 13 +++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) rename client/{spa/index.html => spa-index.html} (100%) diff --git a/client/spa/index.html b/client/spa-index.html similarity index 100% rename from client/spa/index.html rename to client/spa-index.html diff --git a/server/boot/spa.js b/server/boot/spa.js index 1aef3fd..1933dba 100644 --- a/server/boot/spa.js +++ b/server/boot/spa.js @@ -7,15 +7,19 @@ module.exports = function mountApps(server) { var brfs = require('brfs'); // Set up paths - var clientPath = path.join(__dirname, '../..', '/client/spa'); - var clientRelativePath = '/client/spa'; + var clientPath = path.join(__dirname, '../..', '/client/'); + var clientRelativePath = '/client/'; // Set up router var router = server.loopback.Router(); + router.get('/index', function(req, res){ + res.sendFile(path.join(clientPath, 'spa-index.html')); + }); + router.get('/js/build.js', browserify( - path.join(clientPath, 'js/main.js'), + path.join(clientPath, 'spa/js/main.js'), { transform: [ brfs ] } @@ -28,6 +32,3 @@ module.exports = function mountApps(server) { server.use('/spa', router); }; - - - From 8467d3783e63d8b08b198c56ce0aee6c94d92959 Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Sun, 25 Jan 2015 15:33:45 -0800 Subject: [PATCH 02/36] Add make contact module available to single page application --- client/spa/js/contact/contact.controller.js | 69 ++++++++++++++ client/spa/js/contact/contact.html | 3 + client/spa/js/contact/contact.model.js | 24 +++++ client/spa/js/contact/contact.view.js | 37 ++++++++ .../contact/spec/contact.controller.spec.js | 92 ++++++++++++++++++ .../spa/js/contact/spec/contact.model.spec.js | 85 +++++++++++++++++ .../spa/js/contact/spec/contact.view.spec.js | 93 +++++++++++++++++++ client/spa/js/main.js | 2 +- common/models/contact.js | 3 + common/models/contact.json | 15 +++ server/model-config.json | 4 + 11 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 client/spa/js/contact/contact.controller.js create mode 100644 client/spa/js/contact/contact.html create mode 100644 client/spa/js/contact/contact.model.js create mode 100644 client/spa/js/contact/contact.view.js create mode 100644 client/spa/js/contact/spec/contact.controller.spec.js create mode 100644 client/spa/js/contact/spec/contact.model.spec.js create mode 100644 client/spa/js/contact/spec/contact.view.spec.js create mode 100644 common/models/contact.js create mode 100644 common/models/contact.json diff --git a/client/spa/js/contact/contact.controller.js b/client/spa/js/contact/contact.controller.js new file mode 100644 index 0000000..0928685 --- /dev/null +++ b/client/spa/js/contact/contact.controller.js @@ -0,0 +1,69 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./contact.model'); +var View = require('./contact.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'contacts/:id': 'showContact' + }, + + initialize: function(){ + this.options.container = this.options.container || 'body'; + this.model = new Model(); + this.view = new View({model: this.model}); + }, + + showContact: function(contactId, cb){ + this.fetchModel(contactId, function(err){ + var view; + + if (err){ + view = this.renderError(); + } else { + view = this.renderView(); + } + + if (cb){ + cb(err, view); + } + + }.bind(this)); + }, + + fetchModel: function(contactId, cb){ + this.model.set({id: contactId}); + + this.model.fetch({ + success: function(model, response, options){ + //console.log(model); + cb(null, model); + }, + error: function(model, response, options){ + //console.error(response); + cb(response, model); + } + }); + }, + + renderToContainer: function(view){ + return $(this.options.container).html(view); + }, + + renderView: function(){ + this.renderToContainer(this.view.render().$el); + return this.view; + }, + + renderError: function(){ + return this.renderToContainer( + '

There was a problem rendering this contact

'); + } + +}); + + + diff --git a/client/spa/js/contact/contact.html b/client/spa/js/contact/contact.html new file mode 100644 index 0000000..605fac0 --- /dev/null +++ b/client/spa/js/contact/contact.html @@ -0,0 +1,3 @@ +

<%- name %>

+

<%- contactType %>

+ diff --git a/client/spa/js/contact/contact.model.js b/client/spa/js/contact/contact.model.js new file mode 100644 index 0000000..8ce24dd --- /dev/null +++ b/client/spa/js/contact/contact.model.js @@ -0,0 +1,24 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Model.extend({ + + defaults: { + contactType: 'web' + }, + + urlRoot: '/api/contacts', + + initialize: function(){ + this.on('change', function(){ + this.trigger('foo', 'bar'); + }); + }, + + validate: function(attrs){ + if (!attrs.name){ + return 'name cannot be empty'; + } + } +}); diff --git a/client/spa/js/contact/contact.view.js b/client/spa/js/contact/contact.view.js new file mode 100644 index 0000000..4847d11 --- /dev/null +++ b/client/spa/js/contact/contact.view.js @@ -0,0 +1,37 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var _ = require('../vendor/index')._; +var $ = require('../vendor/index').$; + +var fs = require('fs'); //will be replaced by brfs in the browser + +// readFileSync will be evaluated statically so errors can't be caught +var template = fs.readFileSync(__dirname + '/contact.html', 'utf8'); + +module.exports = Backbone.View.extend({ + + className: 'contact', + + template: _.template(template), + + events: { + 'click .delete': 'destroy' + }, + + initialize: function(){ + this.listenTo(this.model, 'destroy', this.remove); + }, + + render: function(){ + var context = this.model.toJSON(); + this.$el.html(this.template(context)); + return this; + }, + + destroy: function(){ + this.model.destroy(); + } + +}); + diff --git a/client/spa/js/contact/spec/contact.controller.spec.js b/client/spa/js/contact/spec/contact.controller.spec.js new file mode 100644 index 0000000..cc3602f --- /dev/null +++ b/client/spa/js/contact/spec/contact.controller.spec.js @@ -0,0 +1,92 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Controller = require('../contact.controller'); +var $ = require('jquery'); +var matchers = require('jasmine-jquery-matchers'); + +// Test suite +console.log('test contact.controller'); +describe('Contact controller', function(){ + + var controller; + + beforeEach(function(){ + controller = new Controller(); + }); + + it('can be created', function(){ + expect(controller).toBeDefined(); + }); + + describe('when it is created', function(){ + + it('has the expected routes', function(){ + expect(controller.routes).toEqual(jasmine.objectContaining({ + 'contacts/:id': 'showContact' + })); + }); + + it('without a container option, uses body as the container', function(){ + expect(controller.options.container).toEqual('body'); + }); + + it('with a container option, uses specified container', function(){ + var ctrl = new Controller({container: '.newcontainer'}); + expect(ctrl.options.container).toEqual('.newcontainer'); + }); + }); + + describe('when calling showContact', function(){ + + beforeEach(function(){ + jasmine.addMatchers(matchers); + }); + + var success = function(callbacks){ + controller.model.set({'name': 'valid contact', 'contactType':'web'}); + callbacks.success(controller.model); + }; + var err = function(callbacks){ + callbacks.error('error', controller.model); + }; + + it('with a valid contact id, fetches the model', function(){ + spyOn(controller.model, 'fetch').and.callFake(success); + var cb = function(err, view){ + expect(err).toBeNull(); + expect(controller.model.get('name')).toEqual('valid contact'); + }; + controller.showContact(1, cb); + }); + + it('with a valid contact id, renders the view', function(){ + spyOn(controller.model, 'fetch').and.callFake(success); + spyOn(controller.view, 'render').and.callFake(function(){ + controller.view.$el = 'fake render'; + return controller.view; + }); + var cb = function(err, view){ + expect($('body')).toHaveText('fake render'); + expect(view.cid).toEqual(controller.view.cid); + }; + controller.showContact(1, cb); + }); + + it('with an invalid contact id, renders an error message', function(){ + spyOn(controller.model, 'fetch').and.callFake(err); + var cb = function(err, view){ + expect(err).toBeTruthy(); + expect($('body')).toHaveText( + 'There was a problem rendering this contact'); + }; + controller.showContact(1, cb); + }); + + }); + +}); + diff --git a/client/spa/js/contact/spec/contact.model.spec.js b/client/spa/js/contact/spec/contact.model.spec.js new file mode 100644 index 0000000..de2c439 --- /dev/null +++ b/client/spa/js/contact/spec/contact.model.spec.js @@ -0,0 +1,85 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Model = require('../contact.model'); + +// Test suite +console.log('test contact.model'); +describe('Contact model ', function(){ + + var model; + + describe('when creating a new model ', function(){ + + beforeEach(function(){ + model = new Model(); + }); + + it('sets the correct default values', function(){ + expect(model.get('contactType')).toEqual('web'); + }); + + xit('initializes with custom logic', function(){ + + }); + }); + + describe('when updating the model ', function(){ + + var errorSpy; + + beforeEach(function(){ + errorSpy = jasmine.createSpy('Invalid'); + model = new Model({ + id: 1 + }); + model.on('invalid', errorSpy); + model.save(); + }); + + it('does not save when name is empty ', function(){ + expect(errorSpy).toHaveBeenCalled(); + expect(errorSpy).toHaveBeenCalledWith( + model, + 'name cannot be empty', + { validate: true, validationError: 'name cannot be empty'} + ); + }); + + // Use if you have transformation logic on set + xit('sets the values correctly ', function(){ + + }); + + // Use if you have transformation logic on get + xit('retrieves the correct values ', function(){ + + }); + }); + + describe('when changing the state of the model ', function(){ + + var eventSpy; + + beforeEach(function(){ + eventSpy = jasmine.createSpy('Change Event'); + model = new Model({ + id: 1, + name: 'test' + }); + model.on('foo', eventSpy); + model.set({name: 'changed'}); + }); + + it('triggers the custom event foo', function(){ + expect(eventSpy).toHaveBeenCalled(); + expect(eventSpy).toHaveBeenCalledWith( + 'bar' + ); + }); + }); + +}); diff --git a/client/spa/js/contact/spec/contact.view.spec.js b/client/spa/js/contact/spec/contact.view.spec.js new file mode 100644 index 0000000..fd27aba --- /dev/null +++ b/client/spa/js/contact/spec/contact.view.spec.js @@ -0,0 +1,93 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var View = require('../contact.view.js'); +var matchers = require('jasmine-jquery-matchers'); +var Backbone = require('../../vendor/index').Backbone; + +// Test suite +console.log('test contact.view'); +describe('Contact view ', function(){ + + var model; + var view; + + beforeEach(function(){ + // Add some convenience tests for working with the DOM + jasmine.addMatchers(matchers); + + var Model = Backbone.Model.extend({}); + // Needs to have the fields required by the template + model = new Model({ + name: 'Contact <3', + contactType: 'web' + }); + + view = new View({ + model: model + }); + }); + + describe('when the view is instantiated ', function(){ + + it('creates the correct element', function(){ + // Element has to be uppercase + expect(view.el.nodeName).toEqual('DIV'); + }); + + it('sets the correct class', function(){ + expect(view.$el).toHaveClass('contact'); + }); + }); + + describe('when the view is rendered ', function(){ + + it('returns the view object ', function(){ + expect(view.render()).toEqual(view); + }); + + it('produces the correct HTML ', function(){ + view.render(); + expect(view.$('h1').html()).toEqual('Contact <3'); + }); + + }); + + xdescribe('when the user clicks on the Save button ', function(){ + + xit('updates the model', function(){ + }); + }); + + xdescribe('when the user clicks on ... ', function(){ + + xit('triggers the ... event', function(){ + }); + }); + + describe('when the user clicks on the Delete button ', function(){ + + beforeEach(function(){ + // Must call through otherwise the actual view function won't be called + spyOn(view, 'destroy').and.callThrough(); + // Must delegateEvents for the spy on a DOM event to work + view.delegateEvents(); + + spyOn(model, 'destroy'); + }); + + it('deletes the model', function(){ + // Must render for the event to be fired + view.render(); + view.$('.delete').trigger('click'); + + expect(view.destroy).toHaveBeenCalled(); + expect(model.destroy).toHaveBeenCalled(); + }); + }); + +}); + diff --git a/client/spa/js/main.js b/client/spa/js/main.js index f4be561..27940ad 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -9,6 +9,6 @@ window.Backbone = require('./vendor').Backbone; // Additional modules go here - +/* global window require */ // This should be the last line window.Backbone.history.start(); diff --git a/common/models/contact.js b/common/models/contact.js new file mode 100644 index 0000000..d564eba --- /dev/null +++ b/common/models/contact.js @@ -0,0 +1,3 @@ +module.exports = function(Contact) { + //Contact.validatesInclusionOf('enumTest', {in: ['contractor', 'employee']}); +}; diff --git a/common/models/contact.json b/common/models/contact.json new file mode 100644 index 0000000..75ed28b --- /dev/null +++ b/common/models/contact.json @@ -0,0 +1,15 @@ +{ + "name": "contact", + "base": "PersistedModel", + "idInjection": true, + "properties": { + "name": { + "type": "string", + "required": true + } + }, + "validations": [], + "relations": {}, + "acls": [], + "methods": [] +} diff --git a/server/model-config.json b/server/model-config.json index 6b39153..fe6e0dd 100644 --- a/server/model-config.json +++ b/server/model-config.json @@ -25,5 +25,9 @@ "Role": { "dataSource": "db", "public": false + }, + "contact": { + "dataSource": "db", + "public": true } } From e8b3b44ce77bc6d2cc284f6188a10b2d912ee3fd Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Sun, 25 Jan 2015 15:38:13 -0800 Subject: [PATCH 03/36] Enable the contact module and fix path to browserified build script for spa code --- client/spa-index.html | 2 +- client/spa/js/main.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/spa-index.html b/client/spa-index.html index 13edab0..f4693de 100644 --- a/client/spa-index.html +++ b/client/spa-index.html @@ -6,6 +6,6 @@ Hi, I'm the spa index from the spa folder. - + diff --git a/client/spa/js/main.js b/client/spa/js/main.js index 27940ad..7cc643a 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -3,9 +3,9 @@ window.Backbone = require('./vendor').Backbone; // Include your code -//var Contact = require('./contact/contact.controller'); +var Contact = require('./contact/contact.controller'); // Initialize it -//window.contact = new Contact({router:true, container: 'body'}); +window.contact = new Contact({router:true, container: 'body'}); // Additional modules go here From 8b1ae5174ca59f267994bbc52447989aaa6cb667 Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Wed, 4 Feb 2015 10:14:29 -0800 Subject: [PATCH 04/36] Add leading slash to path to spa/js/build.js --- client/spa-index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/spa-index.html b/client/spa-index.html index f4693de..23cf4f0 100644 --- a/client/spa-index.html +++ b/client/spa-index.html @@ -6,6 +6,6 @@ Hi, I'm the spa index from the spa folder. - + From 1a01bd6634c6a85dc4e20ff917b3164bd1f24211 Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Wed, 4 Feb 2015 17:50:27 -0800 Subject: [PATCH 05/36] Add sample of for list view --- client/spa/js/contact/contact.controller.js | 2 +- client/spa/js/contact/contacts.collection.js | 30 +++++ client/spa/js/contact/contacts.controller.js | 65 +++++++++ client/spa/js/contact/contacts.html | 8 ++ client/spa/js/contact/contacts.view.js | 52 ++++++++ .../contact/spec/contacts.collection.spec.js | 91 +++++++++++++ .../contact/spec/contacts.controller.spec.js | 72 ++++++++++ .../spa/js/contact/spec/contacts.view.spec.js | 123 ++++++++++++++++++ client/spa/js/main.js | 2 + 9 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 client/spa/js/contact/contacts.collection.js create mode 100644 client/spa/js/contact/contacts.controller.js create mode 100644 client/spa/js/contact/contacts.html create mode 100644 client/spa/js/contact/contacts.view.js create mode 100644 client/spa/js/contact/spec/contacts.collection.spec.js create mode 100644 client/spa/js/contact/spec/contacts.controller.spec.js create mode 100644 client/spa/js/contact/spec/contacts.view.spec.js diff --git a/client/spa/js/contact/contact.controller.js b/client/spa/js/contact/contact.controller.js index 0928685..0c18f26 100644 --- a/client/spa/js/contact/contact.controller.js +++ b/client/spa/js/contact/contact.controller.js @@ -8,7 +8,7 @@ var View = require('./contact.view'); module.exports = Backbone.Controller.extend({ routes: { - 'contacts/:id': 'showContact' + 'contacts/:id': 'showContact', }, initialize: function(){ diff --git a/client/spa/js/contact/contacts.collection.js b/client/spa/js/contact/contacts.collection.js new file mode 100644 index 0000000..1dd0864 --- /dev/null +++ b/client/spa/js/contact/contacts.collection.js @@ -0,0 +1,30 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Collection.extend({ + + url: '/api/contacts/', + + initialize: function(){ + this.on('sortById', this.sortById); + this.on('sortByName', this.sortByName); + this.trigger('sortByName'); + }, + + sortById: function(){ + this.comparator = function(model){ + return model.get('id'); + }; + this.sort(); + }, + + sortByName: function(){ + this.comparator = function(model){ + return model.get('name'); + }; + this.sort(); + } + +}); + diff --git a/client/spa/js/contact/contacts.controller.js b/client/spa/js/contact/contacts.controller.js new file mode 100644 index 0000000..06e219d --- /dev/null +++ b/client/spa/js/contact/contacts.controller.js @@ -0,0 +1,65 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./contact.model'); +var Collection = require('./contacts.collection'); +var View = require('./contacts.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'contacts': 'showContacts' + }, + + initialize: function(){ + this.options.container = this.options.container || 'body'; + }, + + getCollection: function(){ + if (!this.collection){ + var C = Collection.extend({model: Model}); + this.collection = new C(); + } + return this.collection; + }, + + getView: function(){ + if (!this.view){ + var V = View.extend({collection: this.collection}); + this.view = new V(); + } + return this.view; + }, + + showContacts: function(){ + var self = this; + this.getCollection().fetch({ + success: function(collection, response, options){ + self.getView(); + self.renderView(); + }, + error: function(collection, response, options){ + self.renderError(); + } + }); + }, + + renderToContainer: function(html){ + return $(this.options.container).html(html); + }, + + renderView: function(){ + this.renderToContainer(this.view.render().$el); + return this.view; + }, + + renderError: function(){ + return this.renderToContainer( + '

There was a problem rendering contacts

'); + } + +}); + + + diff --git a/client/spa/js/contact/contacts.html b/client/spa/js/contact/contacts.html new file mode 100644 index 0000000..6dbe85a --- /dev/null +++ b/client/spa/js/contact/contacts.html @@ -0,0 +1,8 @@ +

Contacts

+ + +
    + <% _.each( models, function( model ){ %> +
  • <%- model.attributes.name %>
  • + <% }); %> +
diff --git a/client/spa/js/contact/contacts.view.js b/client/spa/js/contact/contacts.view.js new file mode 100644 index 0000000..5a5958c --- /dev/null +++ b/client/spa/js/contact/contacts.view.js @@ -0,0 +1,52 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var _ = require('../vendor/index')._; +var $ = require('../vendor/index').$; + +var fs = require('fs'); //will be replaced by brfs in the browser + +// readFileSync will be evaluated statically so errors can't be caught +var template = fs.readFileSync(__dirname + '/contacts.html', 'utf8'); + +module.exports = Backbone.View.extend({ + + className: 'contacts', + + template: _.template(template), + + events:{ + 'click .sortById': 'sortById', + 'click .sortByName': 'sortByName' + }, + + initialize: function() { + this.listenTo(this.collection, 'add', function(){ + this.render(); + }); + this.listenTo(this.collection, 'reset', function(){ + this.render(); + }); + this.listenTo(this.collection, 'sort', function(){ + this.render(); + }); + }, + + render: function() { + var context = this.collection; + this.$el.html(this.template(context)); + return this; + }, + + sortById: function(){ + this.collection.trigger('sortById'); + this.render(); + }, + + sortByName: function(){ + this.collection.trigger('sortByName'); + this.render(); + } + +}); + diff --git a/client/spa/js/contact/spec/contacts.collection.spec.js b/client/spa/js/contact/spec/contacts.collection.spec.js new file mode 100644 index 0000000..b952e56 --- /dev/null +++ b/client/spa/js/contact/spec/contacts.collection.spec.js @@ -0,0 +1,91 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Collection = require('../contacts.collection'); + +// Test suite +console.log('test contacts.collection'); +describe('Contact collection ', function(){ + + var collection; + var modelA; + var modelB; + var modelC; + + beforeEach(function(){ + // Set up test data + modelA = {id: 3, name: 'A'}; + modelB = {id: 1, name: 'B'}; + modelC = {id: 2, name: 'C'}; + }); + + + describe('when models are added to the collection ', function(){ + + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); + + }); + + + it('orders the models by the contact name', function(){ + expect(collection.at(0).get('id')).toEqual(modelA.id); + expect(collection.at(1).get('id')).toEqual(modelB.id); + expect(collection.at(2).get('id')).toEqual(modelC.id); + }); + + }); + + describe('when the collection interacts with the server', function(){ + + it('fetches from the correct url', function(){ + collection = new Collection(); + expect(collection.url).toEqual('/api/contacts/'); + }); + + }); + + describe('when the collection gets a sort event', function(){ + + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); + + }); + + it('sorts by id', function(){ + collection.trigger('sortByName'); + expect(collection.at(0).get('id')).toEqual(modelA.id); + expect(collection.at(1).get('id')).toEqual(modelB.id); + expect(collection.at(2).get('id')).toEqual(modelC.id); + }); + + it('sorts by name', function(){ + collection.trigger('sortById'); + expect(collection.at(0).get('id')).toEqual(modelB.id); + expect(collection.at(1).get('id')).toEqual(modelC.id); + expect(collection.at(2).get('id')).toEqual(modelA.id); + }); + + }); + +}); + diff --git a/client/spa/js/contact/spec/contacts.controller.spec.js b/client/spa/js/contact/spec/contacts.controller.spec.js new file mode 100644 index 0000000..4187457 --- /dev/null +++ b/client/spa/js/contact/spec/contacts.controller.spec.js @@ -0,0 +1,72 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Controller = require('../contacts.controller'); +var $ = require('jquery'); +var matchers = require('jasmine-jquery-matchers'); + +// Test suite +console.log('test contacts.controller'); +describe('Contacts controller', function(){ + + var controller; + + beforeEach(function(){ + controller = new Controller(); + }); + + it('can be created', function(){ + expect(controller).toBeDefined(); + }); + + describe('when it is created', function(){ + + it('has the expected routes', function(){ + expect(controller.routes).toEqual(jasmine.objectContaining({ + 'contacts': 'showContacts' + })); + }); + + it('without a container option, uses body as the container', function(){ + expect(controller.options.container).toEqual('body'); + }); + + it('with a container option, uses specified container', function(){ + var ctrl = new Controller({container: '.newcontainer'}); + expect(ctrl.options.container).toEqual('.newcontainer'); + }); + }); + + describe('when calling showContacts', function(){ + + beforeEach(function(){ + jasmine.addMatchers(matchers); + }); + + it('sets up the collection', function(){ + + }); + + it('fetches data for the collection', function(){ + + }); + + it('when fetch is successful, sets up the view', function(){ + + }); + + it('when fetch is successful, renders the view', function(){ + + }); + + it('when fetch errors, renders error', function(){ + + }); + + }); + +}); + diff --git a/client/spa/js/contact/spec/contacts.view.spec.js b/client/spa/js/contact/spec/contacts.view.spec.js new file mode 100644 index 0000000..bd81e44 --- /dev/null +++ b/client/spa/js/contact/spec/contacts.view.spec.js @@ -0,0 +1,123 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var View = require('../contacts.view.js'); +var matchers = require('jasmine-jquery-matchers'); +var _ = require('../../vendor/index')._; +var Backbone = require('../../vendor/index').Backbone; + +// Test suite +console.log('test contacts.view'); +describe('Contacts view ', function(){ + + var model; + var collection; + var view; + + beforeEach(function(){ + // Add some convenience tests for working with the DOM + jasmine.addMatchers(matchers); + + var Model = Backbone.Model.extend({}); + var Collection = Backbone.Collection.extend({model: Model}); + // Needs to have the fields required by the template + model = new Model({ + name: 'Contact <3', + contactType: 'web' + }); + + collection = new Collection(model); + + view = new View({ + collection: collection + }); + }); + + describe('when the view is instantiated ', function(){ + + beforeEach(function(){ + spyOn(view, 'render').and.callThrough(); + }); + + it('creates the correct element', function(){ + // Element has to be uppercase + expect(view.el.nodeName).toEqual('DIV'); + }); + + it('sets the correct class', function(){ + view.render(); + expect(view.$el).toHaveClass('contacts'); + }); + + it('renders when something is added to the collection', function(){ + collection.trigger('add'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is reset', function(){ + collection.trigger('reset'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is sorted', function(){ + collection.trigger('sort'); + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the view is rendered ', function(){ + + it('returns the view object ', function(){ + expect(view.render()).toEqual(view); + }); + it('produces the correct HTML ', function(){ + view.render(); + expect(view.$('h1').html()).toEqual('Contacts'); + expect(view.$('.contact')[0]).toHaveText('Contact <3'); + }); + + }); + + describe('when the user clicks on the Sort By Id button ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortById event on the collection', function(){ + var spy = jasmine.createSpy('sortById'); + collection.on('sortById', spy); + + view.$('.sortById').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortById').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + xdescribe('when the user clicks on the Sort By Name button ', function(){ + + xit('triggers the sortByName event on the collection', function(){ + }); + + xit('renders the view', function(){ + + }); + + }); + +}); + diff --git a/client/spa/js/main.js b/client/spa/js/main.js index 7cc643a..f1fa3f3 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -4,8 +4,10 @@ window.Backbone = require('./vendor').Backbone; // Include your code var Contact = require('./contact/contact.controller'); +var Contacts = require('./contact/contacts.controller'); // Initialize it window.contact = new Contact({router:true, container: 'body'}); +window.contacts = new Contacts({router:true, container: 'body'}); // Additional modules go here From ffc22b5c70a2f1b6f88f4694d0d116ea53375eb3 Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Thu, 5 Feb 2015 11:22:52 -0800 Subject: [PATCH 06/36] Finish contacts collection example --- client/spa/js/contact/contacts.controller.js | 4 +- client/spa/js/contact/contacts.view.js | 1 - .../contact/spec/contacts.collection.spec.js | 4 +- .../contact/spec/contacts.controller.spec.js | 75 ++++++++++++++++--- .../spa/js/contact/spec/contacts.view.spec.js | 24 +++--- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/client/spa/js/contact/contacts.controller.js b/client/spa/js/contact/contacts.controller.js index 06e219d..9730fe9 100644 --- a/client/spa/js/contact/contacts.controller.js +++ b/client/spa/js/contact/contacts.controller.js @@ -18,8 +18,8 @@ module.exports = Backbone.Controller.extend({ getCollection: function(){ if (!this.collection){ - var C = Collection.extend({model: Model}); - this.collection = new C(); + Collection = Collection.extend({model: Model}); + this.collection = new Collection(); } return this.collection; }, diff --git a/client/spa/js/contact/contacts.view.js b/client/spa/js/contact/contacts.view.js index 5a5958c..b686ab3 100644 --- a/client/spa/js/contact/contacts.view.js +++ b/client/spa/js/contact/contacts.view.js @@ -2,7 +2,6 @@ var Backbone = require('../vendor/index').Backbone; var _ = require('../vendor/index')._; -var $ = require('../vendor/index').$; var fs = require('fs'); //will be replaced by brfs in the browser diff --git a/client/spa/js/contact/spec/contacts.collection.spec.js b/client/spa/js/contact/spec/contacts.collection.spec.js index b952e56..89a36f9 100644 --- a/client/spa/js/contact/spec/contacts.collection.spec.js +++ b/client/spa/js/contact/spec/contacts.collection.spec.js @@ -8,7 +8,7 @@ var Collection = require('../contacts.collection'); // Test suite console.log('test contacts.collection'); -describe('Contact collection ', function(){ +describe('Contacts collection ', function(){ var collection; var modelA; @@ -56,7 +56,7 @@ describe('Contact collection ', function(){ }); - describe('when the collection gets a sort event', function(){ + describe('when a sort event is triggered', function(){ beforeEach(function(){ collection = new Collection(); diff --git a/client/spa/js/contact/spec/contacts.controller.spec.js b/client/spa/js/contact/spec/contacts.controller.spec.js index 4187457..7a180db 100644 --- a/client/spa/js/contact/spec/contacts.controller.spec.js +++ b/client/spa/js/contact/spec/contacts.controller.spec.js @@ -4,6 +4,7 @@ spyOn */ // Get the code you want to test +var Backbone = require('../../vendor/index').Backbone; var Controller = require('../contacts.controller'); var $ = require('jquery'); var matchers = require('jasmine-jquery-matchers'); @@ -40,29 +41,79 @@ describe('Contacts controller', function(){ }); }); - describe('when calling showContacts', function(){ + describe('when asked to showContacts', function(){ beforeEach(function(){ jasmine.addMatchers(matchers); - }); - - it('sets up the collection', function(){ - - }); - - it('fetches data for the collection', function(){ }); - it('when fetch is successful, sets up the view', function(){ + describe('and fetch is successful', function(){ + + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + //controller.collection.add({id: 1, name: 'test', contactType: 'web'}); + options.success(); + } + ); + }); + + it('sets up the collection if it is not already', function(){ + expect(controller.collection).not.toBeDefined(); + controller.showContacts(); + expect(controller.collection).toBeDefined(); + }); + + it('uses the existing collection if it is already setup', function(){ + controller.showContacts(); + controller.collection.add({id: 'xyz'}); + controller.showContacts(); + expect(controller.collection.at(0).get('id')).toEqual('xyz'); + }); + + it('fetches data for the collection', function(){ + controller.showContacts(); + expect(controller.collection.fetch).toHaveBeenCalled(); + }); + + it('sets up the view if it is not already', function(){ + expect(controller.view).not.toBeDefined(); + controller.showContacts(); + expect(controller.view).toBeDefined(); + }); + + it('uses the existing view if it is already setup', function(){ + controller.showContacts(); + controller.view.test = true; + controller.showContacts(); + expect(controller.view.test).toBeTruthy(); + }); + + it('renders the view to the correct container', function() { + spyOn(controller, 'renderView').and.callThrough(); + controller.showContacts(); + var returnedView = controller.renderView.calls.mostRecent().object.view; + expect(returnedView).toEqual(controller.view); + expect($('body h1')).toHaveText('Contacts'); + }); }); - it('when fetch is successful, renders the view', function(){ + describe('and fetch errors', function(){ - }); + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + options.error(); + } + ); + }); - it('when fetch errors, renders error', function(){ + it('renders error', function(){ + controller.showContacts(); + expect($('body')).toHaveText('There was a problem rendering contacts'); + }); }); diff --git a/client/spa/js/contact/spec/contacts.view.spec.js b/client/spa/js/contact/spec/contacts.view.spec.js index bd81e44..079da56 100644 --- a/client/spa/js/contact/spec/contacts.view.spec.js +++ b/client/spa/js/contact/spec/contacts.view.spec.js @@ -36,22 +36,26 @@ describe('Contacts view ', function(){ }); }); - describe('when the view is instantiated ', function(){ + describe('when the view is instantiated ', function() { - beforeEach(function(){ - spyOn(view, 'render').and.callThrough(); - }); - - it('creates the correct element', function(){ + it('creates the correct element', function () { // Element has to be uppercase expect(view.el.nodeName).toEqual('DIV'); }); - it('sets the correct class', function(){ + it('sets the correct class', function () { view.render(); expect(view.$el).toHaveClass('contacts'); }); + }); + + describe('when collection events happen', function(){ + + beforeEach(function () { + spyOn(view, 'render').and.callThrough(); + }); + it('renders when something is added to the collection', function(){ collection.trigger('add'); expect(view.render).toHaveBeenCalled(); @@ -69,12 +73,12 @@ describe('Contacts view ', function(){ }); - describe('when the view is rendered ', function(){ + describe('when the view is rendered', function(){ - it('returns the view object ', function(){ + it('returns the view object', function(){ expect(view.render()).toEqual(view); }); - it('produces the correct HTML ', function(){ + it('produces the correct HTML', function(){ view.render(); expect(view.$('h1').html()).toEqual('Contacts'); expect(view.$('.contact')[0]).toHaveText('Contact <3'); From 504a0483b5f3b6c0f3e9b31a3349ad3ac9fc90b3 Mon Sep 17 00:00:00 2001 From: Todd Bashor Date: Thu, 5 Feb 2015 11:56:39 -0800 Subject: [PATCH 07/36] Remove comment to stop lint error --- client/spa/js/contact/spec/contacts.controller.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/spa/js/contact/spec/contacts.controller.spec.js b/client/spa/js/contact/spec/contacts.controller.spec.js index 7a180db..def4aed 100644 --- a/client/spa/js/contact/spec/contacts.controller.spec.js +++ b/client/spa/js/contact/spec/contacts.controller.spec.js @@ -53,7 +53,6 @@ describe('Contacts controller', function(){ beforeEach(function(){ spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( function(options){ - //controller.collection.add({id: 1, name: 'test', contactType: 'web'}); options.success(); } ); From e865fbc896de2b03412b09b1bfc6211152e3d0d8 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 9 Feb 2015 13:09:31 -0800 Subject: [PATCH 08/36] add paths to collection --- client/spa/js/learning-resource/learning-resource.view.js | 2 +- client/spa/js/main.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/learning-resource.view.js b/client/spa/js/learning-resource/learning-resource.view.js index 44f514e..923be4a 100644 --- a/client/spa/js/learning-resource/learning-resource.view.js +++ b/client/spa/js/learning-resource/learning-resource.view.js @@ -7,7 +7,7 @@ var $ = require('../vendor/index').$; var fs = require('fs'); //will be replaced by brfs in the browser // readFileSync will be evaluated statically so errors can't be caught -var template = fs.readFileSync(__dirname + '/learning-resources.html', 'utf8'); +var template = fs.readFileSync(__dirname + '/learning-resource.html', 'utf8'); module.exports = Backbone.View.extend({ diff --git a/client/spa/js/main.js b/client/spa/js/main.js index f337ed8..0ca740a 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -4,6 +4,7 @@ window.Backbone = require('./vendor').Backbone; // Include your code var Resource = require('./learning-resource/learning-resource.controller'); +var Resource = require('./learning-resource/learning-resources.controller'); // Initialize it window.resource = new Resource({router:true, container: 'body'}); From d6f9edfc81db21b82da3acc6fed5f40f4f49f431 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 9 Feb 2015 13:10:25 -0800 Subject: [PATCH 09/36] add table and restyle --- .../learning-resource/learning-resources.html | 149 ++++++------------ 1 file changed, 50 insertions(+), 99 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index b17cf80..3fed132 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -1,122 +1,73 @@ -
+
+

Learning Resources

+
+ + + + + + + + + <% _.each( models, function( model ){ %> + + + + + + + + <% }); %> +
#TitleTypeAuthorsDescription
<%- model.attributes.title %><%- model.attributes.resourceType %><%- model.attributes.authors %><%- model.attributes.description %>
+
+
+ + +
- <%- description %> -
-
-
Description:
- -
-
-
- -
-
- - - -
+ <%- model.attributes.description %>
-
- - - + --> + + + + From cc4d9026259913373a73fda01901da56ffcc22eb Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 9 Feb 2015 13:11:24 -0800 Subject: [PATCH 10/36] rename template to appropriate single resource --- .../learning-resource/learning-resource.html | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 client/spa/js/learning-resource/learning-resource.html diff --git a/client/spa/js/learning-resource/learning-resource.html b/client/spa/js/learning-resource/learning-resource.html new file mode 100644 index 0000000..b17cf80 --- /dev/null +++ b/client/spa/js/learning-resource/learning-resource.html @@ -0,0 +1,122 @@ + +
+
+
+
+ +
+ + <%- title %> +
+ <%- resourceType %> + by: +
+
+
+
Title:
+ +
+
+
Resource Type:
+ +
+
+
Authors:
+ +
+ +
+ <%- description %> +
+
+
Description:
+ +
+
+
+ +
+
+ + + +
+
+
+
+
+
From e697dc73c0e190d126a80d5a1313c0dfb9ad35b2 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 9 Feb 2015 13:12:20 -0800 Subject: [PATCH 11/36] adding files for collections and associated tests --- .../learning-resources.collection.js | 30 ++++ .../learning-resources.controller.js | 63 +++++++++ .../learning-resources.view.js | 51 +++++++ .../learning-resources.collection.spec.js | 92 +++++++++++++ .../learning-resources.controller.spec.js | 122 +++++++++++++++++ .../spec/learning-resources.view.spec.js | 129 ++++++++++++++++++ 6 files changed, 487 insertions(+) create mode 100644 client/spa/js/learning-resource/learning-resources.collection.js create mode 100644 client/spa/js/learning-resource/learning-resources.controller.js create mode 100644 client/spa/js/learning-resource/learning-resources.view.js create mode 100644 client/spa/js/learning-resource/spec/learning-resources.collection.spec.js create mode 100644 client/spa/js/learning-resource/spec/learning-resources.controller.spec.js create mode 100644 client/spa/js/learning-resource/spec/learning-resources.view.spec.js diff --git a/client/spa/js/learning-resource/learning-resources.collection.js b/client/spa/js/learning-resource/learning-resources.collection.js new file mode 100644 index 0000000..39dc190 --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.collection.js @@ -0,0 +1,30 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; + +module.exports = Backbone.Collection.extend({ + + url: '/api/learning-resources', + + initialize: function(){ + this.on('sortById', this.sortById); + this.on('sortByName', this.sortByName); + this.trigger('sortByName'); + }, + + sortById: function(){ + this.comparator = function(model){ + return model.get('id'); + }; + this.sort(); + }, + + sortByName: function(){ + this.comparator = function(model){ + return model.get('title'); + }; + this.sort(); + } + +}); + diff --git a/client/spa/js/learning-resource/learning-resources.controller.js b/client/spa/js/learning-resource/learning-resources.controller.js new file mode 100644 index 0000000..6bd1ff1 --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.controller.js @@ -0,0 +1,63 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var $ = require('../vendor/index').$; +var Model = require('./learning-resource.model'); +var Collection = require('./learning-resources.collection'); +var View = require('./learning-resources.view'); + +module.exports = Backbone.Controller.extend({ + + routes: { + 'learning-resources': 'showLearningResources' + }, + + initialize: function(){ + this.options.container = this.options.container || 'body'; + }, + + getCollection: function(){ + if (!this.collection){ + Collection = Collection.extend({model: Model}); + this.collection = new Collection(); + } + return this.collection; + }, + + getView: function(){ + if (!this.view){ + var V = View.extend({collection: this.collection}); + this.view = new V(); + } + return this.view; + }, + + showLearningResources: function(){ + var self = this; + this.getCollection().fetch({ + success: function(collection, response, options){ + console.log(collection); + self.getView(); + self.renderView(); + }, + error: function(collection, response, options){ + self.renderError(); + } + }); + }, + + renderToContainer: function(html){ + return $(this.options.container).html(html); + }, + + renderView: function(){ + this.renderToContainer(this.view.render().$el); + return this.view; + }, + + renderError: function(){ + return this.renderToContainer( + '

There was a problem rendering contacts

'); + } + +}); diff --git a/client/spa/js/learning-resource/learning-resources.view.js b/client/spa/js/learning-resource/learning-resources.view.js new file mode 100644 index 0000000..ced4147 --- /dev/null +++ b/client/spa/js/learning-resource/learning-resources.view.js @@ -0,0 +1,51 @@ +'use strict'; + +var Backbone = require('../vendor/index').Backbone; +var _ = require('../vendor/index')._; + +var fs = require('fs'); //will be replaced by brfs in the browser + +// readFileSync will be evaluated statically so errors can't be caught +var template = fs.readFileSync(__dirname + '/learning-resources.html', 'utf8'); + +module.exports = Backbone.View.extend({ + + className: 'learning-resources', + + template: _.template(template), + + events:{ + 'click .sortById': 'sortById', + 'click .sortByName': 'sortByName' + }, + + initialize: function() { + this.listenTo(this.collection, 'add', function(){ + this.render(); + }); + this.listenTo(this.collection, 'reset', function(){ + this.render(); + }); + this.listenTo(this.collection, 'sort', function(){ + this.render(); + }); + }, + + render: function() { + var context = this.collection; + this.$el.html(this.template(context)); + return this; + }, + + sortById: function(){ + this.collection.trigger('sortById'); + this.render(); + }, + + sortByName: function(){ + this.collection.trigger('sortByName'); + this.render(); + } + +}); + diff --git a/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js new file mode 100644 index 0000000..1716df0 --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js @@ -0,0 +1,92 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Collection = require('../learning-resources.collection'); + +// Test suite +console.log('test learning-resources.collection'); +describe('Learning resources collection ', function(){ + + var collection; + var modelA; + var modelB; + var modelC; + + beforeEach(function(){ + // Set up test data + modelA = {id: 2, title: 'A', resourceType: 'link', description: 'reading', authors: 'mom'}; + modelB = {id: 0, title: 'M', resourceType: 'presentation', description: 'mixing', authors: 'bro'}; + modelC = {id: 1, title: 'X', resourceType: 'document', description: 'listing', authors: 'hass'}; + + }); + + + describe('when models are added to the collection ', function(){ + + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); + + }); + + + it('orders the models by the contact id', function(){ + expect(collection.at(0).get('id')).toEqual(modelC.id); + expect(collection.at(1).get('id')).toEqual(modelB.id); + expect(collection.at(2).get('id')).toEqual(modelA.id); + }); + + }); + + // describe('when the collection interacts with the server', function(){ + + // it('fetches from the correct url', function(){ + // collection = new Collection(); + // expect(collection.url).toEqual('/api/learning-resources/'); + // }); + + // }); + + // describe('when a sort event is triggered', function(){ + + // beforeEach(function(){ + // collection = new Collection(); + + // collection.add([ + // modelC, + // modelB, + // modelA + // ], + // {silent: false} // Set to true to suppress add event + // ); + + // }); + + // it('sorts by id', function(){ + // collection.trigger('sortByName'); + // expect(collection.at(0).get('id')).toEqual(modelA.id); + // expect(collection.at(1).get('id')).toEqual(modelB.id); + // expect(collection.at(2).get('id')).toEqual(modelC.id); + // }); + + // it('sorts by name', function(){ + // collection.trigger('sortById'); + // expect(collection.at(0).get('id')).toEqual(modelB.id); + // expect(collection.at(1).get('id')).toEqual(modelC.id); + // expect(collection.at(2).get('id')).toEqual(modelA.id); + // }); + + // }); + +}); + diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js new file mode 100644 index 0000000..c83d996 --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -0,0 +1,122 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var Backbone = require('../../vendor/index').Backbone; +var Controller = require('../learning-resources.controller'); +var $ = require('jquery'); +var matchers = require('jasmine-jquery-matchers'); + +// Test suite +console.log('test learning-resources.controller'); +describe('Learning resources controller', function(){ + + var controller; + + beforeEach(function(){ + controller = new Controller(); + }); + + it('can be created', function(){ + expect(controller).toBeDefined(); + }); + + describe('when it is created', function(){ + + it('has the expected routes', function(){ + expect(controller.routes).toEqual(jasmine.objectContaining({ + 'learning-resources': 'showLearningResources' + })); + }); + + it('without a container option, uses body as the container', function(){ + expect(controller.options.container).toEqual('body'); + }); + + it('with a container option, uses specified container', function(){ + var ctrl = new Controller({container: '.newcontainer'}); + expect(ctrl.options.container).toEqual('.newcontainer'); + }); + }); + + describe('when asked to showLearningResources', function(){ + + beforeEach(function(){ + jasmine.addMatchers(matchers); + + }); + + describe('and fetch is successful', function(){ + + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + options.success(); + } + ); + }); + + it('sets up the collection if it is not already', function(){ + expect(controller.collection).not.toBeDefined(); + controller.showLearningResources(); + expect(controller.collection).toBeDefined(); + }); + + it('uses the existing collection if it is already setup', function(){ + controller.showLearningResources(); + controller.collection.add({id: 'xyz'}); + controller.showLearningResources(); + expect(controller.collection.at(0).get('id')).toEqual('xyz'); + }); + + it('fetches data for the collection', function(){ + controller.showLearningResources(); + expect(controller.collection.fetch).toHaveBeenCalled(); + }); + + it('sets up the view if it is not already', function(){ + expect(controller.view).not.toBeDefined(); + controller.showLearningResources(); + expect(controller.view).toBeDefined(); + }); + + it('uses the existing view if it is already setup', function(){ + controller.showLearningResources(); + controller.view.test = true; + controller.showLearningResources(); + expect(controller.view.test).toBeTruthy(); + }); + + it('renders the view to the correct container', function() { + spyOn(controller, 'renderView').and.callThrough(); + controller.showLearningResources(); + var returnedView = controller.renderView.calls.mostRecent().object.view; + expect(returnedView).toEqual(controller.view); + expect($('body h1')).toHaveText('Contacts'); + }); + + }); + + describe('and fetch errors', function(){ + + beforeEach(function(){ + spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( + function(options){ + options.error(); + } + ); + }); + + it('renders error', function(){ + controller.showLearningResources(); + expect($('body')).toHaveText('There was a problem rendering contacts'); + }); + + }); + + }); + +}); + diff --git a/client/spa/js/learning-resource/spec/learning-resources.view.spec.js b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js new file mode 100644 index 0000000..f1a9f8b --- /dev/null +++ b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js @@ -0,0 +1,129 @@ +/* +global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, +spyOn +*/ + +// Get the code you want to test +var View = require('../learning-resources.view.js'); +var matchers = require('jasmine-jquery-matchers'); +var _ = require('../../vendor/index')._; +var Backbone = require('../../vendor/index').Backbone; + +// Test suite +console.log('test learning-resources.view'); +describe('Learning resources view ', function(){ + + var model; + var collection; + var view; + + beforeEach(function(){ + // Add some convenience tests for working with the DOM + jasmine.addMatchers(matchers); + + var Model = Backbone.Model.extend({}); + var Collection = Backbone.Collection.extend({model: Model}); + // Needs to have the fields required by the template + model = new Model({ + title: 'Meow', + resourceType: 'presentation', + description: 'Purrr', + authors: 'Mr. Meowmers' + }); + + collection = new Collection(model); + + view = new View({ + collection: collection + }); + }); + + describe('when the view is instantiated ', function() { + + it('creates the correct element', function () { + // Element has to be uppercase + expect(view.el.nodeName).toEqual('DIV'); + }); + + it('sets the correct class', function () { + view.render(); + expect(view.$el).toHaveClass('learning-resources'); + }); + + }); + + describe('when collection events happen', function(){ + + beforeEach(function () { + spyOn(view, 'render').and.callThrough(); + }); + + it('renders when something is added to the collection', function(){ + collection.trigger('add'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is reset', function(){ + collection.trigger('reset'); + expect(view.render).toHaveBeenCalled(); + }); + + it('renders when the collection is sorted', function(){ + collection.trigger('sort'); + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the view is rendered', function(){ + + it('returns the view object', function(){ + expect(view.render()).toEqual(view); + }); + it('produces the correct HTML', function(){ + view.render(); + console.log(view.$('.learning-resources')); + expect(view.$('.learning-resources')[0]).toHaveText('Meow'); + }); + + }); + + describe('when the user clicks on the Sort By Id button ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortById event on the collection', function(){ + var spy = jasmine.createSpy('sortById'); + collection.on('sortById', spy); + + view.$('.sortById').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortById').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + xdescribe('when the user clicks on the Sort By Name button ', function(){ + + xit('triggers the sortByName event on the collection', function(){ + }); + + xit('renders the view', function(){ + + }); + + }); + +}); + From 86ddc422fa743d406d4003c4d7c76611e4bc4d9d Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 17:40:31 -0800 Subject: [PATCH 12/36] edit controller expectation --- .../spa/js/learning-resource/learning-resources.controller.js | 1 - .../spec/learning-resources.controller.spec.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.controller.js b/client/spa/js/learning-resource/learning-resources.controller.js index 6bd1ff1..0b6f425 100644 --- a/client/spa/js/learning-resource/learning-resources.controller.js +++ b/client/spa/js/learning-resource/learning-resources.controller.js @@ -36,7 +36,6 @@ module.exports = Backbone.Controller.extend({ var self = this; this.getCollection().fetch({ success: function(collection, response, options){ - console.log(collection); self.getView(); self.renderView(); }, diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js index c83d996..65415a8 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -94,7 +94,7 @@ describe('Learning resources controller', function(){ controller.showLearningResources(); var returnedView = controller.renderView.calls.mostRecent().object.view; expect(returnedView).toEqual(controller.view); - expect($('body h1')).toHaveText('Contacts'); + expect($('body h3')).toHaveText('Learning Resources'); }); }); From 3b9f8642e1a475abbc976cea1a27785ff96ade14 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 17:44:38 -0800 Subject: [PATCH 13/36] add rendering for various sort functions and their associated tests --- .../learning-resources.view.js | 24 +++- .../spec/learning-resources.view.spec.js | 114 ++++++++++++++++-- 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.view.js b/client/spa/js/learning-resource/learning-resources.view.js index ced4147..7237300 100644 --- a/client/spa/js/learning-resource/learning-resources.view.js +++ b/client/spa/js/learning-resource/learning-resources.view.js @@ -16,7 +16,10 @@ module.exports = Backbone.View.extend({ events:{ 'click .sortById': 'sortById', - 'click .sortByName': 'sortByName' + 'click .sortByTitle': 'sortByTitle', + 'click .sortByResourceType': 'sortByResourceType', + 'click .sortByAuthors': 'sortByAuthors', + 'click .sortByDescription': 'sortByDescription' }, initialize: function() { @@ -42,8 +45,23 @@ module.exports = Backbone.View.extend({ this.render(); }, - sortByName: function(){ - this.collection.trigger('sortByName'); + sortByTitle: function(){ + this.collection.trigger('sortByTitle'); + this.render(); + }, + + sortByResourceType: function(){ + this.collection.trigger('sortByResourceType'); + this.render(); + }, + + sortByAuthors: function(){ + this.collection.trigger('sortByAuthors'); + this.render(); + }, + + sortByDescription: function(){ + this.collection.trigger('sortByDescription'); this.render(); } diff --git a/client/spa/js/learning-resource/spec/learning-resources.view.spec.js b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js index f1a9f8b..6f17424 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.view.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.view.spec.js @@ -82,23 +82,100 @@ describe('Learning resources view ', function(){ }); it('produces the correct HTML', function(){ view.render(); - console.log(view.$('.learning-resources')); - expect(view.$('.learning-resources')[0]).toHaveText('Meow'); + expect(view.$el[0]).toHaveText('Meow'); }); }); - describe('when the user clicks on the Sort By Id button ', function(){ + // describe('when the user clicks on the Sort By Id button ', function(){ + + // beforeEach(function(){ + // view.render(); + // }); + + // it('triggers the sortById event on the collection', function(){ + // var spy = jasmine.createSpy('sortById'); + // collection.on('sortById', spy); + + // view.$('.sortById').trigger('click'); + + // expect(spy).toHaveBeenCalled(); + + // }); + + // it('renders the view', function(){ + // spyOn(view, 'render'); + + // view.$('.sortById').trigger('click'); + + // expect(view.render).toHaveBeenCalled(); + // }); + + // }); + + describe('when the user clicks on the Title header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByTitle'); + collection.on('sortByTitle', spy); + + view.$('.sortByTitle').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByTitle').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the user clicks on the Type header ', function(){ + + beforeEach(function(){ + view.render(); + }); + + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByResourceType'); + collection.on('sortByResourceType', spy); + + view.$('.sortByResourceType').trigger('click'); + + expect(spy).toHaveBeenCalled(); + + }); + + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByResourceType').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + + }); + + describe('when the user clicks on the Authors header ', function(){ beforeEach(function(){ view.render(); }); - it('triggers the sortById event on the collection', function(){ - var spy = jasmine.createSpy('sortById'); - collection.on('sortById', spy); + it('triggers the sortByTitle event on the collection', function(){ + var spy = jasmine.createSpy('sortByAuthors'); + collection.on('sortByAuthors', spy); - view.$('.sortById').trigger('click'); + view.$('.sortByAuthors').trigger('click'); expect(spy).toHaveBeenCalled(); @@ -107,22 +184,37 @@ describe('Learning resources view ', function(){ it('renders the view', function(){ spyOn(view, 'render'); - view.$('.sortById').trigger('click'); + view.$('.sortByAuthors').trigger('click'); expect(view.render).toHaveBeenCalled(); }); }); - xdescribe('when the user clicks on the Sort By Name button ', function(){ + describe('when the user clicks on the Description header ', function(){ - xit('triggers the sortByName event on the collection', function(){ + beforeEach(function(){ + view.render(); }); - xit('renders the view', function(){ + it('triggers the sortByDescription event on the collection', function(){ + var spy = jasmine.createSpy('sortByDescription'); + collection.on('sortByDescription', spy); + + view.$('.sortByDescription').trigger('click'); + + expect(spy).toHaveBeenCalled(); }); + it('renders the view', function(){ + spyOn(view, 'render'); + + view.$('.sortByDescription').trigger('click'); + + expect(view.render).toHaveBeenCalled(); + }); + }); }); From 241a7b3cc7e3022c266e0ed1f5604d640e9200fd Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 17:48:16 -0800 Subject: [PATCH 14/36] fix th class references and remove old html structure --- .../learning-resource/learning-resources.html | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index 3fed132..db3a5b7 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -5,6 +5,13 @@ border: 1px solid #4F4D4D; color: #FAFAFA; } + .lrs-tbl-header th a:visited, .lrs-tbl-header th a { + color: #FAFAFA; + } + .lrs-tbl tr td { + background: #FBFBFB; + } + .lrs-tbl-h-n { width: 5%; } @@ -27,17 +34,17 @@

Learning Resources

- +
- - - - - + + + + + <% _.each( models, function( model ){ %> - + @@ -47,27 +54,3 @@

Learning Resources

#TitleTypeAuthorsDescriptionTitleTypeAuthorsDescription
<%- model.attributes.title %> <%- model.attributes.resourceType %> <%- model.attributes.authors %>
- - - - - - - From c70b39b75bf626369dbce231ed6b064935df1016 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 18:12:44 -0800 Subject: [PATCH 15/36] add methods and tests for sorting by title, resource type, authors, description --- .../learning-resources.collection.js | 35 ++++++- .../learning-resources.collection.spec.js | 96 ++++++++++++------- 2 files changed, 94 insertions(+), 37 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.collection.js b/client/spa/js/learning-resource/learning-resources.collection.js index 39dc190..9e70c4e 100644 --- a/client/spa/js/learning-resource/learning-resources.collection.js +++ b/client/spa/js/learning-resource/learning-resources.collection.js @@ -4,12 +4,18 @@ var Backbone = require('../vendor/index').Backbone; module.exports = Backbone.Collection.extend({ - url: '/api/learning-resources', + url: '/api/learning-resources/', initialize: function(){ this.on('sortById', this.sortById); - this.on('sortByName', this.sortByName); - this.trigger('sortByName'); + this.on('sortByTitle', this.sortByTitle); + this.on('sortByResourceType', this.sortByResourceType); + this.on('sortByAuthors', this.sortByAuthors); + this.on('sortByDescription', this.sortByDescription); + this.trigger('sortByTitle'); + this.trigger('sortByResourceType'); + this.trigger('sortByAuthors'); + this.trigger('sortByDescription'); }, sortById: function(){ @@ -19,11 +25,32 @@ module.exports = Backbone.Collection.extend({ this.sort(); }, - sortByName: function(){ + sortByTitle: function(){ this.comparator = function(model){ return model.get('title'); }; this.sort(); + }, + + sortByResourceType: function(){ + this.comparator = function(model){ + return model.get('resourceType'); + }; + this.sort(); + }, + + sortByAuthors: function(){ + this.comparator = function(model){ + return model.get('authors'); + }; + this.sort(); + }, + + sortByDescription: function(){ + this.comparator = function(model){ + return model.get('description'); + }; + this.sort(); } }); diff --git a/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js index 1716df0..cfa24fd 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.collection.spec.js @@ -17,9 +17,25 @@ describe('Learning resources collection ', function(){ beforeEach(function(){ // Set up test data - modelA = {id: 2, title: 'A', resourceType: 'link', description: 'reading', authors: 'mom'}; - modelB = {id: 0, title: 'M', resourceType: 'presentation', description: 'mixing', authors: 'bro'}; - modelC = {id: 1, title: 'X', resourceType: 'document', description: 'listing', authors: 'hass'}; + modelA = { + id: 2, + title: 'A', + resourceType: 'link', + description: 'reading', + authors: 'mom' + }; + modelB = {id: 0, + title: 'M', + resourceType: 'presentation', + description: 'mixing', + authors: 'bro' + }; + modelC = {id: 1, + title: 'X', + resourceType: 'document', + description: 'listing', + authors: 'hass' + }; }); @@ -48,45 +64,59 @@ describe('Learning resources collection ', function(){ }); - // describe('when the collection interacts with the server', function(){ + describe('when the collection interacts with the server', function(){ - // it('fetches from the correct url', function(){ - // collection = new Collection(); - // expect(collection.url).toEqual('/api/learning-resources/'); - // }); + it('fetches from the correct url', function(){ + collection = new Collection(); + expect(collection.url).toEqual('/api/learning-resources/'); + }); + + }); - // }); + describe('when a sort event is triggered', function(){ - // describe('when a sort event is triggered', function(){ + beforeEach(function(){ + collection = new Collection(); + + collection.add([ + modelC, + modelB, + modelA + ], + {silent: false} // Set to true to suppress add event + ); - // beforeEach(function(){ - // collection = new Collection(); + }); - // collection.add([ - // modelC, - // modelB, - // modelA - // ], - // {silent: false} // Set to true to suppress add event - // ); + it('sorts by title', function(){ + collection.trigger('sortByTitle'); + expect(collection.at(0).get('title')).toEqual(modelA.title); + expect(collection.at(1).get('title')).toEqual(modelB.title); + expect(collection.at(2).get('title')).toEqual(modelC.title); + }); - // }); + it('sorts by resource type', function(){ + collection.trigger('sortByResourceType'); + expect(collection.at(0).get('resourceType')).toEqual(modelC.resourceType); + expect(collection.at(1).get('resourceType')).toEqual(modelA.resourceType); + expect(collection.at(2).get('resourceType')).toEqual(modelB.resourceType); + }); - // it('sorts by id', function(){ - // collection.trigger('sortByName'); - // expect(collection.at(0).get('id')).toEqual(modelA.id); - // expect(collection.at(1).get('id')).toEqual(modelB.id); - // expect(collection.at(2).get('id')).toEqual(modelC.id); - // }); + it('sorts by authors', function(){ + collection.trigger('sortByAuthors'); + expect(collection.at(0).get('authors')).toEqual(modelB.authors); + expect(collection.at(1).get('authors')).toEqual(modelC.authors); + expect(collection.at(2).get('authors')).toEqual(modelA.authors); + }); - // it('sorts by name', function(){ - // collection.trigger('sortById'); - // expect(collection.at(0).get('id')).toEqual(modelB.id); - // expect(collection.at(1).get('id')).toEqual(modelC.id); - // expect(collection.at(2).get('id')).toEqual(modelA.id); - // }); + it('sorts by description', function(){ + collection.trigger('sortByDescription'); + expect(collection.at(0).get('description')).toEqual(modelC.description); + expect(collection.at(1).get('description')).toEqual(modelB.description); + expect(collection.at(2).get('description')).toEqual(modelA.description); + }); - // }); + }); }); From 6cb818cfb6f3f40d28a2315c00d8167165bfbb3c Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 18:25:26 -0800 Subject: [PATCH 16/36] move css to style.css --- client/css/style.css | 34 +++++++++++++++++++ .../learning-resource/learning-resources.html | 33 ------------------ 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index d745261..02fab9c 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -119,3 +119,37 @@ header.carousel .fill { .txt-mbr-desc { font-size: 14px; } + +/*---Learning Resources---*/ +/*Collection*/ + .lrs-tbl-header{ + font-weight: bold; + background: #4F4D4D; + border: 1px solid #4F4D4D; + color: #FAFAFA; + } + .lrs-tbl-header th a:visited, .lrs-tbl-header th a { + color: #FAFAFA; + } + .lrs-tbl tr td { + background: #FBFBFB; + } + + .lrs-tbl-h-n { + width: 5%; + } + .lrs-tbl-h-rt { + width: 10%; + } + .lrs-tbl-h-t { + width: 20%; + } + .lrs-tbl-h-a { + width: 10%; + } + .lrs-tbl-h-d { + width: 30%; + } + .borderless { + border: none; + } diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index db3a5b7..1dbd669 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -1,36 +1,3 @@ -

Learning Resources

From 0b753874658f34c80770eb64704512dc1736991e Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 10 Feb 2015 19:23:48 -0800 Subject: [PATCH 17/36] remove contact folder --- client/spa/js/contact/contact.controller.js | 69 ---------- client/spa/js/contact/contact.html | 3 - client/spa/js/contact/contact.model.js | 24 ---- client/spa/js/contact/contact.view.js | 37 ----- client/spa/js/contact/contacts.collection.js | 30 ----- client/spa/js/contact/contacts.controller.js | 65 --------- client/spa/js/contact/contacts.html | 8 -- client/spa/js/contact/contacts.view.js | 51 ------- .../contact/spec/contact.controller.spec.js | 92 ------------- .../spa/js/contact/spec/contact.model.spec.js | 85 ------------ .../spa/js/contact/spec/contact.view.spec.js | 93 ------------- .../contact/spec/contacts.collection.spec.js | 91 ------------- .../contact/spec/contacts.controller.spec.js | 122 ----------------- .../spa/js/contact/spec/contacts.view.spec.js | 127 ------------------ 14 files changed, 897 deletions(-) delete mode 100644 client/spa/js/contact/contact.controller.js delete mode 100644 client/spa/js/contact/contact.html delete mode 100644 client/spa/js/contact/contact.model.js delete mode 100644 client/spa/js/contact/contact.view.js delete mode 100644 client/spa/js/contact/contacts.collection.js delete mode 100644 client/spa/js/contact/contacts.controller.js delete mode 100644 client/spa/js/contact/contacts.html delete mode 100644 client/spa/js/contact/contacts.view.js delete mode 100644 client/spa/js/contact/spec/contact.controller.spec.js delete mode 100644 client/spa/js/contact/spec/contact.model.spec.js delete mode 100644 client/spa/js/contact/spec/contact.view.spec.js delete mode 100644 client/spa/js/contact/spec/contacts.collection.spec.js delete mode 100644 client/spa/js/contact/spec/contacts.controller.spec.js delete mode 100644 client/spa/js/contact/spec/contacts.view.spec.js diff --git a/client/spa/js/contact/contact.controller.js b/client/spa/js/contact/contact.controller.js deleted file mode 100644 index 0c18f26..0000000 --- a/client/spa/js/contact/contact.controller.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; -var $ = require('../vendor/index').$; -var Model = require('./contact.model'); -var View = require('./contact.view'); - -module.exports = Backbone.Controller.extend({ - - routes: { - 'contacts/:id': 'showContact', - }, - - initialize: function(){ - this.options.container = this.options.container || 'body'; - this.model = new Model(); - this.view = new View({model: this.model}); - }, - - showContact: function(contactId, cb){ - this.fetchModel(contactId, function(err){ - var view; - - if (err){ - view = this.renderError(); - } else { - view = this.renderView(); - } - - if (cb){ - cb(err, view); - } - - }.bind(this)); - }, - - fetchModel: function(contactId, cb){ - this.model.set({id: contactId}); - - this.model.fetch({ - success: function(model, response, options){ - //console.log(model); - cb(null, model); - }, - error: function(model, response, options){ - //console.error(response); - cb(response, model); - } - }); - }, - - renderToContainer: function(view){ - return $(this.options.container).html(view); - }, - - renderView: function(){ - this.renderToContainer(this.view.render().$el); - return this.view; - }, - - renderError: function(){ - return this.renderToContainer( - '

There was a problem rendering this contact

'); - } - -}); - - - diff --git a/client/spa/js/contact/contact.html b/client/spa/js/contact/contact.html deleted file mode 100644 index 605fac0..0000000 --- a/client/spa/js/contact/contact.html +++ /dev/null @@ -1,3 +0,0 @@ -

<%- name %>

-

<%- contactType %>

- diff --git a/client/spa/js/contact/contact.model.js b/client/spa/js/contact/contact.model.js deleted file mode 100644 index 8ce24dd..0000000 --- a/client/spa/js/contact/contact.model.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; - -module.exports = Backbone.Model.extend({ - - defaults: { - contactType: 'web' - }, - - urlRoot: '/api/contacts', - - initialize: function(){ - this.on('change', function(){ - this.trigger('foo', 'bar'); - }); - }, - - validate: function(attrs){ - if (!attrs.name){ - return 'name cannot be empty'; - } - } -}); diff --git a/client/spa/js/contact/contact.view.js b/client/spa/js/contact/contact.view.js deleted file mode 100644 index 4847d11..0000000 --- a/client/spa/js/contact/contact.view.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; -var _ = require('../vendor/index')._; -var $ = require('../vendor/index').$; - -var fs = require('fs'); //will be replaced by brfs in the browser - -// readFileSync will be evaluated statically so errors can't be caught -var template = fs.readFileSync(__dirname + '/contact.html', 'utf8'); - -module.exports = Backbone.View.extend({ - - className: 'contact', - - template: _.template(template), - - events: { - 'click .delete': 'destroy' - }, - - initialize: function(){ - this.listenTo(this.model, 'destroy', this.remove); - }, - - render: function(){ - var context = this.model.toJSON(); - this.$el.html(this.template(context)); - return this; - }, - - destroy: function(){ - this.model.destroy(); - } - -}); - diff --git a/client/spa/js/contact/contacts.collection.js b/client/spa/js/contact/contacts.collection.js deleted file mode 100644 index 1dd0864..0000000 --- a/client/spa/js/contact/contacts.collection.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; - -module.exports = Backbone.Collection.extend({ - - url: '/api/contacts/', - - initialize: function(){ - this.on('sortById', this.sortById); - this.on('sortByName', this.sortByName); - this.trigger('sortByName'); - }, - - sortById: function(){ - this.comparator = function(model){ - return model.get('id'); - }; - this.sort(); - }, - - sortByName: function(){ - this.comparator = function(model){ - return model.get('name'); - }; - this.sort(); - } - -}); - diff --git a/client/spa/js/contact/contacts.controller.js b/client/spa/js/contact/contacts.controller.js deleted file mode 100644 index 9730fe9..0000000 --- a/client/spa/js/contact/contacts.controller.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; -var $ = require('../vendor/index').$; -var Model = require('./contact.model'); -var Collection = require('./contacts.collection'); -var View = require('./contacts.view'); - -module.exports = Backbone.Controller.extend({ - - routes: { - 'contacts': 'showContacts' - }, - - initialize: function(){ - this.options.container = this.options.container || 'body'; - }, - - getCollection: function(){ - if (!this.collection){ - Collection = Collection.extend({model: Model}); - this.collection = new Collection(); - } - return this.collection; - }, - - getView: function(){ - if (!this.view){ - var V = View.extend({collection: this.collection}); - this.view = new V(); - } - return this.view; - }, - - showContacts: function(){ - var self = this; - this.getCollection().fetch({ - success: function(collection, response, options){ - self.getView(); - self.renderView(); - }, - error: function(collection, response, options){ - self.renderError(); - } - }); - }, - - renderToContainer: function(html){ - return $(this.options.container).html(html); - }, - - renderView: function(){ - this.renderToContainer(this.view.render().$el); - return this.view; - }, - - renderError: function(){ - return this.renderToContainer( - '

There was a problem rendering contacts

'); - } - -}); - - - diff --git a/client/spa/js/contact/contacts.html b/client/spa/js/contact/contacts.html deleted file mode 100644 index 6dbe85a..0000000 --- a/client/spa/js/contact/contacts.html +++ /dev/null @@ -1,8 +0,0 @@ -

Contacts

- - -
    - <% _.each( models, function( model ){ %> -
  • <%- model.attributes.name %>
  • - <% }); %> -
diff --git a/client/spa/js/contact/contacts.view.js b/client/spa/js/contact/contacts.view.js deleted file mode 100644 index b686ab3..0000000 --- a/client/spa/js/contact/contacts.view.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -var Backbone = require('../vendor/index').Backbone; -var _ = require('../vendor/index')._; - -var fs = require('fs'); //will be replaced by brfs in the browser - -// readFileSync will be evaluated statically so errors can't be caught -var template = fs.readFileSync(__dirname + '/contacts.html', 'utf8'); - -module.exports = Backbone.View.extend({ - - className: 'contacts', - - template: _.template(template), - - events:{ - 'click .sortById': 'sortById', - 'click .sortByName': 'sortByName' - }, - - initialize: function() { - this.listenTo(this.collection, 'add', function(){ - this.render(); - }); - this.listenTo(this.collection, 'reset', function(){ - this.render(); - }); - this.listenTo(this.collection, 'sort', function(){ - this.render(); - }); - }, - - render: function() { - var context = this.collection; - this.$el.html(this.template(context)); - return this; - }, - - sortById: function(){ - this.collection.trigger('sortById'); - this.render(); - }, - - sortByName: function(){ - this.collection.trigger('sortByName'); - this.render(); - } - -}); - diff --git a/client/spa/js/contact/spec/contact.controller.spec.js b/client/spa/js/contact/spec/contact.controller.spec.js deleted file mode 100644 index cc3602f..0000000 --- a/client/spa/js/contact/spec/contact.controller.spec.js +++ /dev/null @@ -1,92 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var Controller = require('../contact.controller'); -var $ = require('jquery'); -var matchers = require('jasmine-jquery-matchers'); - -// Test suite -console.log('test contact.controller'); -describe('Contact controller', function(){ - - var controller; - - beforeEach(function(){ - controller = new Controller(); - }); - - it('can be created', function(){ - expect(controller).toBeDefined(); - }); - - describe('when it is created', function(){ - - it('has the expected routes', function(){ - expect(controller.routes).toEqual(jasmine.objectContaining({ - 'contacts/:id': 'showContact' - })); - }); - - it('without a container option, uses body as the container', function(){ - expect(controller.options.container).toEqual('body'); - }); - - it('with a container option, uses specified container', function(){ - var ctrl = new Controller({container: '.newcontainer'}); - expect(ctrl.options.container).toEqual('.newcontainer'); - }); - }); - - describe('when calling showContact', function(){ - - beforeEach(function(){ - jasmine.addMatchers(matchers); - }); - - var success = function(callbacks){ - controller.model.set({'name': 'valid contact', 'contactType':'web'}); - callbacks.success(controller.model); - }; - var err = function(callbacks){ - callbacks.error('error', controller.model); - }; - - it('with a valid contact id, fetches the model', function(){ - spyOn(controller.model, 'fetch').and.callFake(success); - var cb = function(err, view){ - expect(err).toBeNull(); - expect(controller.model.get('name')).toEqual('valid contact'); - }; - controller.showContact(1, cb); - }); - - it('with a valid contact id, renders the view', function(){ - spyOn(controller.model, 'fetch').and.callFake(success); - spyOn(controller.view, 'render').and.callFake(function(){ - controller.view.$el = 'fake render'; - return controller.view; - }); - var cb = function(err, view){ - expect($('body')).toHaveText('fake render'); - expect(view.cid).toEqual(controller.view.cid); - }; - controller.showContact(1, cb); - }); - - it('with an invalid contact id, renders an error message', function(){ - spyOn(controller.model, 'fetch').and.callFake(err); - var cb = function(err, view){ - expect(err).toBeTruthy(); - expect($('body')).toHaveText( - 'There was a problem rendering this contact'); - }; - controller.showContact(1, cb); - }); - - }); - -}); - diff --git a/client/spa/js/contact/spec/contact.model.spec.js b/client/spa/js/contact/spec/contact.model.spec.js deleted file mode 100644 index de2c439..0000000 --- a/client/spa/js/contact/spec/contact.model.spec.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var Model = require('../contact.model'); - -// Test suite -console.log('test contact.model'); -describe('Contact model ', function(){ - - var model; - - describe('when creating a new model ', function(){ - - beforeEach(function(){ - model = new Model(); - }); - - it('sets the correct default values', function(){ - expect(model.get('contactType')).toEqual('web'); - }); - - xit('initializes with custom logic', function(){ - - }); - }); - - describe('when updating the model ', function(){ - - var errorSpy; - - beforeEach(function(){ - errorSpy = jasmine.createSpy('Invalid'); - model = new Model({ - id: 1 - }); - model.on('invalid', errorSpy); - model.save(); - }); - - it('does not save when name is empty ', function(){ - expect(errorSpy).toHaveBeenCalled(); - expect(errorSpy).toHaveBeenCalledWith( - model, - 'name cannot be empty', - { validate: true, validationError: 'name cannot be empty'} - ); - }); - - // Use if you have transformation logic on set - xit('sets the values correctly ', function(){ - - }); - - // Use if you have transformation logic on get - xit('retrieves the correct values ', function(){ - - }); - }); - - describe('when changing the state of the model ', function(){ - - var eventSpy; - - beforeEach(function(){ - eventSpy = jasmine.createSpy('Change Event'); - model = new Model({ - id: 1, - name: 'test' - }); - model.on('foo', eventSpy); - model.set({name: 'changed'}); - }); - - it('triggers the custom event foo', function(){ - expect(eventSpy).toHaveBeenCalled(); - expect(eventSpy).toHaveBeenCalledWith( - 'bar' - ); - }); - }); - -}); diff --git a/client/spa/js/contact/spec/contact.view.spec.js b/client/spa/js/contact/spec/contact.view.spec.js deleted file mode 100644 index fd27aba..0000000 --- a/client/spa/js/contact/spec/contact.view.spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var View = require('../contact.view.js'); -var matchers = require('jasmine-jquery-matchers'); -var Backbone = require('../../vendor/index').Backbone; - -// Test suite -console.log('test contact.view'); -describe('Contact view ', function(){ - - var model; - var view; - - beforeEach(function(){ - // Add some convenience tests for working with the DOM - jasmine.addMatchers(matchers); - - var Model = Backbone.Model.extend({}); - // Needs to have the fields required by the template - model = new Model({ - name: 'Contact <3', - contactType: 'web' - }); - - view = new View({ - model: model - }); - }); - - describe('when the view is instantiated ', function(){ - - it('creates the correct element', function(){ - // Element has to be uppercase - expect(view.el.nodeName).toEqual('DIV'); - }); - - it('sets the correct class', function(){ - expect(view.$el).toHaveClass('contact'); - }); - }); - - describe('when the view is rendered ', function(){ - - it('returns the view object ', function(){ - expect(view.render()).toEqual(view); - }); - - it('produces the correct HTML ', function(){ - view.render(); - expect(view.$('h1').html()).toEqual('Contact <3'); - }); - - }); - - xdescribe('when the user clicks on the Save button ', function(){ - - xit('updates the model', function(){ - }); - }); - - xdescribe('when the user clicks on ... ', function(){ - - xit('triggers the ... event', function(){ - }); - }); - - describe('when the user clicks on the Delete button ', function(){ - - beforeEach(function(){ - // Must call through otherwise the actual view function won't be called - spyOn(view, 'destroy').and.callThrough(); - // Must delegateEvents for the spy on a DOM event to work - view.delegateEvents(); - - spyOn(model, 'destroy'); - }); - - it('deletes the model', function(){ - // Must render for the event to be fired - view.render(); - view.$('.delete').trigger('click'); - - expect(view.destroy).toHaveBeenCalled(); - expect(model.destroy).toHaveBeenCalled(); - }); - }); - -}); - diff --git a/client/spa/js/contact/spec/contacts.collection.spec.js b/client/spa/js/contact/spec/contacts.collection.spec.js deleted file mode 100644 index 89a36f9..0000000 --- a/client/spa/js/contact/spec/contacts.collection.spec.js +++ /dev/null @@ -1,91 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var Collection = require('../contacts.collection'); - -// Test suite -console.log('test contacts.collection'); -describe('Contacts collection ', function(){ - - var collection; - var modelA; - var modelB; - var modelC; - - beforeEach(function(){ - // Set up test data - modelA = {id: 3, name: 'A'}; - modelB = {id: 1, name: 'B'}; - modelC = {id: 2, name: 'C'}; - }); - - - describe('when models are added to the collection ', function(){ - - beforeEach(function(){ - collection = new Collection(); - - collection.add([ - modelC, - modelB, - modelA - ], - {silent: false} // Set to true to suppress add event - ); - - }); - - - it('orders the models by the contact name', function(){ - expect(collection.at(0).get('id')).toEqual(modelA.id); - expect(collection.at(1).get('id')).toEqual(modelB.id); - expect(collection.at(2).get('id')).toEqual(modelC.id); - }); - - }); - - describe('when the collection interacts with the server', function(){ - - it('fetches from the correct url', function(){ - collection = new Collection(); - expect(collection.url).toEqual('/api/contacts/'); - }); - - }); - - describe('when a sort event is triggered', function(){ - - beforeEach(function(){ - collection = new Collection(); - - collection.add([ - modelC, - modelB, - modelA - ], - {silent: false} // Set to true to suppress add event - ); - - }); - - it('sorts by id', function(){ - collection.trigger('sortByName'); - expect(collection.at(0).get('id')).toEqual(modelA.id); - expect(collection.at(1).get('id')).toEqual(modelB.id); - expect(collection.at(2).get('id')).toEqual(modelC.id); - }); - - it('sorts by name', function(){ - collection.trigger('sortById'); - expect(collection.at(0).get('id')).toEqual(modelB.id); - expect(collection.at(1).get('id')).toEqual(modelC.id); - expect(collection.at(2).get('id')).toEqual(modelA.id); - }); - - }); - -}); - diff --git a/client/spa/js/contact/spec/contacts.controller.spec.js b/client/spa/js/contact/spec/contacts.controller.spec.js deleted file mode 100644 index def4aed..0000000 --- a/client/spa/js/contact/spec/contacts.controller.spec.js +++ /dev/null @@ -1,122 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var Backbone = require('../../vendor/index').Backbone; -var Controller = require('../contacts.controller'); -var $ = require('jquery'); -var matchers = require('jasmine-jquery-matchers'); - -// Test suite -console.log('test contacts.controller'); -describe('Contacts controller', function(){ - - var controller; - - beforeEach(function(){ - controller = new Controller(); - }); - - it('can be created', function(){ - expect(controller).toBeDefined(); - }); - - describe('when it is created', function(){ - - it('has the expected routes', function(){ - expect(controller.routes).toEqual(jasmine.objectContaining({ - 'contacts': 'showContacts' - })); - }); - - it('without a container option, uses body as the container', function(){ - expect(controller.options.container).toEqual('body'); - }); - - it('with a container option, uses specified container', function(){ - var ctrl = new Controller({container: '.newcontainer'}); - expect(ctrl.options.container).toEqual('.newcontainer'); - }); - }); - - describe('when asked to showContacts', function(){ - - beforeEach(function(){ - jasmine.addMatchers(matchers); - - }); - - describe('and fetch is successful', function(){ - - beforeEach(function(){ - spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( - function(options){ - options.success(); - } - ); - }); - - it('sets up the collection if it is not already', function(){ - expect(controller.collection).not.toBeDefined(); - controller.showContacts(); - expect(controller.collection).toBeDefined(); - }); - - it('uses the existing collection if it is already setup', function(){ - controller.showContacts(); - controller.collection.add({id: 'xyz'}); - controller.showContacts(); - expect(controller.collection.at(0).get('id')).toEqual('xyz'); - }); - - it('fetches data for the collection', function(){ - controller.showContacts(); - expect(controller.collection.fetch).toHaveBeenCalled(); - }); - - it('sets up the view if it is not already', function(){ - expect(controller.view).not.toBeDefined(); - controller.showContacts(); - expect(controller.view).toBeDefined(); - }); - - it('uses the existing view if it is already setup', function(){ - controller.showContacts(); - controller.view.test = true; - controller.showContacts(); - expect(controller.view.test).toBeTruthy(); - }); - - it('renders the view to the correct container', function() { - spyOn(controller, 'renderView').and.callThrough(); - controller.showContacts(); - var returnedView = controller.renderView.calls.mostRecent().object.view; - expect(returnedView).toEqual(controller.view); - expect($('body h1')).toHaveText('Contacts'); - }); - - }); - - describe('and fetch errors', function(){ - - beforeEach(function(){ - spyOn(Backbone.Collection.prototype, 'fetch').and.callFake( - function(options){ - options.error(); - } - ); - }); - - it('renders error', function(){ - controller.showContacts(); - expect($('body')).toHaveText('There was a problem rendering contacts'); - }); - - }); - - }); - -}); - diff --git a/client/spa/js/contact/spec/contacts.view.spec.js b/client/spa/js/contact/spec/contacts.view.spec.js deleted file mode 100644 index 079da56..0000000 --- a/client/spa/js/contact/spec/contacts.view.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/* -global jasmine, describe, it, expect, beforeEach, afterEach, xdescribe, xit, -spyOn -*/ - -// Get the code you want to test -var View = require('../contacts.view.js'); -var matchers = require('jasmine-jquery-matchers'); -var _ = require('../../vendor/index')._; -var Backbone = require('../../vendor/index').Backbone; - -// Test suite -console.log('test contacts.view'); -describe('Contacts view ', function(){ - - var model; - var collection; - var view; - - beforeEach(function(){ - // Add some convenience tests for working with the DOM - jasmine.addMatchers(matchers); - - var Model = Backbone.Model.extend({}); - var Collection = Backbone.Collection.extend({model: Model}); - // Needs to have the fields required by the template - model = new Model({ - name: 'Contact <3', - contactType: 'web' - }); - - collection = new Collection(model); - - view = new View({ - collection: collection - }); - }); - - describe('when the view is instantiated ', function() { - - it('creates the correct element', function () { - // Element has to be uppercase - expect(view.el.nodeName).toEqual('DIV'); - }); - - it('sets the correct class', function () { - view.render(); - expect(view.$el).toHaveClass('contacts'); - }); - - }); - - describe('when collection events happen', function(){ - - beforeEach(function () { - spyOn(view, 'render').and.callThrough(); - }); - - it('renders when something is added to the collection', function(){ - collection.trigger('add'); - expect(view.render).toHaveBeenCalled(); - }); - - it('renders when the collection is reset', function(){ - collection.trigger('reset'); - expect(view.render).toHaveBeenCalled(); - }); - - it('renders when the collection is sorted', function(){ - collection.trigger('sort'); - expect(view.render).toHaveBeenCalled(); - }); - - }); - - describe('when the view is rendered', function(){ - - it('returns the view object', function(){ - expect(view.render()).toEqual(view); - }); - it('produces the correct HTML', function(){ - view.render(); - expect(view.$('h1').html()).toEqual('Contacts'); - expect(view.$('.contact')[0]).toHaveText('Contact <3'); - }); - - }); - - describe('when the user clicks on the Sort By Id button ', function(){ - - beforeEach(function(){ - view.render(); - }); - - it('triggers the sortById event on the collection', function(){ - var spy = jasmine.createSpy('sortById'); - collection.on('sortById', spy); - - view.$('.sortById').trigger('click'); - - expect(spy).toHaveBeenCalled(); - - }); - - it('renders the view', function(){ - spyOn(view, 'render'); - - view.$('.sortById').trigger('click'); - - expect(view.render).toHaveBeenCalled(); - }); - - }); - - xdescribe('when the user clicks on the Sort By Name button ', function(){ - - xit('triggers the sortByName event on the collection', function(){ - }); - - xit('renders the view', function(){ - - }); - - }); - -}); - From e7f600255f9d06602abfcbb639bc6d9fe92547dd Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 11 Feb 2015 13:21:17 -0800 Subject: [PATCH 18/36] remove contact model in model-config --- server/model-config.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/model-config.json b/server/model-config.json index d532311..3763d90 100644 --- a/server/model-config.json +++ b/server/model-config.json @@ -41,9 +41,5 @@ "student": { "dataSource": "db", "public": true - }, - "contact": { - "dataSource": "db", - "public": true } } From b6da83c6a3ccd1aa21c17e07287c578556b611a8 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 11 Feb 2015 15:09:11 -0800 Subject: [PATCH 19/36] remove contact require and initialization --- client/spa/js/main.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client/spa/js/main.js b/client/spa/js/main.js index 0ca740a..ca5a121 100644 --- a/client/spa/js/main.js +++ b/client/spa/js/main.js @@ -4,15 +4,11 @@ window.Backbone = require('./vendor').Backbone; // Include your code var Resource = require('./learning-resource/learning-resource.controller'); -var Resource = require('./learning-resource/learning-resources.controller'); +var Resources = require('./learning-resource/learning-resources.controller'); // Initialize it window.resource = new Resource({router:true, container: 'body'}); -var Contact = require('./contact/contact.controller'); -var Contacts = require('./contact/contacts.controller'); -// Initialize it -window.contact = new Contact({router:true, container: 'body'}); -window.contacts = new Contacts({router:true, container: 'body'}); +window.resources = new Resources({router:true, container: 'body'}); // Additional modules go here From e2432b1dfb47792c60cb11aadcf86fbb48eda6c2 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Fri, 13 Feb 2015 13:21:37 -0800 Subject: [PATCH 20/36] rename route for continuity --- client/spa/js/learning-resource/learning-resource.controller.js | 2 +- .../learning-resource/spec/learning-resource.controller.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resource.controller.js b/client/spa/js/learning-resource/learning-resource.controller.js index 2a7aacf..3d43aba 100644 --- a/client/spa/js/learning-resource/learning-resource.controller.js +++ b/client/spa/js/learning-resource/learning-resource.controller.js @@ -8,7 +8,7 @@ var View = require('./learning-resource.view'); module.exports = Backbone.Controller.extend({ routes: { - 'learning-resource/:id': 'showLearningResource' + 'learning-resources/:id': 'showLearningResource' }, initialize: function(){ diff --git a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js index 02028dd..a191b56 100644 --- a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js @@ -28,7 +28,7 @@ describe('Learning resource controller', function(){ it('has the expected routes', function(){ expect(controller.routes).toEqual(jasmine.objectContaining({ - 'learning-resource/:id': 'showLearningResource' + 'learning-resources/:id': 'showLearningResource' })); }); From 0b0413cd301b76213f0f3672b874180550569b09 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 13:42:49 -0800 Subject: [PATCH 21/36] fix string error --- .../spa/js/learning-resource/learning-resources.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/learning-resources.controller.js b/client/spa/js/learning-resource/learning-resources.controller.js index 0b6f425..262729b 100644 --- a/client/spa/js/learning-resource/learning-resources.controller.js +++ b/client/spa/js/learning-resource/learning-resources.controller.js @@ -56,7 +56,7 @@ module.exports = Backbone.Controller.extend({ renderError: function(){ return this.renderToContainer( - '

There was a problem rendering contacts

'); + '

There was a problem rendering learning resources

'); } }); From e3e616803101b637e34bc5800c4e0113690d03f3 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 15:29:19 -0800 Subject: [PATCH 22/36] refactor some views when saving --- .../learning-resource.view.js | 108 +++++++++--------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resource.view.js b/client/spa/js/learning-resource/learning-resource.view.js index 923be4a..fc7a49a 100644 --- a/client/spa/js/learning-resource/learning-resource.view.js +++ b/client/spa/js/learning-resource/learning-resource.view.js @@ -9,6 +9,32 @@ var fs = require('fs'); //will be replaced by brfs in the browser // readFileSync will be evaluated statically so errors can't be caught var template = fs.readFileSync(__dirname + '/learning-resource.html', 'utf8'); +var showMessage = function(type, msg) { + $('#msg').empty().addClass(type) + .html(msg).fadeIn().delay(2000) + .fadeOut('slow').queue(function(remove) { + $('#msg').removeClass(type); + remove(); + }); +}; + +var renderAuthors = function(self) { + var auth = self.model.get('authors').toString().split(',').join(', '); + self.input = self.$('.editing'); + self.$('select option[value="'+self.model.get('resourceType')+'"]') + .attr('selected','selected'); + self.$('#auth').val(auth); + self.$('#author-dsp').text(auth); +}; + +var initialValues = function(self) { + self.$('#title').val(self.model.get('title')); + self.$('#desc').val(self.model.get('description')); + self.$('#auth').val(self.model.get('authors')); + self.$('select option[value="'+self.model.get('resourceType')+'"]') + .attr('selected','selected'); +}; + module.exports = Backbone.View.extend({ className: 'learning-resource', @@ -34,75 +60,53 @@ module.exports = Backbone.View.extend({ }, cancel: function(){ - this.$('#title').val(this.model.get('title')); - this.$('#desc').val(this.model.get('description')); - this.$('#auth').val(this.model.get('authors')); - this.$('select option[value="'+this.model.get('resourceType')+'"]') - .attr('selected','selected'); $('#form-area').removeClass('sty-form'); //remove bg color of form - $('#msg').empty() - .addClass('alert-warning') - .html('Changes cancelled') - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-warning'); - remove(); - }); + showMessage('alert-warning', 'Changes cancelled'); this.$el.removeClass('editing'); }, save: function(){ var view = this; - var auth = []; - if (this.$('#auth').val() === '') auth = null; - else $.each(this.$('#auth').val().split(','), function(){ - auth.push($.trim(this)); - }); - var attributes = { - title: this.$('#title').val().trim(), - resourceType: $('#resourceType option:selected').val(), - description: this.$('#desc').val().trim(), - authors: auth + + var authorsFormat = function(self) { + var auth = []; + if (self.$('#auth').val() === '') auth = null; + else $.each(self.$('#auth').val().split(','), function(){ + auth.push($.trim(self)); + }); + return auth; }; - var options = { - success: function(){ - $('#form-area').removeClass('sty-form'); - view.$el.removeClass('editing'); - $('#msg').empty() - .addClass('alert-success') - .html('Sucessfully updated') - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-success'); - remove(); - }); + + var saveArgs = { + attributes: { + title: this.$('#title').val().trim(), + resourceType: $('#resourceType option:selected').val(), + description: this.$('#desc').val().trim(), + authors: authorsFormat(this) }, - error: function(model, error){ - //server response errors if no validations specified + options: { + success: function(){ + $('#form-area').removeClass('sty-form'); + view.$el.removeClass('editing'); + showMessage('alert-success', 'Successfully updated'); + }, + error: function(model, error){ + //server response errors if no validations specified + } } }; - this.model.save(attributes, options); + + this.model.save(saveArgs.attributes, saveArgs.options); + if (this.model.validationError) { - $('#msg').empty() - .addClass('alert-danger') - .html(this.model.validationError) - .fadeIn().delay(2000).fadeOut('slow') - .queue(function(remove){ - $('#msg').removeClass('alert-danger'); - remove(); - }); + showMessage('alert-danger', this.model.validationError); } }, render: function(){ var context = this.model.toJSON(); - var auth = this.model.get('authors').toString().split(',').join(', '); this.$el.html(this.template(context)); - this.input = this.$('.editing'); - this.$('select option[value="'+this.model.get('resourceType')+'"]') - .attr('selected','selected'); - this.$('#auth').val(auth); - this.$('#author-dsp').text(auth); + renderAuthors(this); return this; }, From 6ac179a7bf57a1ba6ebe367bf3df3437e9970117 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 15:39:04 -0800 Subject: [PATCH 23/36] change view.destroy to view.remove --- client/spa/js/learning-resource/learning-resource.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/learning-resource.controller.js b/client/spa/js/learning-resource/learning-resource.controller.js index 3d43aba..3585393 100644 --- a/client/spa/js/learning-resource/learning-resource.controller.js +++ b/client/spa/js/learning-resource/learning-resource.controller.js @@ -26,7 +26,7 @@ module.exports = Backbone.Controller.extend({ //Ensures that what the view put in the DOM is removed and //any events the view had listenTo'd are removed. - if (view) view.destroy(); + if (view) view.remove(); this.initializeModel({id: learningResourceId}); From 2c3793a68b7d3b9329a02b1f1562b83d67b52d7f Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 16:08:57 -0800 Subject: [PATCH 24/36] change string to correct name --- .../spec/learning-resources.controller.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js index 65415a8..a241c99 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -111,7 +111,7 @@ describe('Learning resources controller', function(){ it('renders error', function(){ controller.showLearningResources(); - expect($('body')).toHaveText('There was a problem rendering contacts'); + expect($('body')).toHaveText('There was a problem rendering learning resources'); }); }); From 5e5d81b6783a5bd7b8daa13a07fec3cbab78f1ff Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 16:09:25 -0800 Subject: [PATCH 25/36] replace destroy with remove --- .../spec/learning-resource.controller.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js index a191b56..f8a78aa 100644 --- a/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resource.controller.spec.js @@ -80,9 +80,9 @@ describe('Learning resource controller', function(){ it('does has a previous view to remove', function(){ var oldView = controller.view; - spyOn(oldView, 'destroy'); + spyOn(oldView, 'remove'); controller.showLearningResource(222); - expect(oldView.destroy).toHaveBeenCalled(); + expect(oldView.remove).toHaveBeenCalled(); }); }); From ed24eb466802e8be95e219f781b02cb1c049ba51 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 16 Feb 2015 16:16:11 -0800 Subject: [PATCH 26/36] line too long to pass lint --- .../spec/learning-resources.controller.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js index a241c99..c42282a 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -111,7 +111,9 @@ describe('Learning resources controller', function(){ it('renders error', function(){ controller.showLearningResources(); - expect($('body')).toHaveText('There was a problem rendering learning resources'); + expect($('body')).toHaveText( + 'There was a problem rendering learning resources' + ); }); }); From cf64530d5c1360bac79ba026a8c729b997b81846 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 17 Feb 2015 12:54:44 -0800 Subject: [PATCH 27/36] remove self from authorsFormat since inside same scope --- .../js/learning-resource/learning-resource.view.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resource.view.js b/client/spa/js/learning-resource/learning-resource.view.js index fc7a49a..d00f477 100644 --- a/client/spa/js/learning-resource/learning-resource.view.js +++ b/client/spa/js/learning-resource/learning-resource.view.js @@ -68,13 +68,15 @@ module.exports = Backbone.View.extend({ save: function(){ var view = this; - var authorsFormat = function(self) { + var authorsFormat = function() { var auth = []; - if (self.$('#auth').val() === '') auth = null; - else $.each(self.$('#auth').val().split(','), function(){ - auth.push($.trim(self)); - }); - return auth; + if ($('#auth').val() === '') auth = null; + else { + $.each($('#auth').val().split(','), function(key,value){ + auth.push(value.trim()); + }); + return auth; + } }; var saveArgs = { From 16d54d3a7493a7708de8768287552007c7e9f744 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Tue, 17 Feb 2015 13:37:52 -0800 Subject: [PATCH 28/36] fix errors in name and remove remaining this --- .../js/learning-resource/learning-resource.view.js | 11 ++++++----- .../spec/learning-resource.view.spec.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resource.view.js b/client/spa/js/learning-resource/learning-resource.view.js index d00f477..43b16d8 100644 --- a/client/spa/js/learning-resource/learning-resource.view.js +++ b/client/spa/js/learning-resource/learning-resource.view.js @@ -69,13 +69,14 @@ module.exports = Backbone.View.extend({ var view = this; var authorsFormat = function() { - var auth = []; - if ($('#auth').val() === '') auth = null; + var authors = []; + // console.log($('#auth').val().split(',')); + if ($('#auth').val() === '') authors = null; else { $.each($('#auth').val().split(','), function(key,value){ - auth.push(value.trim()); + authors.push(value.trim()); }); - return auth; + return authors; } }; @@ -84,7 +85,7 @@ module.exports = Backbone.View.extend({ title: this.$('#title').val().trim(), resourceType: $('#resourceType option:selected').val(), description: this.$('#desc').val().trim(), - authors: authorsFormat(this) + authors: authorsFormat }, options: { success: function(){ diff --git a/client/spa/js/learning-resource/spec/learning-resource.view.spec.js b/client/spa/js/learning-resource/spec/learning-resource.view.spec.js index 4add979..fc4c1aa 100644 --- a/client/spa/js/learning-resource/spec/learning-resource.view.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resource.view.spec.js @@ -101,7 +101,7 @@ describe('Learning resource view ', function(){ view.render(); view.$('#title').val('changed title'); view.$('#desc').val('changed description'); - view.$('#authors').val('sis'); + view.$('#auth').val('sis'); view.$('#resourceType option:selected').val('link'); spyOn(view, 'save').and.callThrough(); view.delegateEvents(); From a9e572137829461bd89cafc567ee17798e34b6bc Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 23 Feb 2015 22:56:48 -0800 Subject: [PATCH 29/36] add bootstrap dependency and require --- client/spa-index.html | 1 + client/spa/js/vendor/index.js | 4 +++- package.json | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/spa-index.html b/client/spa-index.html index d48068c..e3b9b36 100644 --- a/client/spa-index.html +++ b/client/spa-index.html @@ -5,6 +5,7 @@ SPA + Hi, I'm the spa index from the spa folder. diff --git a/client/spa/js/vendor/index.js b/client/spa/js/vendor/index.js index 2b7cbe9..570e168 100644 --- a/client/spa/js/vendor/index.js +++ b/client/spa/js/vendor/index.js @@ -9,7 +9,9 @@ var Backbone = require('backbone'); var Controller = require('backbone.controller'); // Assign and expose jquery reference since we are using browserify -Backbone.$ = exports.$ = require('jquery'); +Backbone.$ = exports.$ = window.jQuery = require('jquery'); + +require('bootstrap'); // Help prevent 'ghost views' Backbone.View.prototype.close = function(){ diff --git a/package.json b/package.json index 48f4df2..87d36c1 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "backbone": "^1.1.2", "backbone.controller": "^0.3.1", "body-parser": "^1.10.0", + "bootstrap": "^3.3.2", "brfs": "^1.2.0", "browserify-middleware": "^4.1.0", "compression": "^1.0.3", From fc8beb8b46d5eeccc7616302152597478e93b0eb Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 23 Feb 2015 22:57:47 -0800 Subject: [PATCH 30/36] add style for checkbox column --- client/css/style.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/css/style.css b/client/css/style.css index bd23258..e793b3a 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -132,7 +132,7 @@ header.carousel .fill { color: #FAFAFA; } .lrs-tbl tr td { - background: #FBFBFB; + background: #FDFDFD; } .lrs-tbl-h-n { @@ -150,6 +150,9 @@ header.carousel .fill { .lrs-tbl-h-d { width: 30%; } + .lrs-tbl-h-de { + width: 2%; + } .borderless { border: none; } From f66c09970128c43a7c436d35acb8a2d67838a587 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 23 Feb 2015 22:59:05 -0800 Subject: [PATCH 31/36] rewrite check for this.view --- .../js/learning-resource/learning-resources.controller.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.controller.js b/client/spa/js/learning-resource/learning-resources.controller.js index 262729b..e2f707e 100644 --- a/client/spa/js/learning-resource/learning-resources.controller.js +++ b/client/spa/js/learning-resource/learning-resources.controller.js @@ -25,9 +25,11 @@ module.exports = Backbone.Controller.extend({ }, getView: function(){ - if (!this.view){ - var V = View.extend({collection: this.collection}); - this.view = new V(); + // if (this.view) { + // this.view.remove(); + // } + if (!this.view) { + this.view = new View({collection: this.collection}); } return this.view; }, From 386c21005116288736f3d321c6b469501dd01291 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Mon, 23 Feb 2015 23:01:20 -0800 Subject: [PATCH 32/36] add modal and create function for save and delete --- .../learning-resource/learning-resources.html | 198 +++++++++++++++++- .../learning-resources.view.js | 139 +++++++++++- 2 files changed, 324 insertions(+), 13 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index 1dbd669..ce5abf4 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -1,23 +1,209 @@ +
-

Learning Resources

+ +
+
Learning Resources
+ + + + + +
- +
- + <% _.each( models, function( model ){ %> - - + - + + <% }); %>
Title Type Authors Description
<%- model.attributes.title %><%- model.attributes.title.substr(0,50) %> <%- model.attributes.resourceType %> <%- model.attributes.authors %><%- model.attributes.description %><%- model.attributes.description.substr(0,60) %>
+ + + + + + diff --git a/client/spa/js/learning-resource/learning-resources.view.js b/client/spa/js/learning-resource/learning-resources.view.js index 7237300..b3746cc 100644 --- a/client/spa/js/learning-resource/learning-resources.view.js +++ b/client/spa/js/learning-resource/learning-resources.view.js @@ -2,12 +2,36 @@ var Backbone = require('../vendor/index').Backbone; var _ = require('../vendor/index')._; +var $ = require('../vendor/index').$; +var Model = require('./learning-resource.model'); var fs = require('fs'); //will be replaced by brfs in the browser // readFileSync will be evaluated statically so errors can't be caught var template = fs.readFileSync(__dirname + '/learning-resources.html', 'utf8'); +var showMessage = function(type, msg) { + $('#msg').empty().addClass(type) + .html(msg).fadeIn().delay(2000) + .fadeOut('slow').queue(function(remove) { + $('#msg').removeClass(type); + remove(); + }); +}; + +var Add = Backbone.Model.extend({ + + urlRoot: '/learning-resources', + + defaults: { + title: '', + resourceType: '', + authors: '', + description: '' + }, + +}); + module.exports = Backbone.View.extend({ className: 'learning-resources', @@ -19,7 +43,9 @@ module.exports = Backbone.View.extend({ 'click .sortByTitle': 'sortByTitle', 'click .sortByResourceType': 'sortByResourceType', 'click .sortByAuthors': 'sortByAuthors', - 'click .sortByDescription': 'sortByDescription' + 'click .sortByDescription': 'sortByDescription', + 'click .btn-lrs-add': 'renderModal', + 'click .btn-lrs-del': 'delConfirm' }, initialize: function() { @@ -35,16 +61,12 @@ module.exports = Backbone.View.extend({ }, render: function() { + console.log(this.collection.models); var context = this.collection; this.$el.html(this.template(context)); return this; }, - sortById: function(){ - this.collection.trigger('sortById'); - this.render(); - }, - sortByTitle: function(){ this.collection.trigger('sortByTitle'); this.render(); @@ -63,7 +85,110 @@ module.exports = Backbone.View.extend({ sortByDescription: function(){ this.collection.trigger('sortByDescription'); this.render(); - } + }, + + renderModal: function(){ + var view = new ResourceDialog(); + view.render(); + }, + + delConfirm: function() { + var collection = this.collection; + var checkedIds = []; + if ($(':checkbox:checked').length === 0) { + showMessage('alert-info', 'No resources selected'); + } + else { + $(':checkbox:checked').each(function () { + console.log($(this).val()); + checkedIds.push($(this).val()); + }); + $.each(checkedIds, function(key, value) { + console.log(value); + var item = collection.get({id:value}); + item.destroy(); + collection.remove({id:value}); + }); + } + }, }); +var ResourceDialog = Backbone.View.extend({ + className: 'modal fade', + attributes: { + tabindex: '-1', + role: 'dialog', + }, + + initialize: function() { + this.model = this.model || new Add(); + this.temp = _.template($('#dialog-template').html()); + }, + + events: { + 'click #lrs-add-save': 'saveNewResource', + 'click .close': 'close' + }, + + render: function() { + var context = this.model.toJSON(); + this.$el.html(this.temp(context)).appendTo(document.body); + this.$el.modal(); + return this; + }, + + close: function() { + this.remove(); + }, + + saveNewResource: function(){ + + var newResource = new Model(); + + var authorsFormat = function(authored) { + var authors = []; + // console.log($('#auth').val().split(',')); + // if ($('#lrs-add-authors').val() === '') authors = null; + // else { + // $.each($('#lrs-add-authors').val().split(','), function(key,value){ + // console.log(value); + // authors.push(value.trim()); + // }); + // } + authored(authors); + }; + + var saveArgs = { + attributes: { + title: $('#lrs-add-title').val().trim(), + resourceType: $('#lrs-add-resourceType option:selected').val(), + description: $('#lrs-add-description').val().trim(), + authors: $('#lrs-add-authors').val().split(',') + }, + options: { + success: function(response){ + $('#lrs-dismiss').click(); + setTimeout(function() { + // collection.fetch(); + // collection.add(saveArgs.attributes); + $('table tr td').find('[value="na"]').attr('value', response.id); + }, 400); + + showMessage('alert-success', 'Successfully updated'); + console.log('success'); + }, + error: function(model, error){ + //server response errors if no validations specified + } + } + }; + + newResource.save(saveArgs.attributes, saveArgs.options); + + // if (newResource.validationError) { + // showMessage('alert-danger', this.model.validationError); + // } + }, + +}); From 7fe43e22a12477fb9cf267b6c0f354721722c0e0 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 25 Feb 2015 13:12:46 -0800 Subject: [PATCH 33/36] tell jslint to ignore window --- client/spa/js/vendor/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/spa/js/vendor/index.js b/client/spa/js/vendor/index.js index 570e168..d12159b 100644 --- a/client/spa/js/vendor/index.js +++ b/client/spa/js/vendor/index.js @@ -1,4 +1,5 @@ 'use strict'; +/*jslint browser: true*/ // Expose underscore exports._ = require('underscore'); From 1bb93f0d39043ef63e55c4c97ac50608d1b07920 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 25 Feb 2015 13:14:02 -0800 Subject: [PATCH 34/36] change expectation selector to fix test failure --- .../spec/learning-resources.controller.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js index c42282a..51d5539 100644 --- a/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js +++ b/client/spa/js/learning-resource/spec/learning-resources.controller.spec.js @@ -94,7 +94,7 @@ describe('Learning resources controller', function(){ controller.showLearningResources(); var returnedView = controller.renderView.calls.mostRecent().object.view; expect(returnedView).toEqual(controller.view); - expect($('body h3')).toHaveText('Learning Resources'); + expect($('.page-title')).toHaveText('Learning Resources'); }); }); From 084f5f05edb061c6a5bf10b80685157588c01d33 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 25 Feb 2015 13:16:14 -0800 Subject: [PATCH 35/36] change ids to classes and add select all checkbox --- .../learning-resource/learning-resources.html | 66 +++---------------- 1 file changed, 8 insertions(+), 58 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.html b/client/spa/js/learning-resource/learning-resources.html index ce5abf4..187f206 100644 --- a/client/spa/js/learning-resource/learning-resources.html +++ b/client/spa/js/learning-resource/learning-resources.html @@ -4,7 +4,6 @@ /*line-height: 50px;*/ font-size: 26px; display: inline-block; - bac } .btn-lrs-add, .btn-lrs-del { float: right; @@ -28,7 +27,7 @@ border-radius-top-left: 20px; margin-bottom: 70px; } -input#lrs-check { +input.lrs-check, input.lrs-select-all { width: 20px; height: 20px; border: none !important; @@ -93,7 +92,7 @@ Type Authors Description - + <% _.each( models, function( model ){ %> @@ -101,65 +100,16 @@ <%- model.attributes.resourceType %> <%- model.attributes.authors %> <%- model.attributes.description.substr(0,60) %> - + <% }); %>
- - - - + + + From c77044d7eca51aad35fef9590f718b3a74e1db42 Mon Sep 17 00:00:00 2001 From: jstadragon Date: Wed, 25 Feb 2015 13:17:30 -0800 Subject: [PATCH 36/36] remove modal instance on save and add function for select all when deleting --- .../learning-resources.view.js | 197 ++++++++++-------- 1 file changed, 110 insertions(+), 87 deletions(-) diff --git a/client/spa/js/learning-resource/learning-resources.view.js b/client/spa/js/learning-resource/learning-resources.view.js index b3746cc..289f308 100644 --- a/client/spa/js/learning-resource/learning-resources.view.js +++ b/client/spa/js/learning-resource/learning-resources.view.js @@ -1,4 +1,5 @@ 'use strict'; +/*jslint browser: true*/ var Backbone = require('../vendor/index').Backbone; var _ = require('../vendor/index')._; @@ -32,6 +33,90 @@ var Add = Backbone.Model.extend({ }); +//Modal View + +var ResourceDialog = Backbone.View.extend({ + className: 'modal fade', + attributes: { + tabindex: '-1', + role: 'dialog', + }, + + initialize: function() { + this.model = new Add(); + this.temp = _.template($('#dialog-template').html()); + }, + + events: { + 'click #lrs-add-save': 'saveNewResource', + 'click .close, #cancel': 'close' + }, + + render: function() { + var context = this.model.toJSON(); + this.$el.html(this.temp(context)).appendTo(document.body); + this.$el.modal(); + return this; + }, + + close: function() { + this.remove(); + $('.modal').remove(); + showMessage('alert-warning', 'Changes cancelled'); + }, + + saveNewResource: function(){ + var collection = this.collection; + + var newResource = new Model(); + + var authorsFormat = function() { + var authors = []; + // console.log($('#auth').val().split(',')); + // if ($('#lrs-add-authors').val() === '') authors = null; + // else { + // $.each($('#lrs-add-authors').val().split(','), function(key,value){ + // console.log(value); + // authors.push(value.trim()); + // }); + // } + return authors; + }; + + var saveArgs = { + attributes: { + title: $('#lrs-add-title').val().trim(), + resourceType: $('#lrs-add-resourceType option:selected').val(), + description: $('#lrs-add-description').val().trim(), + authors: $('#lrs-add-authors').val().split(',') + }, + options: { + success: function(response){ + $('#lrs-dismiss').click(); + setTimeout(function() { + collection.fetch(); + // collection.add(saveArgs.attributes); + $('table tr td').find('[value="na"]').attr('value', response.id); + $('.modal').remove(); + }, 450); + showMessage('alert-success', 'Successfully updated'); + }, + error: function(model, error){ + //server response errors if no validations specified + } + } + }; + newResource.save(saveArgs.attributes, saveArgs.options); + + if (newResource.validationError) { + showMessage('alert-danger', newResource.validationError); + } + }, + +}); + +//Main Collection View + module.exports = Backbone.View.extend({ className: 'learning-resources', @@ -45,7 +130,8 @@ module.exports = Backbone.View.extend({ 'click .sortByAuthors': 'sortByAuthors', 'click .sortByDescription': 'sortByDescription', 'click .btn-lrs-add': 'renderModal', - 'click .btn-lrs-del': 'delConfirm' + 'click .btn-lrs-del': 'delConfirm', + 'click .lrs-select-all': 'selectAll' }, initialize: function() { @@ -58,10 +144,13 @@ module.exports = Backbone.View.extend({ this.listenTo(this.collection, 'sort', function(){ this.render(); }); + this.listenTo(this.collection, 'remove', function(){ + this.render(); + }); }, render: function() { - console.log(this.collection.models); + this.delegateEvents(); var context = this.collection; this.$el.html(this.template(context)); return this; @@ -88,23 +177,36 @@ module.exports = Backbone.View.extend({ }, renderModal: function(){ - var view = new ResourceDialog(); - view.render(); + var modal = new ResourceDialog({ + collection: this.collection + }); + modal.render(); + }, + + selectAll: function(){ + if ($('.lrs-select-all')[0].checked === true) { + $('.lrs-check').each(function() { + this.checked = true; + }); + } + else { + $('.lrs-check').each(function() { + this.checked = false; + }); + } }, delConfirm: function() { var collection = this.collection; var checkedIds = []; - if ($(':checkbox:checked').length === 0) { + if ($('.lrs-check:checked').length === 0) { showMessage('alert-info', 'No resources selected'); } else { - $(':checkbox:checked').each(function () { - console.log($(this).val()); + $('.lrs-check:checked').each(function () { checkedIds.push($(this).val()); }); $.each(checkedIds, function(key, value) { - console.log(value); var item = collection.get({id:value}); item.destroy(); collection.remove({id:value}); @@ -113,82 +215,3 @@ module.exports = Backbone.View.extend({ }, }); - -var ResourceDialog = Backbone.View.extend({ - className: 'modal fade', - attributes: { - tabindex: '-1', - role: 'dialog', - }, - - initialize: function() { - this.model = this.model || new Add(); - this.temp = _.template($('#dialog-template').html()); - }, - - events: { - 'click #lrs-add-save': 'saveNewResource', - 'click .close': 'close' - }, - - render: function() { - var context = this.model.toJSON(); - this.$el.html(this.temp(context)).appendTo(document.body); - this.$el.modal(); - return this; - }, - - close: function() { - this.remove(); - }, - - saveNewResource: function(){ - - var newResource = new Model(); - - var authorsFormat = function(authored) { - var authors = []; - // console.log($('#auth').val().split(',')); - // if ($('#lrs-add-authors').val() === '') authors = null; - // else { - // $.each($('#lrs-add-authors').val().split(','), function(key,value){ - // console.log(value); - // authors.push(value.trim()); - // }); - // } - authored(authors); - }; - - var saveArgs = { - attributes: { - title: $('#lrs-add-title').val().trim(), - resourceType: $('#lrs-add-resourceType option:selected').val(), - description: $('#lrs-add-description').val().trim(), - authors: $('#lrs-add-authors').val().split(',') - }, - options: { - success: function(response){ - $('#lrs-dismiss').click(); - setTimeout(function() { - // collection.fetch(); - // collection.add(saveArgs.attributes); - $('table tr td').find('[value="na"]').attr('value', response.id); - }, 400); - - showMessage('alert-success', 'Successfully updated'); - console.log('success'); - }, - error: function(model, error){ - //server response errors if no validations specified - } - } - }; - - newResource.save(saveArgs.attributes, saveArgs.options); - - // if (newResource.validationError) { - // showMessage('alert-danger', this.model.validationError); - // } - }, - -});