Meteor: reference other javascript classes in different directory - javascript

In my meteor application. I defined a BaseControllerin base_controller.js:
BaseController = RouteController.extend({
layoutTemplate: 'mainLayout'
});
Then I defined PostController in post_controller.js:
PostController = BaseController.extend({
});
If I put base_controller.js and post_controller.js in same directory, no error founds. But if I put in different directory, such as base_controller.js in controller and post_controller.js in controller/post I will meet exception when running application:
ReferenceError: BaseController is not defined
My question is: how can I divide those javascript file into different directory? I need to do this, because my application will have many controllers so put all controllers into same directory (without any child directory) will make project hard for maintenance.
Thanks :)

You have to understand how Meteor loads your files. See sub-section File Load Order: http://docs.meteor.com/#/full/structuringyourapp
From the docs:
There are several load ordering rules. They are applied sequentially to all applicable files in the application, in the priority given below:
HTML template files are always loaded before everything else
Files beginning with main. are loaded last
Files inside any lib/ directory are loaded next
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
If you want your files to be loaded first, put them in lib directory in the root of your project or in any subdirectory.
Your PostController depends on BaseController, so I would put BaseController into lib folder.
If you need to specify exact file load order, you can create a package with desired functionality. You can specify exact file load order only in packages.

If both are in the lib/ directory, I think the answer is from 4 and 5 from the docs:
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
So it should be the case that lib/controller/post/PostController would get loaded before lib/controller/BaseController, since its path is deeper.
I think the solution is probably to move them both into lib/controller so that BaseController gets loaded first, since it will be first alphabetically.

Related

Meteor index.js is implied, how?

I have a pretty basic question and I've read through the Meteor Application Structure but this is still a little confusing:
In meteor chef's understanding the imports directory, it says that:
The index.js file is implied by not specifying a filename on the end. This is also known as an "entry point" file.
When I ran meteor create testproject --full to create a new project, in /client/main.js it writes import '/imports/startup/client';
Why doesn't main.js include the index.js file directly?
Why does import '/imports/startup/client' automatically include the index.js file only?
In Meteor's official documentation, index.js is not a reserved word.
HTML template files are always loaded before everything else
Files beginning with main. are loaded last
Files inside any lib/ directory are loaded next
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
Quoted from another question.
As Styx's comment points to, importing the index.js file is a characteristic of the CommonJS module system, which Node uses a version of and Meteor uses under the hood on the client.
The scaffold elects to not specify the index.js file for brevity.
It's also worth noting that the load order you've quoted does not apply when using the imports directory and ES6 imports. Files will be loaded in the order that they are referenced by the code.

Resolving to a different main file per directory

I have a components directory which hosts several sub folders with actual components. Each folder has main file named as the name of folder plus extension. Then I have to create index.js file containing something like export default from './MyComponent';.
So my question is if there is a way in Webpack to get rid of those index.js files and instead dynamically point to component file itself. Perhaps I could write some plugin, I haven't looked much into a plugin system yet. Just wondering if it's even possible to change fundamentals like this.

Tapestry error: The resource path was not within an aliased path

