I'm attempting to get a marionette layout to render like a backbone view. Ie, I've declared a tagName and an id and I want it to generate a dom element based of that. For such a simplistic element, it seems redundant to have to make a template stub just for that.
HTML:
<body><div id="page"></div></body>
Test code:
var HeaderBar = Backbone.Marionette.Layout.extend({
tagName: "div",
id: "headerBar"
});
/*========== APP Tests ============*/
App = Marionette.Application.extend({});
var MyApp = new App();
MyApp.addRegions({
pageRegion: "#page"
});
var header = new HeaderBar();
MyApp.pageRegion.show(header);
However, calling show from the app throws the TemplateNotFoundError.
I'm looking for a way to have Marionette render this without a template and without having to commandeer the render function in the library.
Try with
var HeaderBar = Backbone.Marionette.Layout.extend({
template: _.template("<div></div>"),
id: "headerBar"
});
See https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.renderer.md#using-pre-compiled-templates
Layouts require a template because they need to render regions inside of them. If you aren't using any regions with your layout, you don't need to use a Layout; use an ItemView instead:
var HeaderBar = Backbone.Marionette.ItemView.extend({
tagName: "div",
id: "headerBar"
});
Then use it as normal with your region.
If you do, however, need regions inside HeaderBar, then yes, you need a template with a Layout.
Related
I have a model's view set up as follows:
var PlayerView = Backbone.View.extend({
el: "div.player",
template: _.template($("#playerTemplate").html()),
render: function() {
this.$el.append(this.template(this.model.toJSON()));
return this;
}
});
and the main view is set up as follows:
var AppView = Backbone.View.extend({
el: "#players",
render: function() {
var pView = new PlayerView({model: playerModel});
this.$el.append(pView.render().$el);
}
});
If you notice the render of AppView, I am rendering the PlayerView first, and then appending its $el to AppView's $el. I was expecting an error situation here, as it would display PlayerView 2 times to main View as follows:
First, in the pView.render() where I put content in pView and
Second, in the same line where I append pView's $el to main view.
But it just appends the pView only once. How does this work?
I am not sure I have explained my question clearly; I can add more context if required.
Assuming div.player exists in DOM as you mentioned in comments,
When you do pView.render(), it adds the template inside it.
Then when you append pView's element (div.player) to AppView's element (#players), entire div.player is moved into #players.
Your code is working the way it should work.
If you intent to create multiple players, You shouldn't use el option in player view, Instead you should decorate the element created by backbone and create multiple instances of player view.
In my backbone app I have a view that needs to be rendered using different templates, dependant on a variable that is passed to the view. I am using require js and want to know if there is smart way of doing this.
Here is how I would call instantiate the new view:
var templateType = 'Template1'
var view = new DetailView({model:thisModel, 'templateType': templateType});
Here is an example of the view:
define(['backbone',
'text!templates/template1.html',
'text!templates/template2.html' ],
function(Backbone,
Template1,
Template2){
var DetailView = Backbone.View.extend({
tagName : 'li',
className: 'detail-tag',
initialize : function(){
this.detailTemplate = _.template( this.options.templateType );
this.render();
},
This does not give me the required result. How do I use the options value to link to the require variable - Template1? I realise I could probably set up a switch statement or something, but that seems a bit messy. Is there a neater way of doing it?
Thanks
I decided to pass the template to the view, instead of trying to match it up with a template included with require. I used this question as a model for my solution:
How to pass a template to a view in Backbone
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();
ContainerView.pushObject() does not automatically wire-up dynamically added views with a Container object.
The lack of an auto-wired container causes a render failure when a view renders a template that contains a handlebars render helper.
SIMPLE CASE THAT WORKS (KIND OF)
View:
App.DynamicView = Em.View.extend({
templateName: 'dynamic',
didInsertElement: function() {
var control = this.get('controller');
control.send( 'view_inserted', this.templateName, control._debugContainerKey);
control.send('callDynamicController');
}
});
Template:
<script type="text/x-handlebars" data-template-name="dynamic">
dynamic
</script>
Controller (only used when manually assigned):
App.DynamicController = Em.ObjectController.extend({
className: 'App.DynamicWithRenderController',
callDynamicController: function() {
console.log('DynamicController.callDynamicController()');
}
});
Index Controller:
App.IndexController = Em.ObjectController.extend({
view_inserted: function(aview, acontroller) {
console.log('view inserted!', aview, acontroller);
}
})
Instantiation code:
var acontainer = App.DynamicController.create({});
var aview = App.DynamicView.create({ controller: acontroller })
acontainerView.pushObject(aview);
These classes render & behave as expected, but if you interogate them, lack some of the Ember-wiring (e.g. no _debugContainerKey & container properties IIRC):
MORE ADVANCED CASE THAT BREAKS
If we introduce a handlebars template that uses rendering helpers, it breaks rendering. The dynamically added view currently lacks some properties the rendering helper assumes
<script type="text/x-handlebars" data-template-name="dynamic-with-render">
dynamic w/render:
{{render knob}}
</script>
and make knob look like this:
<script type="text/x-handlebars" data-template-name="knob">
{{render knob}}
</script>
The (failing) dynamic view instantiation code:
var acontainer = App.DynamicController.create({});
var aview = App.DynamicView.create({
controller: acontroller,
template:'dynamic-with-render' })
acontainerView.pushObject(aview);
CODE EXAMPLE
A fuller example with some notes can be seen here:
http://jsfiddle.net/AshCoolman/KyJ2U/6/embedded/result/
NOTE: My tests include a custom handlebars helper based of the control helper called controlWithVars
THE PROBLEM
It looks like I need to write something that does the Ember-wiring, in either:
the more native ContainerView (getting into the Ember guts), OR
a more de-coupled new render helper possibly inelegant)
I'm not sure how to proceed. It would be great if someone has already come up with an elegant solution, or at least could give me some helpful tips.
EDIT: So it looks like creating and assigning a container, which includes the views dependencies might be a solution. Thoughts anyone?
HELPFUL READING
https://github.com/emberjs/ember.js/issues/2108
What is the purpose of the Ember.Container
http://mcdowall.info/posts/ember-application-initializers/
The raison d'être of Ember.ContainerView is for dynamically adding and removing views, so I'm pretty confident you can do all the things you want to with it.
One thing I noticed in your examples is that you are creating your child views with View.create(attrs). It is important to use containerView.createChildView(viewClassName, attrs) to create views that get the container, parent view hierarchy, and more. See the implementation more details:
https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/view.js#L2072
I am learning Backbone.
I am wondering whether or not a Backbone View always requires a Backbone Model.
For example, let's say I have a panel that contains two child panels. The way I would structure this is with a parent view for the main panel, then two child views for the child panels...
var OuterPanel = Backbone.View.extend({
initialize: function() {
this.innerPanelA = new InnerPanelA(innerPanelAModel);
this.innerPanelB = new InnerPanelB(innerPanelBModel);
},
});
var outerPanel = new OuterPanel();
The parent view is really just a container. It may have some controls in it, but no data that needs to be persisted. Is this the proper way to do it? Or is this bad practice?
Thnx (in advance) for your help
As said in Backbone.View docs
Backbone views are almost more convention than they are code — they
don't determine anything about your HTML or CSS for you, and can be
used with any JavaScript templating library.
In other words, if you don't have a model, don't use a model. On the other hand, I would inject the children models as options to the outer view instance and not rely on global variables, something like this:
var OuterPanel = Backbone.View.extend({
initialize: function(options) {
this.innerPanelA = new InnerPanelA({model: options.modelA});
this.innerPanelB = new InnerPanelB({model: options.modelB});
}
});
var outerPanel = new OuterPanel({
modelA: innerPanelAModel,
modelB: innerPanelBModel
});