I have a working Marionette app using modules together with RequireJS modules largely based on David Sulc's book.
When the server serves the index.html, which includes some base regions (menuRegion and mainRegion), my app.js starts the Marionette app. The app automatically loads the MenuApp module for the menu, and depending on the route, loads a certain module for the main content (i.e KanbanApp). Thus, I require all modules that define routes to be fully loaded before starting the app.
i.e
require(["apps/home/home_app", "apps/kanban/kanban_app"], function(){
Backbone.history.start();
});
At this point, the app is always using the regions defined in the index.html/app.js.
Now, I realize I need a landing page (login form, etc) to be included in the app which has a different layout. I have tried different approaches but I always get to a dead end. Which steps would you try to follow?
My main idea is that depending on the route, an "AppLayout" or "LandingLayout" is loaded. However, the fact that the routes are defined in submodules and I don't have a Main router makes this complicated. Additionally, I am unsure on where would the code for this logic go, maybe to a new extra layer between app.js and each submodule .js? I am unsure this would be the best solution because things get overcomplex. For example: Now when /route-from-the-real-app is called, a submodule route captures it and executes the controller. Until now the controller basically filled the mainRegion. But now, the controller also needs to call this extra layer to display the AppLayout and additionally, trigger an event for the MenuApp to be displayed in the menuRegion. This shouldn't be the original KanbanApp task.
This is how I am unsuccessfully trying to accomplish to do this, but any ideas working on my current code would be greatly appreciated:
I move the regions declared in the index.html/app.js into a new layout and leave it with a unique content region. LandingApp, displays its content here when "/" without problems.
I don't load MenuApp together with the app start anymore.
I created a new MainApp module, with the idea of it being the new extra layer. This module instantiates the LayoutView and adds it to the content region when an event is triggered.
I am unsure on how/when should I load both the MenuApp and the MainApp. As they have no defined routes, I don't require them with the app start, but I need them to register the event handlers.
You can view the full code (very simple) of the app before any of this changes had been applied here: https://github.com/mezod/multikanban
How would you approach this without using any advanced patterns or external libs?
If I understand the question right, you are asking for strategies involved with changing the "page" of your app?
I believe the general approach is to have one layout view instantiated in the app object which represents the container for the "page". That layout view could have a region called page. You could then switch views easily via that region. For example:
var App = Marionette.Application.extend({
changePage : function(view) {
this.rootView.page.show(view);
},
onStart : function() {
this.rootView = Marionette.LayoutView.extend({
el: 'body',
regions: {
'page' : '.page-container' // some div on the page <div class="page-container"></div>
}
});
});
});
Then to change the page (whether in a route method or somewhere else in the app) you can just go App.changePage(myNewPageView). That other page (myNewPageView) could also be a layout view which might contain a region for a sidebar, or a header, or whatever you want.
Related
So, I've read in different places that Backbone and angular don't get along always, and that makes sense with respect to certain modules (like anchor link routing, since there can only be one top dog).
I went ahead and did the work to add angular anyway because it is clearly the better choice and there are way better plugins available for it, including a few table plugins that I am loving.
Now, I've yet to actually do this, but this has been my plan all along: use angular to generate and render a particular view or set of views, but always invoke it using Backbone.
For instance, I will need a "grid view" in the interior of one of my existing Backbone.Views. I am planning on booting angular with the root element provided by the client Backbone.View:
var ClientBBView = Backbone.View.extend({
...
render: function() {
... //do my normal rendering
require(['app/app.boot'],function(appBoot) { appBoot.boot($(targetElement)); });
}
...
}
Of course I would need to make the call specific to the view needed.
Anyway, if I do this, my intent is to demote angular so it doesn't try to take over the whole document. I noticed this line...
define([
'angular',
'app/js/app'
], function (angular, app) {
'use strict';
// as script is at the very bottom of the page no waiting for domReady
angular.bootstrap(document, ['app']); // <-- REPLACE DOCUMENT
});
so that's where the targetElement parameter will go. This way, I am thinking, it won't be able to mess with my routes at least.
Are there any gotchas I should know about with this sort of patchwork use of angular? Is this a common use case? It seems like it would work fine but I could use some advice.
At the moment we are in the process of creating a new web application infrastructure.
We initially load a dashboard which is esentially the top bar displaying the logged in user and the set of menus along with it. Clicking on each menu would load a screen (mostly crud screens) in the main section. We areplanning to put each of the crud screens and their components (services, controllers and such) in a seperate module which will encapsulate all the screens from each other, so for example if there is 78 screens there will be 78 seperate modules for each screen. We are also using planing on using Requirejs to load these dependencies dynamically.
The problem however occurs that we need to link the menu with each of the modules for each screen. Typically in a single module based app it would be done like this.
config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/load', {
templateUrl: 'book.html',
controller: 'BookController'
})
.when('/Screen/load', {
templateUrl: 'chapter.html',
controller: 'ChapterController'
});
Where the BookController and ChapterController will be in the SAME module.
However in our case the BookController will be in a BookModule for the book screen and the same applies for the ChapterController. And the routes would be in the initial main module for example AppModule which loads the dashboard initially during startup then.
How would we say for example link the AppModule and the routes with each module for each screen (for example in this case BookController and ChapterController) keeping in mind that we need to load the modules dynamically when NEEDED using requirejs.
(P.S : We are essentially segmenting our application based on feature where feature in our system usually equals screen)
Also any suggestions on any other way we could best structure our app including an answer to the above problem would be very much appreciated.
Regards,
Milinda
Why do you have the route configuration in the initial main module? This creates unnecessary coupling between your modules (ie. your initial module has to know all the possible routes of every module), sort of defeating the purpose of moving your code into modules in the first place.
Each of your modules can have their own route configuration, which will take effect when the modules are loaded. (A consistent naming convention can help avoiding clashes between routes of unrelates modules)
While configuring the routeProvider, you can use lazy loading mechanisms using the resolve attribute of routes: resources referenced here will be resolved before the routeChange event happens, which enables you to wait for any promise resolution or a requireJS loading of a file. This blogpost might help in that regard.
As of this moment, there is no mechanism as far as I am aware for dynamically loading modules at runtime and then incorporating them in an AngularJS app. You can breakdown your app into 78 individual modules loaded via requirejs, but you will still need a single primary module which has all those other 78 as dependencies. It is this primary module which you will then configure all the routes.
There is work going on into a new AngularJS router which borrows from other more flexible routers (i.e. ui-router, etc) which will allow exactly the sort of dynamic loading of modules you are speaking about but as far as I know it won't be available until AngularJS 1.4.
I'm having an issue thinking about the best way to architect a React app with multiple pages/views (still a SAP).
Let's say we have a simple app with 4 major sections (pages): dashboard, users, stats, comments. Each section has different components in it (think react components). For example, the comments section would have a hierarchy like so:
CommentsSection
- CommentsQueue
-- Comment
--- Text
--- Buttons
- CommentsApproved
--Comment
--- Text
--- Buttons
In a framework like angular for example, the 4 main sections would be split into partials, and loaded in an ng-view upon request, with their respective components inside. When landing on the homepage, the app would only load the dashboard view and upon the user clicking on a nav item, the selected route (i.e. app/users or app/users/:id) would trigger and the app will load the required "template-view-partial" (without a browser refresh).
Now in terms of React, how would this occur? it seems like ALL views and ALL their components would need to be available in a browserified JS file and the app can then update the DOM.
This seems terribly wrong, as we'd be loading all sections in the first load, even if the user doesn't ever need to get to that section. Granted, we could split it with routes on the server, and only serve the components for the page based on the route, but that would require a browser refresh, where as in Angular for example, it would happen without a browser refresh as the view is loaded asynchronously.
The question is, how can this asynchronous loading happen in a React-based app?
I think there's a few different ways in approaching this, I'll explain the approach that I am currently using for my work and side projects.
Instead of using browserify, we use a module-bundler called webpack (https://github.com/webpack/webpack). What's great about webpack is that it's like Browserify but can split your app into multiple 'bundles'. This is great because if we have multiple components/views, the user would just download the features they need for that particular view without having to download everything initially. It allows react-components and their dependencies to be downloaded on demand.
Pete Hunt wrote an article that goes into depth on the benefits of webpack when using it with React (including how to async load react components), and how it is similar/different to Browserify and modern build tools like Grunt/Gulp: https://github.com/petehunt/webpack-howto
I have described one solution using webpack here : http://blog.netgusto.com/asynchronous-reactjs-component-loading-with-webpack/
In essence :
use require.ensure([], cbk) to define code chunks; in the cbk, load your packages synchronously using require()
in your host component, load your asynchronous component in componentWillMount(), and set in in the host component state.
use it in the host component render, when defined on the state
I looked at many examples on the internet how to start develop BB applications with requireJS but I am a kind of lost.
I think AMD has a goal that it load files only if they really needed. Not sooner.
Why I am seeing examples only where the developer put almost every files as a dependency at the beginning of his/her main file?
Here is an example:
https://github.com/jcreamer898/RequireJS-Backbone-Starter/tree/master/js
This application instantly loads main.js which depends on app.js which loads routers/home.js which requires views/view.js which loads the view's template and models/model.js which ... and end.
I can't see how to extend this application for example with more views where views' dependencies (its models, templates, collections, third party APIs, etc) load only when the router calls and initialize them. Other way this would be nonsense to use AMD where you load all your files when initializing your app.
Similar example here:
http://backbonetutorials.com/organizing-backbone-using-modules/
see router.js file.Actually it loads 'views/projects/list' and 'views/users/list' dependencies while the router does not know yet whether the user will need them in the future or not.
Please advise, thanks in advance!
It's a bit hard to see in such a small sample app, because you have to load something on the initial route, and loading something in Backbone usually means a model, a collection, and a view. Since the sample you linked has only one of each, then yes you're loading almost everything.
Where you start to see the "on demand" feature is where you add additional routes/views/models/etc. Keep in mind, however, that on-demand loading is probably a secondary goal of AMD/RequireJS. The primary goal is modularity. They then give you lots of options for either loading things on demand, or bundling everything up via the optimizer
Also there is nothing which says you have to put all the require() at the beginning of the file. You can do them later (e.g. when initiating a route). Here is a modified version of home.js from your first linked example. If you're using Chrome dev tools you can look at the network tab when the "debugger;" statement pauses execution. Then continue execution and see how the rest of scripts get loaded.
define([
'jquery',
'backbone',
'underscore'
],
function($, Backbone, _){
var Router = Backbone.Router.extend({
initialize: function(){
Backbone.history.start();
},
routes: {
'': 'home'
},
'home': function(){
debugger;
require(['views/view'], function (mainView) {
mainView.render();
});
}
});
return Router;
});
See this person's article for more background and where you might go next with it.
I am using Backbone with Layout Manager and RequireJS.
View1 depends on 2 dependencies as can be seen below.
The application also has a similar view
named View2, which depends only on 'jquery.fileupload', unlike View1, which has 2 deps.
define(['jquery.fileupload', 'jquery.fileupload-ui'], function (dep1, dep2) {
var View1 = Backbone.View.extend({
...
});
return View1;
});
The problem is that 'jquery.fileupload-ui' (second dependency) seems to be loaded/evaluated by requireJS even if I don't visit a view that depends on it and that causes some plugin errors (I am using basic fileupload plugin in one view, and extended fileupload plugin in another view). It looks like define() pre-loads modules right away.
How can I avoid loading the second dependency at app initialization and load it only inside my view?
I think I could nest require() call into define for View1,
but I am not sure how then I can return a value if calls are nested.
Any time you module load the view1 module, 'jquery.fileupload-ui' will be loaded. If you only need this module in certain cases when you module load view1, you could have view1 require() in 'jquery.fileupload-ui' only if a certain code path is reached.
You can do this by adding a require(['jquery.fileupload-ui'], function(jqui){...}); in the specific method within view1 where you need the library.
I am not sure if this answers your question, but I think it may.