I created a view like this:
var MyView = Marionette.LayoutView.extend({
template: #someTemplate,
regions: function() {
return {
someRegions: '.Regions',
};
},
initialize: function(options) {
if (options) {
do stuff...
}
},
onRender: function() {
var titleBar = new Bar({ options: "options" });
this.barRegion.show(titleBar);
}
});
Which is basically a view that has a top bar with some buttons.
Now, I need to create a whole bunch of different views that have the same top bar, so I want to be able to do something like this:
var SecondView = MyView.extend({
template: #template,
onRender: function() {
create content below top bar here...
}
});
When I add breakpoints I see that MyView's initialize & render functions DO NOT get called, only my SecondView's initialize and render functions get called.
So the top bar does not show up, in fact MyView's template does not appear. Only SecondView shows up.
What am I missing here?
Thanks in advance...
The problem is that your onRender method on your second view is overriding the first one, so only your SecondView.onRender code is executed.
I think you're taking the wrong approach here. Create a Layout that includes the common topbar, and add another region for the views that go below that. If that view is another Layout is OK.
Graphically:
MainLayout
--Topbar Region
--SecondBar Region
You can make your MainLayout be the Application regions, and use your router or whatever mechanism to load the needed view into the SecondBar region.
Hope it makes sense. ;)
If you insist on using inheritance, then call a second method from your MainView onRender, something like:
onRender: function() {
var titleBar = new Bar({ options: "options" });
this.barRegion.show(titleBar);
//hook for child views
this.triggerMethod("onSecondRender");
}
And then move your onRender code to onSecondRender on your child views.
Thanks everyone who tried to help me with this...
So for whoever might be interested in what I was trying to do, this is how I got it to work:
From SecondView's initialize method I had to call MyView's initialize method:
initialize: function(options) {
MyView.prototype.initialize.call(this, options);
}
Then, from SecondView's onRender method I had to call MyView's onRender method:
onRender: function() {
MyView.prototype.onRender(this);
// extra content for my SecondView goes here...
var secondViewContent = new Content();
this.someRegionInMyView.show(secondViewContent);
}
Related
The code below is from FullCalendar's Custom View documentation. It seems like a great start, but it would be very helpful for someone brand new like me to have some basic code that renders the most simple custom view (with some events). They tell you to look at BasicView and AgendaView as a reference, but it's a little beyond my understanding. Are each of the functions required to be overridden in the custom class?
This Plunker has a basic FullCalendar and a button to change to a custom view. What would be very helpful is to see a working example. I have been tinkering for hours with no success for a custom view. If you know FullCalendar and would be willing to fill in some code for the functions it would be very appreciated!
https://plnkr.co/edit/gfEUCVYTWTm1md24e33m?p=preview
My goal is to build a day list that lists all events of the day in order in a scrollable div (where each entry will eventually be quite fleshed out with data and css styling--I'm not sure if listDay would allow for this type of customization??).
var FC = $.fullCalendar; // a reference to FullCalendar's root namespace
var View = FC.View; // the class that all views must inherit from
var CustomView; // our subclass
CustomView = View.extend({ // make a subclass of View
initialize: function() {
// called once when the view is instantiated, when the user switches to the view.
// initialize member variables or do other setup tasks.
},
render: function() {
// responsible for displaying the skeleton of the view within the already-defined
// this.el, a jQuery element.
},
setHeight: function(height, isAuto) {
// responsible for adjusting the pixel-height of the view. if isAuto is true, the
// view may be its natural height, and `height` becomes merely a suggestion.
},
renderEvents: function(events) {
// reponsible for rendering the given Event Objects
},
destroyEvents: function() {
// responsible for undoing everything in renderEvents
},
renderSelection: function(range) {
// accepts a {start,end} object made of Moments, and must render the selection
},
destroySelection: function() {
// responsible for undoing everything in renderSelection
}
});
I've added a few lines to your plunker to make the custom view work. You can find here the example: https://plnkr.co/edit/8iOq15CsL2x6RPt29wgE?p=preview
Just to mention the changes:
In the calendar initializer the view definition has been added
$('#calendar').fullCalendar({
...
views: {
CustomView: {
type: 'custom',
buttonText: 'my Custom View',
click: $('#calendar').fullCalendar('changeView', 'CustomView')
}
}
});
In the custom view just added this in the render
$('.fc-view').append("<div>Insert your content here</div").css("background", "red");
In the custom view you get access to the events by doing this:
var myEvents=$('#calendar').fullCalendar('clientEvents');
From there on you can do your further customizations
Consider the following JS:
var ChildLayout = Marionette.Layout.extend({
template: "... let's not go here ...",
initialize: function() {
console.log('Child Layout Initialized'); // registers once
},
onRender: function() {
console.log('Rendered'); // registers 2 times
},
});
var ParentLayout = Marionette.Layout.extend({
template: "<div class='child-region'></div>",
regions: { childRegion: '.child-region' },
onRender: function() {
console.log('About to initialize ChildLayout'); // registers once
this.childRegion.show(new ChildLayout());
},
});
In the above, I use the ParentLayout to render the ChildLayout in one of its Regions. Notice that I do not pass any sort of model to the ChildLayout.
The show function, a property of Marionette, should logically initialize and then render the model once. A Marionette View should not re-render itself unless there is some change in its model, from what I understand.
In my application, the onRender of ChildLayout is triggering in my code several times, though its initialize only triggers once.
I cannot see what is causing Marionette to render the ChildLayout multiple times - this does not make sense.
Any insight?
edit
On inspecting the source code of Marionette.js, the show function clearly only renders the passed view once, right after initializing. So the re-renders could only occur from the Layout deciding autonomously to re-render. Interesting.
This turned out to be my own problem with a Zombie View, which I since handled.
Simple view (reduced code):
My.View = Backbone.View.extend({
className: 'my-view',
initialize: function() { },
render: function() {
console.log('render');
return this;
}
});
I use it in another view as subview like this:
var myView = new My.View();
this.$el.append(myView.render().$el);
First instead of a className the view had a template and the view was rendering fine.
However when I removed the template and added className instead it is not rendering correctly.
It just renders the div and the correct class name however no logging in the render method is performed. AND when I add some html inside the render method to this.$el it never appears. Any ideas why?
UPDATE:
When I put my custom rendering code inside the afterRender method it works. Why is it not possible to overwrite the render method in my case?
this.$el.append(myView.render().el);
I would render the subview the following way.
var myView = new My.View();
this.$el.append(myView.el);
myView.render();
I would like to use mixins with my BackBone Views.
Here is my mixin:
var mixin = {
events: {
"click" : "doStuff"
},
doStuff: function() { alert("bah!"); }
}
Here is how I mix it into two separate views:
var view1 = Backbone.View.Extend({ ... });
_.extend(view1.prototype, mixin);
var view2 = Backbone.View.Extend({ ... });
_.extend(view2.prototype, mixin);
The trouble I am running into is that the click event only seems to work in view1. If I initialize view2 first, then the click event only works in view2.
Any ideas what I am doing wrong?
Thanks (in advance) for your help.
You can override the extend method on backbone to due the sort of inheritance and merging you expect. You just need to dig through the documentation a little bit and the objects to find what you want.
This way you have a BaseObject then extend off of that your two objects.
I am using the Backbone Boilerplate https://github.com/tbranyen/backbone-boilerplate and don't know what's the best way to handle more than one page. I cannot find answer that helps me understand easily. Basically, I am thinking of those options:
Should each page has a different config.js? Like config-userpage.js, config-homepage.js...?
Should I have different router.js for different page instead? Like router-userpage.js or router-homepage.js,...?
Should I just try a different boilerplate like https://github.com/hbarroso/backbone-boilerplate?
You can definitely try a different boilerplate, but I'm not sure that will
help. Multiple pages can be achieved in many different ways.
A good reference example for the Backbone Boilerplate is:
http://githubviewer.org/. I have released the entire thing as open source and
you can View how basic pages are added there.
You may want to get creative and make a Page model that handles what page
you're on and inside of each route set the new page title and which layouts to
use.
A very basic, proof-of-concept, implementation inside of app/router.js might
look something like this:
define([
// Application.
"app",
// Create modules to break out Views used in your pages. An example here
// might be auth.
"modules/auth"
],
function(app, Auth) {
// Make something more applicable to your needs.
var DefaultPageView = Backbone.View.extend({
template: _.template("No page content")
});
// Create a Model to represent and facilitate Page transitions.
var Page = Backbone.Model.extend({
defaults: function() {
return {
// Default title to use.
title: "Unset Page",
// The default View could be a no content found page or something?
view: new DefaultPageView();
};
},
setTitle: function() {
document.title = this.escape("title");
},
setView: function() {
this.layout.setView(".content", this.get("view")).render();
},
initialize: function() {
// Create a layout. For this example there is an element with a
// `content` class that all page Views are inserted into.
this.layout = app.useLayout("my-layout").render();
// Wait for title and view changes and update automatically.
this.on({
"change:title": this.setTitle,
"change:view": this.setView
}, this);
// Set the initial title.
this.setTitle();
// Set the initial default View.
this.setView();
}
});
// Defining the application router, you can attach sub routers here.
var Router = Backbone.Router.extend({
routes: {
"": "index"
},
index: function() {
// Set the login page as the default for example...
this.page.set({
title: "My Login Screen!",
// Put the login page into the layout.
view: new Auth.Views.Login()
});
},
initialize: function() {
// Create a blank new Page.
this.page = new Page();
}
});
return Router;
});
As you can see, this is an opinionated way of creating "pages" and I'm sure
other's have better implementations. At Matchbox, I have a very robust Page
model that does breadcrumbs and figures out which navigation buttons to
highlight based on the state. You can also create Routers inside your modules
to encapsulate functionality and expose the Page model on the app object so
that it's available throughout your application.
Hope this helps!