Rendering Layouts and sub-views in Marionette / Backbone.js - javascript

I have a working solution in regard to rendering layouts with views in regions in a Marionette application I'm working on, but something doesn't feel right about it. Do you have to append anything directly to the DOM?
Here is my method in the controller:
//Grab the main Layout
var layout = new APP.Views.LayoutView();
//Render that layout
layout.render();
//Make the model
var simpleModel = new APP.Models.simpleModel({
"field1" : "foo",
"field2" : "bar"
});
layout.header.show(new APP.Views.subView({model: simpleModel}));
$('body').html(layout.el);
It's the last part that feels unnatural to me. This is primarily because if I move 'layout.header.show' to after the .html(), then it doesn't render properly. What's the point of regions if they aren't dynamically changable after pushing it to the DOM?
Here is my layout:
APP.Views.LayoutView = Backbone.Marionette.Layout.extend({
template: "#layoutTemplate",
className : 'wrapper',
regions: {
header: "#header",
footer: "#footer"
}
});
and here is the sub view:
APP.Views.subView = Backbone.Marionette.ItemView.extend({
template : '#headerTemplate',
className: 'container'
});
As I said, this works, but it feels like I'm not using regions properly. Is there a better, more concise way to do this that will allow you access to regions after you rend the layout to the DOM?
In the Marionette documentation there seems to be no mention of using .html() to get things on the page -- I'm wondering if this is left out because it's not needed or that it's assumed.
Can anyone help?

Okay, so it seems like this can be circumvented by creating a 'region' in your application, then using .show() to show the layouts inside of it.
Here is a link to a fiddle I found on SO that helped: http://jsfiddle.net/TDqkj/6/
as well as to another question: Understanding layouts in Marionette for Backbone.js
The fiddle in particular has this code:
App = new Backbone.Marionette.Application();
App.addRegions({
'nav': '#nav',
'content': '#content'
});
Which the programmer than uses to add layouts to those regions -- meaning you never have to append to the DOM.
If anyone else has a more elegant solution, please post!

Related

Routes and Embedded View/Controller linking in Ember.js

Routing in Ember.js is troubling me, and I can't seem to find the "correct" way of doing what I want to do.
I have a route, let's call it #/map, which contains a series of top-level and containers of child views.
Hierarchically, I have a top map_view, which contains 4 additional views: A topbar (which has topbar menu item triggers within it), a sidebar (which has sidebar menu item triggers in it), and two containerViews (a sidebar menu containerView and a topbar menu containerView), which will contain one or more nested views that are programatically inserted on clicking a menu item trigger.
My issue is that while this works, and I can embed all of these views into their various templates, none of them are linking with controllers, and the controller they are picking up is the map_controller (which makes sense as that is the linked outlet controller for the top level view). Currently I am using a method described on Ember's github here, but it seems a little...hacky?
Here is a JSFiddle showing the problem. Notice that the controller for level-one-entry and level-two-entry is the index_controller: http://jsfiddle.net/fishbowl/Z94ZY/3/
Here are some code snippets for what I am doing to get around it:
map.hbs:
<section id='map'>
{{view App.SidebarView}}
{{view App.TopbarView}}
<div id='map-canvas'></div>
</section>
topbar_view.js:
var TopbarView = Em.View.extend({
templateName: 'topbar',
classNames: ['topbar-container'],
init: function() {
var content = this.get('content'),
controller = App.TopbarController.create({
view: this
});
this.set('controller', controller);
this._super();
}
});
module.exports = TopbarView;
topbar_controller.js
var TopbarController = App.ApplicationController.extend({
content: Ember.computed.alias('view.content'),
trigger: null,
start_date: null,
end_date: null,
travelling: null,
word: 'topbar'
});
module.exports = TopbarController;
I'm not doing anything special in the router other than declaring this.route('map'). A further problem i'm having is that whenever I declare needs: ['some_other_controller'], I get an error
<App.TopbarController:ember413> specifies 'needs', but does not have a container. Please ensure this controller was instantiated with a container.
Am I missing something blindingly obvious about how to go about linking these together. I'm guessing that i'm using routing incorrectly. I don't want to change what the URL is, as i'm technically not moving pages, just opening and closing menus on the page, but I don't really understand how else i'm supposed to use the router to achieve this.
EDIT 2: i've mocked up another jsfiddle of what I could do with outlets and link-to's, but i'm not sure that I want the URL changing (as you'd probably be able to do odd things with the back button etc): jsfiddle - The alternative to this is to set location: 'none' in the Router, but I don't really like that option either...

Nested routes and template replacement in Ember.js

I try to develop a simple Ember.js app.
I think that these screens describe the desired scenario good enough.
Buttons on the top are #link-tos. Note that I want the first button to be highlighted on the 3rd screen.
It's easy to find examples where the 3rd template lives in the outlet in the 2nd one, but I need some kind of template replacement in the main outlet.
Please help me to achieve this behavior. Hope that my description is clear enough.
You can make list and detail routes same level, so they both are rendered to the same outlet, one at time. Like that:
App.Router.map(function() {
this.resource("movies", function() {
this.route("list");
this.route("view", {path:"view/:movie_id"});
});
});
App.MoviesIndexRoute = Em.Route.extend({
redirect : function() {
this.transitionTo('movies.list');
}
});
All the rest is done as allways

Append child view outside parent view in DOM in Backbone.js app