I am not a Java developer and forced to work with Tapestry 5.3.8.
I am able to use assets like css, images and a fav icon in my web page. I do this by injecting them in my page like this:
#Inject
#Path("context:static/img/logo.png")
private Asset logo;
#Inject
#Path("context:static/img/favicon.ico")
private Asset favIcon;
This works without problems. Although it works, I am not convinced the assets are in the right folder. Should they be placed in folder 'static'?
Now I want to add a Javascript file. This is what I did:
#Import(library = "context:static/js/additional.js")
public class Master {
// ...
}
The file is found, but I runtime i get the following error: "The resource path was not within an aliased path".
From what I understand is that I need to create an alias for the path where my Javscript file is located, but how?
I guess this must be done in method "contributeClasspathAssetAliasManager" in CiAppModule.java. Is this right, and what should I add?
I've never tried to use a context: asset for a javascript file. Tapestry makes classpath assets available under the assets/ alias which is possibly what this error is referring to. See the mention of 'asset fingerprinting' here
A possible souition is:
package foo.bar.components;
#Import(library = "additional.js")
public class Master {
And then have foo/bar/components/additional.js available on the classpath. For maven / gradle this will mean the file lives at src/main/resources/foo/bar/components/additional.js
The problem was in this part:
#Import(library = "context:static/js/additional.js")
When I change it into
#Import(library = {"context:static/js/additional.js"})
it works.
Thanks to Tapestry's error reporting, this was not clear at all. Grrrr.

Concatenate modules in RequireJS

I am using RequireJS to modularize my code. The website I am using will have several distinct page categories. For each one of them I want to load general JS file with all the dependencies that exist across all page categories, jQuery being most obvious one. I have managed to do that already.
For the rest of the pages, I want to have separate JS files. Each page has its own module but I am thinking that it would make more sense if I download several pages modules all at once and this way when the user comes to other pages, he will have the JS file already cached and won't have to download.
For now I tried to concatenate these modules which did work rather well. However, I am not sure how to load the specific module from a file. So far I am doing this:
require(['./js/common'], function (common) {
require(['public']);
});
common is the dependencies file and public is my concatenated file with all the dependencies. Inside it has modules for each page but I don't know how to call a specific one. Any ideas?
Take a look at the bundles option for RequireJS' runtime configuration. In your case it would be something like:
bundles: {
public: ['mod1', 'mod2', ...]
}
I've used mod1, mod2 because I don't see the name of the actual modules in your question but what you'd want there are the names of the modules inside the public bundle and that you want to load individually. With this, RequireJS will know that when you want to load such module, it has to get them from public instead of searching for them indivdually.
Then you should be able to change your loading code to:
require(['./js/common'], function (common) {
require(['mod1']);
});
Again, mod1 is for illustration.
Take a look at require-lazy.
It allows you to bundle independent parts of the application into separate bundles. From the developer's point of view, instead of:
define(["module1", "module2"], function(module1, module2) {...});
you write:
define(["lazy!module1", "lazy!module2"], function(module1, module2) {...});
Modules 1 & 2 will not be loaded upfront; they and their dependencies will be loaded lazilly when you call:
module1.get().then(function(realModule1) {
// here you have access to the real module1
});
Check out the examples for a few use cases.

Is there a dynamic module loader for AngularJS that uses convention over configuration?

Most dynamic modules that I can find are not really dynamic and are actually lazy loaders, as they mostly require you to register what you want loaded in config files and registers.
I am looking for a loader js way of using convention and just auto loading anything under folders for my one page app.
Something like:
app
public
loader.js <-- no need to register anything manually
modules
module1
controllers
controller1.js
controller2.js
directives
directive1.js
directive2.js
services
service1.js
service2.js
views
view1.js
view2.js
Careful here.
You say you want loaders.js to be in your public folder. What you want to do only works with server side tools, as you can't read your filesystem from a browser (without server side assistance). Therefore you don't want to expose the loaders.js file to the public.
The thing about modules is, that they help you give your application some structure. Of course, you don't have to use this (the angular way, although you should if you want to support your app with a unit test suite * [see below]). If you just want to load everything you have, then you don't have to define any modules at all (but one). But you can still benefit from the structure you setup in your file system. Given that, it's sufficient if you have all your controllers, directives and services belong just to "yourApp" which is the only module you will have defined:
angular.module('yourApp', []); // defined once, maybe within an
// inline script tag at the top of your index.html
+ (at the top of each of your controllers / directives / services):
angular.module('yourApp').controller(.....
In this case, grunt-html-build will do the job:
https://www.npmjs.org/package/grunt-html-build
Of course, as grunt is just a command line based task runner, this will introduce a build step (i.e. your app will not work by just serving the contents of its folder in a web server) to your workflow. Every time you add a new file you have to run the grunt-html-build task again.
What will you get from grunt-html-build?
You will be able to denote a section in the head of your index.html, where grunt-html-build will insert all the files you specify in the configuration, e.g. like this:
grunt.initConfig({
modulesPath: "modules",
htmlbuild: {
dist: {
src: 'index.html',
dest: 'public/',
options: {
relative: true,
scripts: {
modules: '<%= modulesPath%>/**/*.js'
}
}
}
}
});
Then in your index.html, put at the top of your head (after you've loaded angular):
<!-- build:script modules-->
<!-- /build -->
This is the part where all your .js scripts below modules/ will be inserted into.
* The downside of having just one big module is, that you will load and instantiate lots of unnecessary services in your unit tests (i.e. whatever you inject into your app's run and config blocks). It'll increase the memory foot print of your test runner's browser and the tests will be slower, too.

Categories