My broader problem statement is to reduce the JS file size used in the main web page. I minified using grunt uglify to reduce it from 455KB to 140KB.
As a second step, I am removing unnecessary file(s) which are not required on main page. Hence I was able to reduce the initial JS file size to 90KB, with the remaining 50KB file to be loaded later on.
I used Ben Thielker's loadScript function mentioned in
Single page application - load js file dynamically based on partial view to dynamically load the JS file onto head.
Now the issue is that the modules defined in the 2nd JS file are not loaded as
their dependency could not be mentioned earlier at the start of app(Would throw error). I want to load the modules present in 2nd JS file dynamically.
I tried the below in callback function of loadScript mentioned earlier:
angular.module('reportsApp', ['reportsApp.DList', 'reportsApp.item', 'reportsApp.Filters', 'reportsApp.Download'])
.controller('ReportsCtrl', ['$scope', '$rootScope', '$injector', function($scope, $rootScope, $injector) {
$rootScope.accessDenied = !$scope.accessReports;
$rootScope.loadScript('public/js/grid.min.js','text/javascript','utf-8', function() {
console.log('****************grid.min.js loaded successfully.....');
var grid;
var bool = $injector.has('reportsApp.Grids');
console.log(bool);//outputs "false"
if(bool) {
grid = $injector.get('reportsApp.Grids');
}
});
}])
But the above code does not even detect the module.
When I looked into angular's createInjector function, I found they are adding a string 'Provider' at the end of the modulename. Not sure if this is messing things up.
Also, this is the reportsApp.Grids module's 1st line:
angular.module('reportsApp.Grids', ['ui.grid'])
Here, reportsApp.Grids is present in the initial JS file itself,
but 'ui.grid' is only present in the 2nd JS file loaded later on.
How exactly should I use $injector.invoke function here?
OR
What is the best approach for this?
Related
Ok, I'm near to the finish line with my new PHP/JS app built with Gulp and Browserify. The last part is how to "boot", I mean how to do the "first call".
Let's say I have 3 JS entry points
/js/articles.js
/js/categories.js
/js/comments.js
each of them using some JS modules.
Then I have 3 HTML files, requiring their JS
/articles.html
/categories.html
/comments.html
example /js/articles.js
var $ = require("jquery");
var common = require("../common.js");
var viewModel = {
readData: function() {
/* read record from API and render */
},
insert: function() {
/* open a modal to insert new record */
}
};
What I should do now is to perform this sort of "boot": that is calling some init function I need, then load server data, then bind all buttons and stuff to viewModel's methods
$(document).ready(function() {
common.init();
viewModel.readData();
$('#btn-add').click(viewModel.insert);
});
Ok, but where am I to put this?
A) In HTML file?
I can't cause I don't have any global JS variabile to access..
B) Am I put it into articles.js?
At the moment, my Gulp task will bundle everything (articles.js, categories.js, comments.js, common libraries) into a single bundle.js.
If I put it into articles.js it will end up into the bundle.js. So articles-related boot stuff would be called in "categories" page either. And this is wrong.
C) Should I split articles.js into 2 files, one containing viewModel definition and the other doing the $(document).ready stuff?... but again how do I access to the correct viewModel?
Which is the correct solution?
Thank you
Seems your gulp task would just concat all the entries into bundle.js so you probably could just add another entry called js/index.js and put your initialization code inside it.
It's confusing that your code will be executed (base on your description in B) even though you don't call require on it. Can you provide your bundle.js and one of your html file?
What I want to do is load js using the data-bind attribute. I am fairly new to requirejs and knockout and I'm not sure how to go out this.
Right now I have my js split into different require modules for each type of component I have. For example, I have a file that deals with the header dropdown (header.js):
define('headerDropdown',['jquery', 'bootstrap']),function(jquery, bootstrap){
var $menu = $(".menu");
var $dropdown = $menu.find("ul");
$menu.on("click", function () {
$dropdown.toggle("fast");
});
};
What I want to do is:
<div class="header" data-bind="headerDropdown">...</div>
And load the respective js.
Most of my js modules are UI changes based on clicks (show and hiding stuff on click) but I only want the js to load is the html block is on the page.
Hopefully this makes sense!
How can I do this using requirejs and knockout?
Looks like you are mixing concepts. First let's see the define() definition (suppose the file is headerDropdown.js):
define('headerDropdown',['jquery', 'bootstrap']),function(jquery, bootstrap){
var $menu = $(".menu");
var $dropdown = $menu.find("ul");
$menu.on("click", function () {
$dropdown.toggle("fast");
});
};
Require.js does not recommend to define a module expliciting their name ('headerDropdown'); you can get the name based on the filename. That's because require has a tool for optimization of the javascript in production: you can concatenate and minimize the output JS. The optimizer uses the filename to define the module name. Please, avoid defining with name.
If you look at the code, you are requiring ['jquery'] but inside the module definition you're using the global jQuery variable. That's OK because jQuery define their module as a global variable, but the convention is to receive in the function the jquery reference:
define('headerDropdown',['jquery', 'bootstrap']),function($, bootstrap)
You are defining a module that manipulates DOM directly, which goes against the DOM update procedure of knockout. In your case, you are using a data-bing="headerDropwodn" so the headerDropdown is a bindingHandler rather than a simple module. Please check: http://knockoutjs.com/documentation/custom-bindings.html
You can load on require as you pointed on the question. You just need to change your codes:
Load in your HTML an app.js script (for example). This app.js requires knockout and your headerDropdown bindingHandler. In the function declaration you define the ko.applyBindings and that's all.
Greetings!
I am working on migrating an application that manually writes script tags to one that uses the YUI3 loader to manage script dependencies. I'm running into an issue with scripts like jQuery that shouldn't be loaded twice, because in some cases legacy code drops the script on the page, and then the YUI loader later loads it up again. Is there a way to prevent this from happening? It seems like the loader should be able to query for script tags with the same src as the one it's going to create before injecting a new tag.
About 3 / 4ths of this video you'll see how to load external modules not written for YUI and notify the loader that it has been loaded so it doesn't try to do it twice. Basically, you monitor the onProgress event of the loader and when it fires, you call YUI.add() with the name you want to give the module which will then make the loader mark that module as loaded.
It turns out that one way to do this is to call Y.add() proactively on the module name. This works well for modules that are not themselves defined with Y.add() (e. g. jQuery plugins). However, there is a potential race condition if you do this with modules that ARE defined with Y.add(). For example, consider the following sequence:
script for module A is run (calls Y.add('A', ...))
YUI goes off to load the requirements for module A before running the callback
Y.add('A') is called again with an empty callback (to notify YUI that the script file for A is on the page)
YUI since the async load hasn't yet returned, YUI takes the empty callback as the definition of A
The fix is to use the following script:
var preloadedModules = ['a', ... ]; // list of module scripts already on the page
, noop = function (Y) { }, version = '#VERSION#', i, len, moduleName;
for (var i = 0, len = preloadedModules.length; i < len; ++i) {
moduleName = preloadedModules[i];
if (!YUI.Env.mods[moduleName]) { // avoids overwriting YUI module definitions
YUI.add(moduleName, noop, version);
}
}
I am working with a website built with Jade/Express for a few weeks now. I recently organized the image folder for the website so all the images were disbursed between several folders to make it easier to use and sort through.
To make it easier to make changes to the hierarchy of images (and other such files) I wrote a script that contains some globals for file paths. Now I have been trying to get the script to run so that I can call functions inside the jade template to automatically use these globals.
For example. Images are now sorted into several folders:
File Hierarchy
img/
glyphs/
interactivity/
buttons/
...
In my path manager script, I created several functions, including the following:
In: path-manager.js
images_root_path = "/img/";
glyph_path = images_root_path + "glyphs/";
function getGlyph(color, name) {
return glyph_path + color + "/" + name;
}
I tried several methods to get the script to execute before the template. Here is one of the attempts:
In page.jade
include ../../../public/js/lib/path-manager.js
=NamespacePathManager();
The above is, in theory, supposed to include the js and then I execute the namespace below to make the functions available, but that isn't working.
This is a portion of the Jade template that I want to use the function in:
In page.jade after the script include
span.challenge-time
img(src=getGlyph("black","stopwatch.png"), style="margin-right:5px;")
The above example should return: "/img/glyphs/black/stopwatch.png"
The problem is, I believe, that the scripts I am trying to make available server-side to the jade template are not being executed before the jade template is rendered. Everything I have tried doing to get this to work always results in an error saying that the server doesn't recognize the function getGlyph or when I started using the namespace function, NamespacePathManager
Summary: I want a javascript file to execute before a jade template is rendered into a webpage so that I can call functions and variables from that javascript on the server to use while rendering the jade template. My problem is that all the methods I have tried are unable to execute the javascript before the Jade is rendered.
Update
One work around I found was to put the javascript into unbuffered code directly on the page including a jade. This isn't quite the elegant solution I was looking for, but it works for now
- some code
- more code
This code is executed inline. The downside is that I have to include it on every page manually - instead of just including it once and having the functions available everywhere.
You can register helper methods in Express that will then be accessible in the views.
So in your case, the path-manager.js can be the helper file that you register, and contains:
var images_root_path = "/img/";
var glyph_path = images_root_path + "glyphs/";
exports.helpers = {
getGlyph: function (color, name) {
return glyph_path + color + "/" + name;
}
// Other helper methods here..
};
Then when setting up the express server, you register the helper
var app = express.createServer();
// app.configure here...
// app.use ...
app.helpers(require('./path-manager.js').helpers);
// Routes set up here ..
Finally, you can call the helper method from Jade view like this:
span.challenge-time
img(src='#{getGlyph("black","stopwatch.png")}', style='margin-right:5px;')
There's a good write up on this topic at DailyJS http://dailyjs.com/2011/01/03/node-tutorial-8/
I will explain my idea behind this:
I use python for google app engine + js + css
the main project will be stored under the src folder like this:
\src
\app <--- here goes all the python app for gae
\javascript <--- my non-packed javascript files
\static_files <--- static files for gae
now the javascript dir looks like this
\javascript
\frameworks <--- maybe jQuery && jQueryUI
\models <--- js files
\controllers <--- js files
\views <--- HTML files!
app.js <--- the main app for js
compile.py <--- this is the file I will talk more
About compile.py:
This file will have 2 methods one for the min and other for the development javascript file;
When is run will do:
Join all the files with "js" extension;
The app.js contains a variable named "views" and is an object, like a hash; Then the compiler copy the contents of each file with "html" extension located in the "/javascript/views/" dir using this rule;
example: if we have a view like this "/views/login.html" then the "views" js var will have a property named "login"; views['login'] = '...content...';
example2: "/views/admin/sexyBitcy.html" then view['admin.sexyBitcy'] = '...content...' or whatever exists in that html file..;
Then this big file will be saved into the "/src/static_files/core.js"; if is minified will be saved as "/src/static_files/core.min.js";
The javascript will use dependenccy injection, or sort of it. (:
I will explain how it will work then:
the index.html that is loaded when you come into the site loads the core.js and the jquery.js;
the core.js will create the layout of the page, as SEO is not important for the most of the pages;
the core.js uses the controllers-models-views to create the layout of course; the html for the layout is inside the var "views"; will be a heavy variable of course!
Some code:
mvcInjector = new MVCInjector;
mvcInjector.mapView(views['login'], 'login', LoginController);
parent = $('#jscontent');
jquery
view = mvcInjector.instanceView('login', parent); // <--- this will create the contents of the views['login'] in the parent node "parent = $('#jscontent');" then will instance the LoginController that will map the "SkinParts" (like in FLEX if you know); what does it mean map the "SkinParts"? - when the user will click on a button an handler for that action is defined in the controller; ex.:
// LoginController
this.init = function(){
// map skin parts
this.mapSkinPart('email', 'input[name]="email"');
this.mapSkinPart('submit', 'input[name]="submit"');
// link skin parts to handlers
this.getSkinPart('submit').click = this.login;
}
// handlers
this.login = function(event){
// connect to the db
// some problems here the get the value as the "this" keyword references to the this of the controller class, I will work it around soon
alert('open window button1' + this.getSkinPart('email').value());
}
If something is not clear just say something, I will be happy to explain;
So the question remains: is this scalable, manageable and fast enough for a big RIA application build with javascript+jquery and maybe with jqueryUI?
Thanks ;)
I like your idea quit a bit.
I would think about loading html pages by ajax, if they are big and there are many of them...
Have a look on angular project, I hope, it could help you a lot. It's a kind of JS framework, designed to work together with jQuery. Well suitable for test driven development.
It uses html as templates, you can simply create your own controllers, use dependency injector, etc... Feel free to ask any question on mailing list.
Then, I must recommend JsTestDriver - really cool test runner for JS (so you can easily run unit tests in many browsers, during development - let's say after save...)