I cant seem to access my Views from my main app file. I'm new to backbone and marionette and I'v read through the documents but cannot find why its not working. Any help or advise?
app.js
window.App = new Marionette.Application();
App.addRegions({
gameCriteria: "#game-criteria"
});
App.myRegion.show(myView);
});
myView.js
App.module("Views", function(Views, App, Backbone, Marionette, $, _) {
var GameCriteriaTab = Backbone.Marionette.ItemView.extend({
template: JST["game-criteria-tab"],
regions: {
rulesRegion: "#rules-region"
},
onShow: function() {
this.rulesRegion.show(RulesView);
}
});
});
did you instatiate your view before calling show ? i am waiting for a code similar to that :
App.myRegion.show(new GameCriteriaTab ({
model: new GameCriteriaModel()
}));
can you provide us with the error in chrome console ?
Not sure what you are trying to do here. I'm assuming that you want to create an app called 'windowApp', and you are trying to display a view called 'GameCriteriaTab' inside a region called 'gameCriteria'. I'm refactoring your code to the following:-
windowApp = new Backbone.Marionette.Application();
windowApp.addRegions({
gameCriteria: "#game-criteria"
});
windowApp.GameCriteriaTab = Marionette.ItemView.extend({ //Defining the view
template: " <include your template Id or className here> "
});
windowApp.on("start",function(){
var myView = new windowApp.GameCriteriaTab(); //You need to create an instance of the view if you want to render it in the page
windowApp.gameCriteria.show(myView); //Showing the view in the specified region
});
windowApp.start(); //This will run the marionette application
You cannot define any regions inside ItemView. Are you trying to create a nested view? In that case, a LayoutView would suit your needs, and you can add regions inside it. Just change the ItemView to a LayoutView then.
I have two main page layouts: one is the default layout that is going to be used for most pages and the other one is a bare layout without the header/footer etc. that will be used for example for the login page. There might be more in the future.
My current router:
var AppRouter = Backbone.Router.extend({
routes: {
'login': 'login',
'main': 'main'
},
login: function(){
var mainLayoutView = new MainLayoutView({
'layout': 'bare'
});
App.Notes.mainLayoutContainer.show(mainLayoutView);
},
main: function(){
var mainLayoutView = new MainLayoutView({
'layout': 'default'
});
App.Notes.mainLayoutContainer.show(mainLayoutView);
}
});
How should approach the implementation of the MainLayoutView to be able to render the layout specified in options? Or should I actually have two separate layouts to handle the two templates? They'll obviously share a lot of functionality, though, so I'd rather have only one.
One way could look like this...
MainLayoutView = Backbone.Marionette.ItemView.extend({
initialize: function(options){
this.layout = options.layout;
},
onRender: function() {
if (this.layout == 'default') { // or "fullView"
// Render all components
} else {
...
// Perhaps there's nothing here
}
// Render the center partial view
}
});
Another way could be directly in your view template. Marionette suggests to have a templating system (Handlebars, Jade, ...).
If you don't use a templating system, first you should consider using one. Second, if you thought about always rendering all the views and simply $(element).hide() what you don't want: DON'T. The advanced users will be able to make the elements visible again and perhaps abuse of your system. Plus, this is useless processing and useless data sent through the wire :-)
I think I've found the best way to handle this. I've decided to set the template dynamically in "initialize":
var MainLayoutView = Backbone.Marionette.LayoutView.extend({
initialize: function(options){
if(options.layout==='bare'){
this.template = '#bare-layout';
}
else{
this.template = '#default-layout';
}
}
});
This way I can use as many templates for a layout as I want to. Although I'm starting to think that ideally I should be using separate instances of LayoutView for that.
I'm new in Highcharts.js and Marionette.js ,
Task:
I try to create two Marionette app's such that each of them would store a Highcharts.Chart() , such that both of these two Chart()'s would be appeared each time on the same region .
Implementation:
So my general idea is like that -
Let's Graph1Manager be the 1st Marionette app -
var Graph1Manager = new Marionette.Application()
and main-region the region for the Charts()'s -
<div id="main-region" class="container"> Here would be a chart </div>
For Graph1Manager I implemented the -
Graph1Manager.on("initialize:after", function () {...})
such that Graph1Manager loads all the data required for the 1st Chart() once .
So my question is:
Where and how I have to store and draw the Chart() on Graph1Manager ?
Does it have to be in any Marionette.ItemView.extend ?
I have previously built an application using Marionette and Highcharts so I think I can speak to this from some experience.
There's more than one way to go about this but in general unless you have some special reason you would just want to have one Marionette app running. I can't tell if you're trying to display two views at the same time in the same region but in case you are this is definitely not possible--as soon as you show the second view the first will be removed from the dom.
Anyways on to your questions:
The simplest way (and the way I have done this) is to use an ItemView and draw the chart in the onRender() method. So it will look something like this:
var MyView = Marionette.ItemView.extend({
template: _.template('<div class="chart-container" style="height: 325px;"></div>'),
onRender: function() {
this.$('.chart-container').highcharts({
...highcharts options here
})
}
});
Then when you're ready to display it you'll create an instance and show it in the region:
var view = new MyView();
Graph1Manager.nameOfRegion.show(view);
Whenever you show another view in the region the previous one will automatically close so remember you'll have to create a new instance of it if you want to use it again...
Hey all I am pretty new to Backbone though I have put several days into trying to get familiar with this framework, and it seems everytime I start feeling comfortable I run across a new problem.
I am wondering how to reference a view that is rendered from within my main appview. I know this is a really simple issue but I just can't seem to figure it out.
So for instance I have a simple view
var SubView = Backbone.View.extend({
//something here including render function
});
Then I render that view from within the main app view
var myApp = Backbone.View.extend({
render: function{
var mysubView = new SubView();
mysubView.render();
},
editSomething: function{
mysubView.remove();
}
});
When I try and reference that view from a function (editSomething:) in the main app view, I get a reference error.
What I am trying to achieve is that I have two views that include forms. I want to swtich between the two forms as an edit function is called and when an add function is called. But I can't seem to access the views that have already been rendered.
I don't want to initialize and render a new view before removing the existing view because from what I understand, I will start to get a bunch of views floating in memory.
Reference it using this:
var myApp = Backbone.View.extend({
render: function{
this.subView = new SubView();
this.subView.render();
},
editSomething: function{
this.subView.remove();
}
});
In my simple project I have 2 views - a line item view (Brand) and App. I have attached function that allows selecting multiple items:
var BrandView = Backbone.View.extend({
...some code...
toggle_select: function() {
this.model.selected = !this.model.selected;
if(this.model.selected) $(this.el).addClass('selected');
else $(this.el).removeClass('selected');
return this;
}
});
var AppView = Backbone.View.extend({
...some code...
delete_selected: function() {
_.each(Brands.selected(), function(model){
model.delete_selected();
});
return false;
},
});
Thing is, I want to know how many items are selected. In this setup selecting is NOT affecting the model and thus not firing any events. And from MVC concept I understand that views should not be directly talking to other views. So how can AppView know that something is being selected in BrandViews?
And more specifically, I AppView to know how many items were selected, so if more than 1 is selected, I show a menu for multiple selection.
You might want to have a read of this discussion of Backbone pub/sub events:
http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/
I like to add it in as a global event mechanism:
Backbone.pubSub = _.extend({}, Backbone.Events);
Then in one view you can trigger an event:
Backbone.pubSub.trigger('my-event', payload);
And in another you can listen:
Backbone.pubSub.on('my-event', this.onMyEvent, this);
I use what Addy Osmani calls the mediator pattern http://addyosmani.com/largescalejavascript/#mediatorpattern. The whole article is well worth a read.
Basically it is an event manager that allows you to subscribe to and publish events. So your AppView would subscript to an event, i.e. 'selected'. Then the BrandView would publish the 'selected' event.
The reason I like this is it allows you to send events between views, without the views being directly bound together.
For Example
var mediator = new Mediator(); //LOOK AT THE LINK FOR IMPLEMENTATION
var BrandView = Backbone.View.extend({
toggle_select: function() {
...
mediator.publish('selected', any, data, you, want);
return this;
}
});
var AppView = Backbone.View.extend({
initialize: function() {
mediator.subscribe('selected', this.delete_selected)
},
delete_selected: function(any, data, you, want) {
... do something ...
},
});
This way your app view doesn't care if it is a BrandView or FooView that publishes the 'selected' event, only that the event occured. As a result, I find it a maintainable way to manage events between parts of you application, not just views.
If you read further about the 'Facade', you can create a nice permissions structure. This would allow you to say only an 'AppView' can subscribe to my 'selected' event. I find this helpful as it makes it very clear where the events are being used.
Ignoring the problems with this that you already mention in your post, you can bind and trigger events to/from the global Backbone.Event object, which will allow anything to talk to anything else. Definitely not the best solution, and if you have views chatting with one another then you should consider refactoring that. But there ya go! Hope this helps.
Here is my case with a similar need: Backbone listenTo seemed like a solution to redirect to login page for timed out or not authenticated requests.
I added event handler to my router and made it listen to the global event such as:
Backbone.Router.extend({
onNotAuthenticated:function(errMsg){
var redirectView = new LoginView();
redirectView.displayMessage(errMsg);
this.loadView(redirectView);
},
initialize:function(){
this.listenTo(Backbone,'auth:not-authenticated',this.onNotAuthenticated);
},
.....
});
and in my jquery ajax error handler:
$(document).ajaxError(
function(event, jqxhr, settings, thrownError){
.......
if(httpErrorHeaderValue==="some-value"){
Backbone.trigger("auth:not-authenticated",errMsg);
}
});
You can use Backbone object as the event bus.
This approach is somewhat cleaner but still relies on Global Backbone object though
var view1 = Backbone.View.extend({
_onEvent : function(){
Backbone.trigger('customEvent');
}
});
var view2 = Backbone.View.extend({
initialize : function(){
Backbone.on('customEvent', this._onCustomEvent, this);
},
_onCustomEvent : function(){
// react to document edit.
}
});
Use the same model objects. AppView could be initialized with a collection, and BrandView initialized with one model from that collection. When attributes of a branch object change, any other code that has a reference to that model can read it.
So lets so you have some brands that you fetch via a collection:
var brands = new Brands([]);
brands.fetch();
Now you make an AppView, and an array of BrandView's for each model.
var appView = new AppView({brands: brands});
var brandViews = brands.map(function(brand) {
return new BrandView({brand: brand});
});
The appView and the brandViews now both have access to the same model objects, so when you change one:
brands.get(0).selected = true;
Then it changes when accessed by the views that reference it as well.
console.log(appView.brands.get(0).selected); // true
console.log(brandViews[0].brand.selected) // true
Same as John has suggested above, the Mediator Pattern works really good in this scenario, as Addy Osmani summing this issue up again in Backbone fundamentals.
Wound up using the Backbone.Mediator plugin which is simple and great, and makes my AMD View modules working together seamlessly =)