Releases: dmytroyarmak/marionette-contact-manager
Step 9 - Store data in localStorage
-
Install Backbone.localStorage
bower install Backbone.localStorage --save
-
Add script in
index.html
<script src="vendor/Backbone.localStorage/backbone.localStorage.js"></script>
-
Add
localStorage
toContactManager.Collections.Contacts
ContactManager.Collections.Contacts = Backbone.Collection.extend({ model: ContactManager.Models.Contact, localStorage: new Backbone.LocalStorage('contacts') });
-
Start app without sample data
<script> $(function() { ContactManager.start(); }); </script>
var contacts = new ContactManager.Collections.Contacts(),
-
Fetch collection in controller
this._contacts.fetch();
-
Create sample data if collection is empty
if (this._contacts.isEmpty()) { this._createSampleData(); }
_createSampleData: function() { _.each([ { id: 1, name : 'Terrence S. Hatfield', tel: '651-603-1723', email: '[email protected]', avatar: '1.jpg' }, { id: 2, name : 'Chris M. Manning', tel: '513-307-5859', email: '[email protected]', avatar: '2.jpg' }, { id: 3, name : 'Ricky M. Digiacomo', tel: '918-774-0199', email: '[email protected]', avatar: '3.jpg' }, { id: 4, name : 'Michael K. Bayne', tel: '702-989-5145', email: '[email protected]', avatar: '4.jpg' }, { id: 5, name : 'John I. Wilson', tel: '318-292-6700', email: '[email protected]', avatar: '5.jpg' }, { id: 6, name : 'Rodolfo P. Robinett', tel: '803-557-9815', email: '[email protected]', avatar: '6.jpg' }], function(contact) { this._contacts.create(contact); }, this); }
-
Use
destroy
instead ofremove
this.listenTo(contactsView, 'itemview:delete:clicked', function(contactView) { contactView.model.destroy(); });
-
Use
save
instead ofset
this.listenTo(editContactForm, 'form:submitted', function(attrs) { contact.save(attrs); this.showContacts(); });
-
Remove avatar generator
from ContactManager.Models.Contact
-
Change contact creation code
this.listenTo(newContactForm, 'form:submitted', function(attrs) { attrs.avatar = _.random(1, 15) + '.jpg'; this._contacts.create(attrs); this.showContacts(); });
Step 8 - Remove dependency to router
-
Do not trigger routes when navigate in
ContactManager.Controller
instead ofthis._router.navigate('contacts', true);
use
this._router.navigate('contacts'); this.showContacts();
-
In
ContactManager.Views.Contacts
on click "Add Contact" button trigger event instead of change url:add class in template
<p class="text-center"> <a href="#contacts/new" class="btn btn-lg btn-outline add-contact-btn">Add Contact</a> </p>
add
triggers
object to viewtriggers: { 'click .add-contact-btn': 'addContact:clicked' }
-
Listen to
addContact:clicked
in controllerthis.listenTo(contactsView, 'addContact:clicked', function() { this._router.navigate('contacts/new'); this.newContact(); });
-
In
ContactManager.Views.ContactForm
on click "Cancel" button trigger event instead of change url:
add class in template<div class="col-sm-3"> <a href="#contacts" class="btn btn-outline btn-lg btn-block form-cancel-btn">Cancel</a> </div>
add
triggers
object to viewtriggers: { 'click .form-cancel-btn': 'form:canceled' },
-
Listen to
form:canceled
in controllerthis.listenTo(newContactForm, 'form:canceled', function() { this._router.navigate('contacts'); this.showContacts(); });
-
Move
this._router.navigate
to end of corresponding methodsshowContacts: function() { /* ... */ this._router.navigate('contacts'); },
newContact: function() { /* ... */ this._router.navigate('contacts/new'); },
editContact: function(id) { /* ... */ this._router.navigate('contacts/edit/' + id); } else { this.showContacts(); } }
-
In
ContactManager.Views.Contact
on click "Delete" button trigger event
instead of:events: { 'click .delete-contract': 'onClickDelete' }, onClickDelete: function(e) { e.preventDefault(); this.model.collection.remove(this.model); }
use
triggers: { 'click .delete-contract': 'delete:clicked' }
-
Listen to
itemview:delete:clicked
in controllerthis.listenTo(contactsView, 'itemview:delete:clicked', function(contactView) { this._contacts.remove(contactView.model); });
-
In
ContactManager.Views.Contact
on click "Edit" button trigger event<a href="#contacts/edit/<%- id %>"><span class="glyphicon glyphicon-pencil edit-contract"></span></a>
triggers: { 'click .delete-contract': 'delete:clicked', 'click .edit-contract': 'edit:clicked' }
-
Listen to
itemview:edit:clicked
in controllerthis.listenTo(contactsView, 'itemview:edit:clicked', function(contactView) { this.editContact(contactView.model.id); });
Step 7 - Use AppRouter
-
In
router.js
changeBackbone.Router.extend
toMarionette.AppRouter.extend
-
Remove all routes except
home
ContactManager.Router = Backbone.Router.extend({ routes: { '': 'home' } });
-
Move
home
method fromContactManager.Controller
toContactManager.Router
home: function() { this.navigate('contacts', { trigger: true, replace: true }); }
-
Bind contacts routes to router in application initialization
ContactManager.addInitializer(function(data) { var contacts = new ContactManager.Collections.Contacts(data.contacts), router = new ContactManager.Router(), controller = new ContactManager.Controller({ contacts: contacts, router: router, mainRegion: this.mainRegion }); router.processAppRoutes(controller, { 'contacts': 'showContacts', 'contacts/new': 'newContact', 'contacts/edit/:id': 'editContact' }); });
Step 6 - Add Controller
-
Create file
controller.js
and add inindex.html
-
Move all router's handlers from app initialization to controller
ContactManager.Controller = Marionette.Controller.extend({ initialize: function(options) { this._contacts = options.contacts; this._router = options.router; this._mainRegion = options.mainRegion; }, home: function() { this._router.navigate('contacts', { trigger: true, replace: true }); }, showContacts: function() { var contactsView = new ContactManager.Views.Contacts({ collection: this._contacts }); ContactManager.mainRegion.show(contactsView); }, newContact: function() { var newContactForm = new ContactManager.Views.ContactForm({ model: new ContactManager.Models.Contact() }); this.listenTo(newContactForm, 'form:submitted', function(attrs) { attrs.id = this._contacts.isEmpty() ? 1 : (_.max(this._contacts.pluck('id')) + 1); this._contacts.add(attrs); this._router.navigate('contacts', true); }); ContactManager.mainRegion.show(newContactForm); }, editContact: function(id) { var contact = this._contacts.get(id), editContactForm; if (contact) { editContactForm = new ContactManager.Views.ContactForm({ model: contact }); this.listenTo(editContactForm, 'form:submitted', function(attrs) { contact.set(attrs); this._router.navigate('contacts', true); }); ContactManager.mainRegion.show(editContactForm); } else { this._router.navigate('contacts', true); } } });
-
Crate controller on application initialization and bind to router
ContactManager.addInitializer(function(data) { var contacts = new ContactManager.Collections.Contacts(data.contacts), router = new ContactManager.Router(), controller = new ContactManager.Controller({ contacts: contacts, router: router, mainRegion: this.mainRegion }); router.on('route:home', controller.home, controller); router.on('route:showContacts', controller.showContacts, controller); router.on('route:newContact', controller.newContact, controller); router.on('route:editContact', controller.editContact, controller); });
Step 5 - Use Application
-
Let
ContactManager
inapp.js
be instance ofMarionette.Application
instead of simple objectvar ContactManager = new Marionette.Application({ Models: {}, Collections: {}, Views: {} });
-
Use
addInitializer
instead of methodstart
ContactManager.addInitializer(function(data) { ... });
-
Add
mainRegion
toContactManager
ContactManager.addRegions({ mainRegion: '.main-container' });
and use as:
ContactManager.mainRegion.show(newContactForm);
-
Move
Backbone.history.start()
toinitialize:after
callbackMyApp.on('initialize:after', function(options){ if (Backbone.history){ Backbone.history.start(); } });
Step 4 - Use Region
-
Create
mainRegion
in start method ofapp.js
var mainRegion = new Marionette.Region({ el: ".main-container" });
-
Instead of change html of
.main-container
use region'sshow
methodChange
$('.main-container').html(contactsView.render().$el); ... $('.main-container').html(newContactForm.render().$el); ... $('.main-container').html(editContactForm.render().$el);
to
mainRegion.show(contactsView); ... mainRegion.show(newContactForm); ... mainRegion.show(editContactForm);
Step 3 - Use CompositeView
-
In
views/contacts.js
changeBackbone.View.extend
to 'Marionette.CompositeView.extend' -
Instead of
template: _.template($('#tpl-contacts').html()),
use just
template: '#tpl-contacts',
-
Remove
renderOne
andrender
methods -
Add
itemView
anditemViewContainer
itemView: ContactManager.Views.Contact, itemViewContainer: '.contacts-container'
-
Remove
modelEvents
fromContactManager.Views.Contact
Step 2 - Use ItemView
- Use ItemView in
views/contactForm.js
:-
Change
Backbone.View.extend
to 'Marionette.ItemView.extend' -
Instead of
template: _.template($('#tpl-new-contact').html()),
use just
template: '#tpl-new-contact',
-
Remove render method
-
Add
serializeData
methodserializeData: function() { return _.extend(this.model.toJSON(), { isNew: this.model.isNew() }); },
-
Use
ui
to organize UI elementsui: { nameInput: '.contact-name-input', telInput: '.contact-tel-input', emailInput: '.contact-email-input' },
this.trigger('form:submitted', { name: this.ui.nameInput.val(), tel: this.ui.telInput.val(), email: this.ui.emailInput.val() });
-
- Use ItemView in
views/contact.js
:-
Change
Backbone.View.extend
to 'Marionette.ItemView.extend' -
Instead of
template: _.template($('#tpl-contact').html()),
use just
template: '#tpl-contact',
-
Remove render method
-
Check in browser
-
Instead of
initialize: function() { this.listenTo(this.model, 'remove', this.remove); },
use
modelEvents
modelEvents: { 'remove': 'close' },
-
Step 10 - Store data in RESTful backend
-
Remove backbone.localStorage
-
Add url to collection
ContactManager.Collections.Contacts = Backbone.Collection.extend({ model: ContactManager.Models.Contact, url: 'contacts' });
-
Add
ajaxPrefilter
toapp.js
ContactManager.addInitializer(function() { $.ajaxPrefilter(function( options, originalOptions, jqXHR ) { options.url = 'http://contact-manager-api.herokuapp.com/' + options.url; }); });
-
Make sure that collection is fetched before any action
ContactManager.Controller = Marionette.Controller.extend({ initialize: function(options) { this._router = options.router; this._mainRegion = options.mainRegion; this._contacts = options.contacts; }, showContacts: function() { this._fetchContacts().then(_.bind(this.showContactsSync, this)); }, showContactsSync: function() { var contactsView = new ContactManager.Views.Contacts({ collection: this._contacts }); this.listenTo(contactsView, 'addContact:clicked', this.newContactSync); this.listenTo(contactsView, 'itemview:delete:clicked', function(contactView) { contactView.model.destroy(); }); this.listenTo(contactsView, 'itemview:edit:clicked', function(contactView) { this.editContactSync(contactView.model.id); }); ContactManager.mainRegion.show(contactsView); this._router.navigate('contacts'); }, newContact: function() { this._fetchContacts().then(_.bind(this.newContactSync, this)); }, newContactSync: function() { var newContactForm = new ContactManager.Views.ContactForm({ model: new ContactManager.Models.Contact() }); this.listenTo(newContactForm, 'form:submitted', function(attrs) { attrs.avatar = _.random(1, 15) + '.jpg'; this._contacts.create(attrs, {wait: true}); this.showContactsSync(); }); this.listenTo(newContactForm, 'form:canceled', this.showContactsSync); ContactManager.mainRegion.show(newContactForm); this._router.navigate('contacts/new'); }, editContact: function(id) { this._fetchContacts().then(_.bind(this.editContactSync, this, id)); }, editContactSync: function(id) { var contact = this._contacts.get(id), editContactForm; if (contact) { editContactForm = new ContactManager.Views.ContactForm({ model: contact }); this.listenTo(editContactForm, 'form:submitted', function(attrs) { contact.save(attrs); this.showContactsSync(); }); this.listenTo(editContactForm, 'form:canceled', this.showContactsSync); ContactManager.mainRegion.show(editContactForm); this._router.navigate('contacts/edit/' + id); } else { this.showContacts(); } }, _fetchContacts: function() { this._contactsFetching = this._contactsFetching || this._contacts.fetch(); return this._contactsFetching; } });
Step 1 - Install Marionette.js
Step 1
-
Install marionette using bower:
bower install marionette --save
-
Add script to
index.html
:<script src="vendor/marionette/lib/backbone.marionette.js"></script>
-
Check in DevTools