I have a backbone app that uses require.js.
Prior to using require my Backbone router looked something like this.
APP.views = {};
APP.Router = Backbone.Router.extend({
routes: {
'(/)' : 'index',
'about(/)' : 'about'
},
initialize : function(){
Backbone.history.start({ pushState: true });
},
index: function() {
this.showView( new APP.Views.IndexView() );
},
about: function() {
this.showView( new APP.Views.AboutView() );
},
showView : function( view ) {
if ( APP.views.current ) {
APP.views.current.remove();
}
APP.views.current = view;
$( '#page' ).html( view.render().$el );
}
});
I would stash the 'current' view in a global variable and kill the existing view each time a route was changed and life was good.
But, how do I achieve this with require.js ?
My requirejs router currently looks like the following but I'm not sure how to remove the existing views. Although, I have not noticed any of the typical "zombie view" symptoms I feel like I should be removing the existing views.
define( function( require ){
// DEPS
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone');
// ROUTER
var Router = Backbone.Router.extend({
routes: {
'(/)' : 'index',
'about(/)' : 'about'
},
initialize : function(){
Backbone.history.start({ pushState: true });
},
index: function(){
this.showPage('index');
},
about: function() {
this.showPage('about');
},
showPage : function( pageName ) {
var view = 'views/pages/' + pageName;
require( [ view ] , function( Page ) {
var page = new Page();
$('#page').html( page.render().el );
});
}
});
return Router ;
});
Even before using require.js, a global wasn't needed.
Just put the current view into a router property.
initialize : function() {
this.$page = $('#page');
Backbone.history.start({ pushState: true });
},
showView : function(view) {
if (this.current) this.current.remove();
this.$page.html((this.current = view).render().el);
}
Then, same thing applies to your async require case:
showPage : function(pageName) {
if (this.current) this.current.remove();
var view = 'views/pages/' + pageName,
self = this;
require([view], function(Page) {
self.$page.html((self.current = new Page()).render().el);
});
}
But even then, I don't feel like requiring each view with an async require is worth it. You're just slowing down your application with a lot of extra requests.
Just define the dependencies for each module.
define([
'jquery',
'backbone',
'views/index',
'views/about'
], function($, Backbone, IndexView, AboutView){
// ...
});
While in development, you'll see a lot of request each time you refresh, but when ready for production, build a minified bundle of all the js files with require optimizer.
Also note that you can have module scope global, which are just local variable declared at the root of a module scope (IIFE or with require.js).
(function() {
var currentView;
var Router = Backbone.Router.extend({
// ...snip...
showView: function(view) {
if (currentView) currentView.remove();
this.$page.html((currentView = view).render().el);
}
});
})();
I want to create an ember application as follows:
var app = Ember.Application.create({
rootElement: document.getElementById('root'),
name: 'my-application',
IndexRoute: Ember.Route.extend({
model: function() {
console.log('model');
},
afterModel: function(model, transition) {
},
template: Ember.Handlebars.compile('<div id="test"></div>'),
}),
locationType: 'hash',
});
The problem is that I dont get the test div in the DOM. How come?
http://jsbin.com/koyihapuze/edit?html,js,console,output
1) in your code this.namespace.TEMPLATES is undefined ( inside resolver ) so you
Fix code like
resolveTemplate: function(parsedName) {
var templatePath = parsedName.fullNameWithoutType.replace('.', '/');
var template = this.namespace.TEMPLATES && this.namespace.TEMPLATES[templatePath];
if (!template) {
template = this._super(parsedName);
}
return template;
},
After you'll find new div.ember-view element inside application container
2) template is not property of route ( as in your code ), you can define ApplicationView with template or templateName in router and solve resolver errors
P.S. It's better to start with Ember using ember-cli
I try implement lazy loading of angular services, controllers, directives, filters, I found way to do something similar using RequireJS. But I cant`t found how to load service into controller dynamically only when it is needed.
My controller:
require(["app"], function (app) {
app.controller('dialogsCtrl',function($scope,dialogsSer){
$scope.tooltip = function(){
dialogsSer.tooltip(); // work ok
}
...
I want to implement something like (also to still possibility inject service as angular inline way):
require(["app"], function (app) {
app.controller('dialogsCtrl',function($scope){
$scope.tooltip = function(){
// load service only if tooltip function is called
require(["dialogsSer"], function (dialogsSer){
dialogsSer.tooltip();
);
}
...
require config:
require.config({
baseUrl: ...,
paths: {
....
'dialogService':'resources/web/app/services/dialogsSer',
...
deps: ['app']
});
Dialogs servise:
require(["app","directives"], function (app) {
app.service('dialogsSer', function($http,$uibModal){
var ds = {};
...
I found angular $injector method for insert service on run time.
require(['dialogService','userSer'],function(){
if(!$rootScope.userSer)$rootScope.userSer = $injector.get("userSer");
if(!$rootScope.dialogsSer)$rootScope.dialogsSer = $injector.get("dialogsSer");
$scope.$apply();
});
When I define a route explicitly, Ember fails to render the associated template. Do I have to specify in the route object the renderTemplate property every time I create an explicit route? Just to be more clear, here is my example:
define(['ember'],
function(Ember) {
"use strict";
var DudeRoute = Ember.Route.extend({
model: function() {
},
setupController: function() {
},
renderTemplate: function() {
}
});
return DudeRoute;
});
and if I specify in my app like this:
define([ ... ],
function(
Router,
IndexRoute,
DudeRoute,
ApplicationController,
IndexController
) {
"use strict";
/*Module Pattern*/
var App = {
LOG_TRANSITIONS: true,
Router: Router,
// Load routes
IndexRoute: IndexRoute,
DudeRoute: DudeRoute,
//Load Controllers
ApplicationController: ApplicationController,
IndexController: IndexController
//Load Models
//Load Views
};
return App;
});
The whole thing falls apart, it does not render my template. Though if I remove DudeRoute everything works fine.
OK, I figured it out. So My problem was, that I was using some automation to generate code for Route/Controller/View/templates. And what I did and you can see from the code too is that I stupidly set the renderTemplate method to do nothing. So by removing it it will work.
I'm beginning a large scale javascript application with Marionette. A Marionette application has a concept of application Modules and RequireJS is also used to break code into modules,
currently I have this for the start of my application:
require([ "jquery", "underscore", "backbone", "marionette" ],
function ($, _, Backbone, Marionette) {
$(function() {
App = new Marionette.Application();
App.addInitializer(function(options) {
App.addRegions({
mainArea: "#mainArea"
});
});
App.on("start", function() {
// done starting up, do stuff here
});
App.start();
});
});
If I wanted to add a view would I do something like the following in a file?
require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {
App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
return Marionette.ItemView.extend({
//define view stuff in here
});
});
});
I'm not sure how I'd get this code to actually run, any help is much appreciated
Marionette's modules are meant to be a simple alternative to RequireJS (and other) module formats. I would not recommend using them together, as noted in the wiki:
https://github.com/marionettejs/backbone.marionette/wiki/AMD-Modules-vs-Marionette's-Modules
IMHO I like to differ from the view point stated above "Marionette's modules are meant to be a simple alternative to RequireJS (and other) module formats."
I like to draw a comparison between Require.js modules and Marionette.js modules with C#'s assembly and namespace concepts. Marionette.js's modules help us group definitions of various building blocks based on functionality, while Require.js could be used to load / inject dependencies.
Again, this is my view / understanding (based on discussions with David Sulc on his book 'Structuring Backbone Code with RequireJS and Marionette Modules'), which has helped in my implementation. In a way we can use Marionette.js and Require.js together as described below.
The example below is a small Library Manager app (sample) which could be found online # https://github.com/srihari-sridharan/LibraryManagement. The code below (omitting insignificant bits and pieces) creates the application object and renders the list of books after initialization. Please find it here - https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/app.js
define([
'marionette',
'modules/config/marionette/regions/dialog'], function (Marionette) {
// Create the application object
var LibraryManager = new Marionette.Application();
// Add regions to the application object
LibraryManager.addRegions({
//Header
headerRegion: "#header-region",
//Main
mainRegion: "#main-region",
//Footer
footerRegion: "footer-region",
//Overlay Dialog
dialogRegion: Marionette.Region.Dialog.extend({
el:"#dialog-region"
})
});
// Subscribe to Initialize After event.
LibraryManager.on('initialize:after', function() {
if(Backbone.history){
require(['modules/books/booksModule', 'modules/about/aboutModule'], function (){
Backbone.history.start();
if(LibraryManager.getCurrentRoute() === ''){
LibraryManager.trigger("books:list");
}
});
}
});
// Return the application object.
return LibraryManager;
});
Next we define the module / sub-modules based on the functionality. This will also have a module specific router and will wire controllers and handle routes. Note the require call to controllers. This code is present in https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/booksModule.js
define(['app'], function (LibraryManager) {
// Define a new module for Books - BooksModule
LibraryManager.module('BooksModule', function (BooksModule, LibraryManager, Backbone, Marionette, $, _) {
BooksModule.startWithParent = false;
BooksModule.onStart = function () {
console.log('Starting BooksModule.');
};
BooksModule.onStop = function () {
console.log('Stopping BooksModule.');
};
});
// Define a new module for a Router specific to BooksModule
LibraryManager.module('Routers.BooksModule', function (BooksModuleRouter, LibraryManager, Backbone, Marionette, $, _) {
BooksModuleRouter.Router = Marionette.AppRouter.extend({
appRoutes: {
'books': 'listBooks',
'books(?filter:=criterion)': 'listBooks',
'books/:id': 'showBook',
'books/:id/edit': 'editBook'
}
});
var executeAction = function (action, arg) {
LibraryManager.startSubModule('BooksModule');
action(arg);
LibraryManager.execute('set:active:header', 'books');
};
var API = {
// This is where we are using / referring to our controller
listBooks: function (criterion) {
require(['modules/books/list/listController'], function (ListController) {
executeAction(ListController.listBooks, criterion);
});
},
showBook: function (id) {
require(['modules/books/show/showController'], function (ShowController){
executeAction(ShowController.showBook, id);
});
},
editBook: function (id) {
require(['modules/books/edit/editController'], function (EditController) {
executeAction(EditController.editBook, id);
});
}
};
// Navigating routes.
LibraryManager.on('books:list', function () {
LibraryManager.navigate('books');
API.listBooks();
});
LibraryManager.on('books:filter', function(criterion) {
if(criterion){
LibraryManager.navigate('books?filter=' + criterion);
}
else{
LibraryManager.navigate('books');
}
});
LibraryManager.on('book:show', function (id) {
LibraryManager.navigate('books/' + id);
API.showBook(id);
});
LibraryManager.on("book:edit", function(id){
LibraryManager.navigate('books/' + id + '/edit');
API.editBook(id);
});
LibraryManager.addInitializer(function () {
new BooksModuleRouter.Router({
controller: API
});
});
});
return LibraryManager.BooksModuleRouter;
});
Finally we have the definitions for our views, models and controllers. These definitions will be tied to module / sub module objects.
The view code is shown below. Look at the .extend() methods. They are assigned to variables attached to the BooksModule.List.View sub module. https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/list/listView.js
define(['app',
'tpl!modules/books/list/templates/layout.html',
'tpl!modules/books/list/templates/panel.html',
'tpl!modules/books/list/templates/none.html',
'tpl!modules/books/list/templates/list.html',
'tpl!modules/books/list/templates/listItem.html'],
function (LibraryManager, layoutTemplate, panelTemplate, noneTemplate, listTemplate, listItemTemplate) {
LibraryManager.module('BooksModule.List.View', function(View, LibraryManager, Backbone, Marionette, $, _) {
View.Layout = Marionette.Layout.extend({
template: layoutTemplate,
regions:{
panelRegion: '#panel-region',
booksRegion: '#books-region'
}
});
View.Panel = Marionette.ItemView.extend({
// More code here!
});
View.Book = Marionette.ItemView.extend({
// More code here!
});
var NoBooksView = Marionette.ItemView.extend({
template: noneTemplate,
tagName: "tr",
className: "alert"
});
View.Books = Marionette.CompositeView.extend({
// More code here!
});
});
return LibraryManager.BooksModule.List.View; // Return the definition.
});
The controller code is shown below. This gets called from the code in booksModule.js. The controller definition is attached to BooksModule.List sub module.
define(['app', 'modules/books/list/listView'], function (LibraryManager, View) {
LibraryManager.module('BooksModule.List', function (List, LibraryManager, Backbone, Marionette, $, _) {
List.Controller = {
listBooks: function (criterion) {
require(['common/views', 'entities/book'], function (CommonViews) {
var loadingView = new CommonViews.Loading();
LibraryManager.mainRegion.show(loadingView);
var fetchingBooks = LibraryManager.request('book:entities');
var booksListLayout = new View.Layout();
var booksListPanel = new View.Panel();
require(['entities/common'], function (FilteredCollection) {
$.when(fetchingBooks).done(function (books) {
// More code here!
});
if(criterion){
filteredBooks.filter(criterion);
booksListPanel.once('show', function () {
booksListPanel.triggerMethod("set:filter:criterion", criterion);
});
}
var booksListView = new View.Books({
collection: filteredBooks
});
booksListPanel.on('books:filter', function (filterCriterion) {
filteredBooks.filter(filterCriterion);
LibraryManager.trigger("books:filter", filterCriterion);
});
booksListLayout.on("show", function(){
booksListLayout.panelRegion.show(booksListPanel);
booksListLayout.booksRegion.show(booksListView);
});
booksListPanel.on('book:new', function () {
require(["modules/books/new/newView"], function (NewView) {
// More code here!
});
LibraryManager.dialogRegion.show(view);
});
});
booksListView.on('itemview:book:show', function (childView, model) {
LibraryManager.trigger("book:show", model.get('id'));
});
booksListView.on('itemview:book:edit', function(childView, model) {
require(['modules/books/edit/editView'], function (EditView) {
// More code here!
LibraryManager.dialogRegion.show(view);
});
});
booksListView.on("itemview:book:delete", function (childView, model) {
model.destroy();
});
LibraryManager.mainRegion.show(booksListLayout);
});
});
});
}
}
});
return LibraryManager.BooksModule.List.Controller; // Return the definition.
});
Thus require.js modules and marionette modules can coexist. The following are the advantages.
Much cleaner organization of source code and clearer separation of concerns.
Module start and stop methods provide provision to initialize and cleanup objects.
When you model functionalities and sub-functionalities as modules and sub modules, we have more granular control over what resides in memory and what should not.
Also, module definition can be split across multiple files.
Please post your thoughts. Thanks for reading.
PS: Based on the above view point, please find the changes to your example below:
require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {
App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
FirstView.View = Marionette.ItemView.extend({
//define view stuff in here
});
return FirstView.View;
});
});