I'm working on my first large front-end application built with Marionette.js framework. By now I have module BlogApp, which is small blog sub-appliaction with only 2 methods list(to list all posts) and show (to display single post by id):
#MainApp.module "BlogApplication", (BlogApplication, App, Backbone, Marionette, $, _) ->
class BlogApplication.Router extends Marionette.AppRouter
appRoutes:
"blog" : "list"
"posts/:id" : "showPost"
API =
list: ->
BlogApplication.List.Controller.list()
showPost: (id, post) ->
BlogApplication.Show.Controller.showPost id, post
App.addInitializer ->
new BlogApplication.Router
controller: API
App.vent.on "blog:post:clicked", (post) ->
App.navigate Routes.post_path(post.id)
API.showPost post.id, post
App.on "blog:list": ->
App.navigate("blog")
API.list()
My folders are organized like this:
--blog
-blog_app.js
--list
--templates
-blog_sidebar.template
-blog_panel.template
-blog_layout.template
-blog_post.template
-list_controller.js
-list_view.js
--show
--templates
-blog_sidebar.template
-blog_panel.template
-blog_layout.template
-blog_post.template
-show_controller.js
-show_view.js
Everything works fine.
The only difference between the show and list pages - is displaying one full post instead of listing them all. Now I have 2 controllers and I'm duplicating my templates for show and list methods, which is totally not a good practice. But I also don't think that I should implement my show method in List.Controller - it will help me to use same templates, because it will break my application infrastructure and in future, when I have to add some features, for example - comments,tags etc, it will mess up everything. How should I organize my blog module the right way?
The way I organise my code has been working overall well without any memory leaks (afaik) until now.
I will share this with you and for any people who might find this useful.
I will also provide some supplementary information on how I manage the memory.
1) For the folder structure I have something like:
folder: apps
folder: app_blog.js
folder: layouts (for views + templates)
folder: views (for views + templates)
folder: => here comes the name of one or more sub-apps
folder: layouts
folder: views
file: ctr_list.js => a Marionette controller
file: app_blog
folder: components
folder: models
folder: vendor
file: main.js
file: app.js
In my application, the app_blog is an app (folder) that uses an app_name.js file to manage the routes, and bootstrap the subapps, and controllers to manage subsections of the app.
When a route is hit:
//1. render layout
var layout = new Layout();
layout.render();
//2. instantiate controller & tell the controller what to do
var ctr_List = new Ctr_List();
ctr_list.showList();
A controller in my application has the following format:
define(["backbone", "marionette", "jquery", "zwoop", "apps/app_blog/list/views/some_view"
],
function(Backbone, Marionette, $, Zwoop, Some_View){
var Ctr_List = Marionette.Controller.extend({
initialize: function(options){
this.events();
this.region = new Marionette.Region({ el: #some-region });
//Define more regions if you need to
},
events: function(){
app.on("reset", _.bind(function(){ this.close(); }, this));
app.on("dosomething", _.bind(function(param){ this._doSomething(); }, this));
this.events();
this.region.on("show", _.bind(function(){ this.activatePlugin(); }, this));
},
showList: function(){
this._showList();
},
_showList: function(){
//Render the list
var some_view = new Some_View();
this.region.show(some_view);
},
_doSomething(param){
},
_activatePlugin(){
//eg. $("#date-input").datepicker();
},
//Unbind all events
onClose: function(){
app.off("doSomething");
this.region.off("show");
}
});
return Ctr_List;
});
Then when another route gets triggered and another app is started, I will call app.trigger("reset") if necessary to close the controller and unbind events that were bound by the controller.
So far, this has worked well for me.
Related
As a newbie to both Ember.js and ember-cli, I'm having trouble making sense of what seems necessary to make my app work.
The logical hierarchy of my app is something like this:
Projects
|___Project
|___Details
|___Team Members
|___Budget
|___Planned
|___Actual
And currently, this is my router.js:
this.resource('projects');
this.resource('project', { path: 'projects/:project_id' }, function() {
this.route('details');
this.route('team');
this.route('milestones');
this.resource('budget', function(){
this.route('project-budget', { path: 'project'});
this.route('resource-budget', {path: 'team'});
});
});
What I'm having trouble with is where to put my files. Up until the point of the two nested routes under Budget, my folder structure looked like my hierarchy, but since a Resource resets the namespace, now to make it work I have to pull my Budget template, route, and controller back out to the top level (with Projects stuff), which just seems messy and like it will cause headaches when trying to maintain this thing later.
Am I doing it wrong?
Router definition can be a little tricky in Ember. Your resource/route definition in router.js should reflect your page structure. So for example, if your 'team' template should be nested inside your 'project' template, then 'team' should be nested inside of 'project' in router.js:
Router.map(function() {
this.resource('project', function() {
this.route('team');
});
});
If you use this.route() in router.js, then your folder structure should mimic the structure in router.js. Using the example above, because we're using this.route() to define 'team', your folder structure would be like this:
app/routes/project.js
app/routes/project/team.js
app/templates/project.hbs
app/templates/project/team.hbs
If, however, you choose to use use this.resource() in router.js, then you're telling Ember that you're going to reset your folder structure. So if you changed router.js to this:
Router.map(function() {
this.resource('project', function() {
this.resource('team');
});
});
...then your folder structure would be like this:
app/routes/project.js
app/routes/team.js
app/templates/project.hbs
app/templates/team.hbs
Going back to your specific question, if you feel that resetting your folder structure is messy, then you can use this.route() everywhere and forego this.resource(), because nestable this.route() landed in Ember 1.7: http://emberjs.com/blog/2014/08/23/ember-1-7-0-released.html
I'm starting a marionette app and I try to structure it. So now I have:
define(["marionette", "handlebars", "routes"], function(Marionette, Handlebars, Route){
var App = new Marionette.Application();
...
App.addRegions({
header: "#header_region",
...
});
App.addInitializer(function(options){
...
new Route();
Backbone.history.start();
});
return App;
});
and my routes looks like:
define(["marionette", "app", "header/view"], function(Marionette, App, headerView){
var Route = Backbone.Marionette.AppRouter.extend({
routes : {
'' : 'home'
},
home: function(){
var header_view = new headerView();
App.header.show(header_view);
...
}
});
return Route;
});
Obviously I have a loop in dependencies here with App.header.show(header_view). What is the common way to solve it? How do you structure your Marionette app?
This was a great example that helped to get me going when building my first Marionette App: https://github.com/BoilerplateMVC/Marionette-Require-Boilerplate/tree/master/public/js/app
The way you currently have your route file set up is the backbone way of doing it. You should create a Marionette Controller that will respond to the App routes.
Also, you'll see that this person creates init files to specify all of the start up logic (specifying global static variables and instantiating the controller and router to be used).
Hope this helps!
I am trying to integrate ember into my grails app. I've got one page working in Ember but am unsure of how to have two different pages.
I have a page called color.gsp the server does nothing but just redirects to this page so the method is just def color() {}
In this page I have several templates one of which is Application template. I have a App.js which handles everything on this page and everything is working fine on this page.
Question
Now I want to have another page called shade.gsp where also the server should not do anything by redirect so again the method will simply be def shade() {}.
The problem is, how would App.js know whether to update application template in shade.gsp or color.gsp.
I understand this might not be the ideal way to do things in ember. but since I'm integrating ember rather than complete overwrite, i need this option to work. Is there a way I can have separate JS files for color and shade?
I think that changing your js structure, to reflect your dependencies can solve this problem.
// App.js
App.Router.map(function() {
this.route('color');
this.route('shade');
});
// Color.js
// here all color resources
App.ColorRoute = Ember.Route.extend({
// your implementation
});
// Shade.js
// here all shade resources
App.ShadeRoute = Ember.Route.extend({
// your implementation
});
In your ApplicationResources.groovy
modules = {
application {
dependsOn 'jquery', 'handlebars', 'ember'
resource url:'js/App.js'
}
shade {
dependsOn 'application'
resource url: 'js/Shade.js'
}
color {
dependsOn 'application'
resource url: 'js/Color.js'
}
}
In shade.gsp
<r:require modules="shade"/>
In color.gsp
<r:require modules="color"/>
I've just started using Jasmine with maven. I have Jasmine working, but for some reason it cannot find my Backbone models. I have the JavaScript src directory pointing to the folder containing my Backbone.js models. In my JavaScript test directory I have a simple test as such:
describe('ToDo Model',function()
{
it('Test',function() {
var todo = new ToDo();
});
});
But I keep getting ToDo is not defined. Do I have to write my tests inside of the my backbone model files or anything? Thanks.
ToDo has to be in the global namespace as well. Try typing this in your Chrome/Firefox console:
window.ToDo
If it returns undefined, then that's the problem!
It's usually a good practice to define a global namespace for your app, for example:
window.Application = {
Models: {},
Views: {},
Collections: {}
}
// etc.
Then, I like to define models like this:
(function (Models) {
Models.ToDo = Backbone.Models.extend({
// etc...
});
})(Application.Models);
The namespacing here isn't necessary, but seeing Models right at the top of the file is a nice visual cue, I think.
I am new to javascript and I saw another post with a similar question but I'm not sure how to actually inject models into a backbone.js that lives in a separate file.
In my index file I have the following which is starting the app:
$(function () {
var app = new App();
Backbone.history.start();
});
Inside my application.js file I have the router which needs to have customers:
window.App = Backbone.Router.extend({
routes: {
"": "home"
},
home: function () {
console.log("route::home");
console.log(this.customers);
}
});
How can I actually get this.customers to be injected into the application? Where does this code live?
I wrote an article that outlines how code like what you've shown here is potentially an anti-pattern, and illustrates a simple way of getting the data bootstrapped. Though your direct question is not the purpose of this article, I think the contents of this article should lead you in a direction that does solve the problem your running in to.
http://lostechies.com/derickbailey/2011/08/30/dont-limit-your-backbone-apps-to-backbone-constructs/