How do I load a 'child' application inside a 'parent' application?
I have this main application called the Frame and several child applications. The frame has a border layout. On the left there are some buttons (like a menu) to load other projects. In the center there is the container for my child projects. The frame project has the default mvc structure.
Inside the folder I put a Test application. Also the default structure.
/frame/app/controller
/view
/test/app/controller
/store
/model
/view
/css
/app.html
/app.js
/test2/app/controller
/store
/model
/view
/css
/app.html
/app.js
/css
/app.html
/app.js
something like this.
Now I wanted to load from a button in the frame a child project.
So my function would look something like this:
function(){
Ext.require('Test.view.testMainContainer', function(){
var toPutInMyContainer = Ext.create('Test.view.testMainContainer');
console.log(toPutInMyContainer);
});
}
With this code are 2 things wrong:
the js is in test/app/view/testMainContainer.js
the function never fires...
What is the best structure an how should I approach this?
What i want to do next is inside the testMainContainer are requires for controllers, models, stores and views and I want to load them automatically when needed.
There are various strategies to achieve this.
With respect to the title question, you probably want to have a look at the SubAppDemo by Mitchell Simoens, which demonstrates how to load an sub application within an application.
A similar, yet different, approach is to dynamically load controller upon request. Here is my code to do (part of the application object):
loadPage: function(aControllerName)
{
var iController = this.dynamicallyLoadController( aControllerName ),
iPage = iController.view,
iContentPanel = this.getContentPanel(),
iPageIndex = Ext.Array.indexOf(iContentPanel.items, iPage);
// If the page was not added to the panel, add it.
if ( iPageIndex == -1 )
iContentPanel.add( iPage );
// Select the current active page
iContentPanel.getLayout().setActiveItem( iPage );
},
dynamicallyLoadController: function(aControllerName)
{
// See if the controller was already loaded
var iController = this.controllers.get(aControllerName);
// If the controller was never loaded before
if ( !iController )
{
// Dynamically load the controller
var iController = this.getController(aControllerName);
// Manually initialise it
iController.init();
}
return iController;
},
When the controller is loaded dynamically, all its models and stores are also loaded dynamically. In my case, I always explicitly create the first view of the controller (which means the view is also dynamically loaded) and inject it into the controller's view property (controller code):
init: function()
{
this.callParent();
// The dynamically created view is stored as a property
this.view = this.getView(this.views[0]).create();
},
With regards to your code, I'm not sure why your function will fire in the first place. But it should work if you put the Ext.require outside any function.
Related
I've built a ractive.js app using partials. These partials are loaded via fetch/ajax - and all works nicely.
I then decided I wanted to encapsulate data along with the partial so looked at components - as I understood a component to do just that: Isolate a template/partial with its data.
I then looked to load the components in: http://ractivejs.github.io/ractive-load/
However, I don't really see the advantage of this approach - as it appears with the loader you can only load in the components template, not the entire encapsulated component (data, templates etc). You still have to put the data onto the main ractive instance (as you would with a partial).
I'm trying to dyanamically update the component. I'm also using page.js for routing. I'm trying to separate out all the concerns.
I'm probably not explaining myself very well - here is my code... most of it was taken from martydpx's answer here How to create Ractive's subcomponents dynamically and change them programmatically )
....
<dynamic name='{{name}}'/>
</script>
<script>
// Component loader
Ractive.load({
home: '/components/home.html', // seems this can only contain a template. Is it possible for it to contain everything - data and all?
packs: '/components/packs.html',
....
addplayer: '/components/addplayer.html',
notfound: '/components/notfound.html',
}).then( function ( components ) {
Ractive.components[ 'home' ] = components.home;
Ractive.components[ 'packs' ] = components.packs;
....
Ractive.components[ 'addplayer' ] = components.addplayer;
Ractive.components[ 'notfound' ] = components.notfound;
// dynamically load component based on route
Ractive.components.dynamic = Ractive.extend({
template: '<component/>',
components: {
component: function() {
this.set('foo','bar'); // I can dynamically set the data here.. but how would I add defaults for each component, within the component?
return this.get('route');
}
},
oninit: function(){
this.observe('route', function(){
this.reset();
},
{ init: false}
);
}
});
var r = new Ractive({
el: document.body,
template: '#template',
data: {
route: 'home'
}
});
// Routing. Sets the route... which triggers the component
page('/', index);
...
page();
function index() {
console.log('index');
r.set('route','home')
}
EDIT
I've read this - which has been a great help :)
https://github.com/ractivejs/component-spec/blob/master/authors.md
In the dynamic component scenario - how would I dynamically update component specific data. I seem to be able to do it when the component tag is hardwired into the page... but not when the component tag is dynamically created. After much playing about in the console - its as if it doesn't see the dynamic component. So things like r.findComponent('home').get() don't work.
Yet, if I put a <home/> tag in the template - it does work.
Also, do components automatically 'tear down' when they're un-rendered?
I'm not 100% sure what you are looking for.
First you create a child component -
var MyWidget = Ractive.extend({
template: '<div>{{message}}</div>',
data: {
message: 'No message specified, using the default'
}
});
You register this with Ractive runtime
Ractive.components.widget = MyWidget;
Then you create a parent component
var Parent = Ractive.extend({
template: '<div>
<MyWidget message={{widget}} />
</div>'
});
You use the parent instance to pass the data to child
// Live instance of parent
new Parent({
el: 'id',
data : {
widget: {
message : 'Waddup kiddo'
}
}
});
data.widget gets mapped to MyWidget's data, in-turn gets the message data.
For more info refer this
Generally there are 3 types of components you will be creating & using -
Self-sufficient Components - It knows everything it needs to know by itself. You don't pass anything to it. It creates it's own data or knows where to get it from. Ex: A logo component which knows by itself where to get the image from.
Dumb Components - They have no intelligence and all the data that it needs should be passed from parent. Like in our example - MyWidget has no idea where and what message stands for. Just renders it. No questions asked. Parent will fetch message and just pass it on.
Smart Components - Components which do some heavy lifting. An example would be Profile component. Parent will pass just a profileID to this, and it knows where to get profile data from, does some ajax calls, knows how to parse and interpret the data, may be even starts a socket and listens to changes etc.
So you decide how you want to make your components, who takes responsibility and think about data-encapsulation then.
Once I register a helper function for Handlebars using Handlebars.registerHelper(), is it possible for me to change and/or remove the helper? Can I just use registerHelper() again to overwrite the current helper, or is there such a thing as Handlebars.unregisterHelper()? Or should I use a different approach if I need a helper to change during an application?
The use case for me is with the Iron Router plugin for Meteor. I am using a layoutTemplate as the general structure of my page. I wanted to use a helper in the layout template right before I yield the main content of the page body (via a <template>, per se) so that each individual template can define its own page title but not have to specify the location in the page every time. For example, my layout template could look like this:
{{pageTitle}}
{{yield}}
And then in the .js file for the rendered template, I would use the following to fill in the {{pageTitle}} placeholder:
Handlebars.registerHelper("pageTitle", function() {
return "My Page Title";
};
Perhaps there is an alternative way to solve this problem.
What you can do is something like this
Handlebars.registerHelper("pageTitle", function() {
return Session.get('pt');
};
function changePageTitle(str){
Session.set('pt', str);
}
Meteor, being reactive, should update the page when a session variable changes. When you switch to another page, simply run changePageTitle.
I'm trying to figure out following scenario:
Lets say that I have two views: one for viewing items and one for buying them. The catch is that buying view is a sub view for viewing.
For routing I have:
var MyRouter = Backbone.Router.extend({
routes: {
'item/:id': 'viewRoute',
'item/:id/buy': 'buyRoute'
}
});
var router = new MyRouter;
router.on("route:viewRoute", function() {
// initialize main view
App.mainview = new ViewItemView();
});
router.on("route:buyRoute", function() {
// initialize sub view
App.subview = new BuyItemView();
});
Now if user refreshes the page and buyRoute gets triggered but now there is no main view. What would be best solution to handle this?
I am supposed that the problem you are having right now is that you don't want to show some of the stuff inside ViewItem inside BuyView? If so then you should modularized what BuyView and ViewItem have in common into another View then initialize it on both of those routes.
Here is a code example from one of my apps
https://github.com/QuynhNguyen/Team-Collaboration/blob/master/app/scripts/routes/app-router.coffee
As you can see, I modularized out the sidebar since it can be shared among many views. I did that so that it can be reused and won't cause any conflicts.
You could just check for the existence of the main view and create/open it if it doesn't already exist.
I usually create (but don't open) the major views of my app on booting up the app, and then some kind of view manager for opening/closing. For small projects, I just attach my views to a views property of my app object, so that they are all in one place, accessible as views.mainView, views.anotherView, etc.
I also extend Backbone.View with two methods: open and close that not only appends/removes a view to/from the DOM but also sets an isOpen flag on the view.
With this, you can check to see if a needed view is already open, then open it if not, like so:
if (!app.views.mainView.isOpen) {
//
}
An optional addition would be to create a method on your app called clearViews that clears any open views, perhaps with the exception of names of views passed in as a parameter to clearViews. So if you have a navbar view that you don't want to clear out on some routes, you can just call app.clearViews('topNav') and all views except views.topNav will get closed.
check out this gist for the code for all of this: https://gist.github.com/4597606
I have an application that has a middle panel that always changes depending on what part of the application the user is looking at. These might be messages, transactions etc.
Then there are 4 'fixed' panels at the 4 corners of the application around the middle panel that are mostly fixed for the lifetime of the application, but contain dynamically updated data and therefore need to be implemented using backbone.js
How do I structure such an application in backbone.js. It seems to defeat the "Do not repeat" rule to implement the intial rendering for all the side panels within every route in the router as I would end up repeating the same rendering code in every route.
How do I structure my code in this instance so that I don't repeat code in multiple places.
JavaScript is like any other code: if you find yourself writing the same lines of code, extract them in to a function. If you find yourself needing to use the same function, extract it (and related functions and data) in to its own object.
So, your router shouldn't be calling your views and models directly. Instead, it should be delegating to other objects that can manipulate your views and objects.
Additionally, since your going to set up the same basic page layout every time the app starts up, you might not want that code in the router. The layout happens whether or not the router fires, and no matter which route is fired. Sometimes it's easier to put the layout code in another object, as well, and have the layout put in place before the router fires up.
MyApplication = {
layout: function(){
var v1 = new View1();
v1.render();
$("something").html(v1.el);
var v2 = new View2();
v2.render();
$("#another").html(v2.el);
},
doSomething: function(value){
// do someething with the value
// render another view, here
var v3 = new View3();
v3.render();
$("#whatever").html(v3.el);
}
}
MyRouter = Backbone.Router.extend({
routes: {
"some/route/:value": "someRoute"
},
someRoute: function(value){
MyApplication.doSomething(value);
}
});
// start it up
MyApplication.layout();
new MyRouter();
Backbone.history.start();
I've written a handful of articles relating to these things, which you might find useful:
http://lostechies.com/derickbailey/2012/02/06/3-stages-of-a-backbone-applications-startup/
http://lostechies.com/derickbailey/2011/08/30/dont-limit-your-backbone-apps-to-backbone-constructs/
http://lostechies.com/derickbailey/2011/12/27/the-responsibilities-of-the-various-pieces-of-backbone-js/
http://lostechies.com/derickbailey/2012/03/22/managing-layouts-and-nested-views-with-backbone-marionette/
New to using Backbone and have a very simple application. Basically there are Clients and ClientItems. I have a view to show all Clients and if you click on a Client you get taken to their ClientItems. Going to this ClientItems view should just hide the Clients view and going back to Clients should hide ClientItems. Now, in my render() function for each view, it is going through the collections and dynamically adding stuff to the page. When I go back and forth between the two (using the back button) I don't really need to fully render again as all the data is there in the page, just hidden. Where should this logic go? Right now I have it in the render() function but it feels sloppy, what is the preferred way of handling this?
We are using a global variable App with several common function used across application:
var App = {
initialize : function() {
App.views = {
clientView : new ClientsView(),
clientItemView : new ClientsItemsView()
}
},
showView: function(view){
if(App.views.current != undefined){
$(App.views.current.el).hide();
}
App.views.current = view;
$(App.views.current.el).show();
},
...
}
And then I use this App from other parts of application:
App.showView(App.views.clientView);
IntoTheVoid's solution is good – it's nice to have a single place to hide/show views. But how do you activate the logic?
In my experience, routers are the best place for this. When a route changes and the appropriate function is called, you should update the active, visible view(s).
What if you need multiple views to be visible at once? If you have a primary view that always changes when the route changes, and multiple subsidiary sticky views, you need not worry. But if it's more complex than that, think of creating a ComboView that neatly packages all the relevant views into one containing el node. That way the above logic still works, and your router functions are not littered with logic for managing what views are visible at the moment.