In my backbone app I create a popover in parent view and add it to the DOM like this
afterRender: function() {
this.$el.append(
new Popover.Views.Default({
stick:'right',
offsetTop: 3,
offsetRight: 5,
content: "Foo",
reference: this.$el
}).render().$el
);
},
toggle: function(){
app.vent.trigger('popover34:toggle');
}
However the popover is appended to its parent view. The parent view is a link and this causes some css issues in the popover.
My question is, is it safe to do something like:
afterRender: function() {
$('body').append(
new Popover.Views.Default({
stick:'right',
offsetTop: 3,
offsetRight: 5,
content: "Foo",
reference: this.$el
}).render().$el
);
}
Or is there a way to set it just beside the parent view? The problem is that when I toggle the popover it will be added to the DOM multiple times.
To second (and hopefully clarify) Isaac's answer here, your 'parent' view should be in one region, the Popover view should be in another region.
Brian Mann has done an excellent set of videos on Marionette.js and good application design, one specifically that demonstrates your exact scenario.
Good luck,
Aaron
It looks like you're using Marionette. Using Marionette, one way to handle this is with regions. From the docs:
Regions provide a consistent way to manage your views and show / close them in your application. They use a jQuery selector to show your views in the correct place.
You can put your region anywhere you like and trigger it using the Event Aggregator. In addition, it automatically closes old views so you don't end up with zombies.

Convert HTML to Sencha

I am very new to sencha and probably do not know how everything you need. I'm trying to extract some contents of an HTML page using ajax. For example, the text in a div with id = "content".
I want to put this content extracted in a panel or container Sencha.
This is the view that I have:
Ext.define("myapp.view.Main", {
extend: 'Ext.tab.Panel',
requires: ['Ext.TitleBar'],
config: {
tabBarPosition: 'bottom',
items: [
{
title: 'Welcome',
iconCls: 'home',
styleHtmlContent: true,
scrollable: true,
html: FUNCTION_TO_GET_CONTENT .join("")
}
]
}
});
I would like to know if there is any way to get the contents of the HTML page displayed in a panel, but I do not know how. Can someone please help me understand this and how I can best address this?
UPDATE:
Basically what I would like to do is to replicate the result of this JQuery statement:
$("#mylocaldiv").load("sourcePage.html #mainDiv");
and then append the result to the html property in the tab panel.
First, add an alias to your view (make sure it's preceded by widget.
Ext.define("myapp.view.Main", {
extend: 'Ext.tab.Panel',
alias: 'widget.Main',
...
});
Then get your view by doing either:
myView = Ext.Viewport.down('Main');
or
myView = Ext.ComponentQuery.query('Main');
Now you can access your items like so:
myView.items.items
And since your panel is the only item, it will be item zero. You can then use it's setHtml method to append html:
myView.items.items[0].setHtml('your html here');
In terms of loading an html file into the panel, I believe Sencha removed Ext.dom.Element.load(). I would use a simple Ajax request and then append the result into the panel in the success callback. See here: load an html file into a panel
sencha is not a good tool for building large applications, so better to choose another one. I was tried it and it sucks my time and no use at all, the windows panels and many more developed using images not with css. if we build large apps with that we may decline the performance of app.

Backbone.js View Tagname set dynamically?

I'm trying to set up the base architecture for a Phonegap project using jQueryMobile and Backbone.js
We will be targeting multiple platforms and plan on expanding in the future.
I want my Backbone code to be completely ignorant of the types of HTML elements associated with the Views since we have no idea what the views will look like across the platforms.
A simple example would be that on one platform a List of items might be made from a UL and LI elements, but on another for some reason we might want to switch it to a DIV and P elements.
At the moment, if I don't specify a tagName, it defaults to a DIV.
I want to be able to provide a template like this on one page:
<script id="attachmentTemplate" type="text/template">
<li><%= title %></li>
</script>
and on another provide:
<script id="attachmentTemplate" type="text/template">
<p><%= title %></p>
</script>
I can't do that without specifying a tagName (as I said, it defaults to DIV).
Does anyone have any advice or methods to do this? The key is that I don't want the Javascript to be aware of the type of HTML element, I want the HTML to define that.
You can specify a tagName property in your view with a function, so it will be dynamic.
...
tagName: function(){
return this.model.get('tagName');
},
...
Checkout this fiddle http://jsfiddle.net/b7mR6/
Though I wouldn't recommend this architecture you can override your render method:
render: function()
{
// unbind all handlers since delegateEvents was called in the constructor
this.undelegateEvents();
// set this.el to the domElement from the templates
this.el = this.template(this.model.toJSON()); // or however you get to your template
// create the shorthand dom reference that setElement() creates
this.$el = $(this.el);
// re-delegate the events
this.delegateEvents();
}
what that means is that you don't need to worry about your tagName as you defined it in your template.
edit: looking through the Backbone code, this is exactly what setElement does, so in theory you could try
render: function()
{
var tpl = this.template(this.model.toJSON());
this.setElement(tpl, true);
return this;
}
As usual, I search and search and find no answer, then I ask a question and figure it out on my own.
I discovered that if I make my Template like this:
<script id="attachmentTemplate" tagname="li" type="text/template">
<%= title %>
</script>
Then in my js code, when instantiating my View I can do this:
var attachmentView = new AttachmentListItemView({ tagName: $('#attachmentTemplate').attr('tagname'), model: item });
And it works perfectly.
No idea if that will be valid HTML or cause any problems down the road though.

Categories

Resources