How to load angularjs scripts files - javascript

I have an angularjs application, and there are a bunch of controllers, services and directives. Let me say they are controller.js, service.js and directive.js, but the truth is there are many more js files than three. To reduce the http request, I'm combining those js files into one, let me say it's app.js. So my index.html looks like
<html lang="en">
<body>
<div data-ng-view></div>
<script src="app.js"></script>
</body>
</html>
However, in my development environment, I want to debug separated files not the combined one. A modified index.html gives the capability.
<html lang="en">
<body>
<div data-ng-view></div>
<script src="controller.js"></script>
<script src="service.js"></script>
<script src="directive.js"></script>
</body>
</html>
However, I don't want to change the index.html. Is it possible to define something like:
require('controller.js');
require('service.js');
require('directive.js');
in my app.js. I've done some search, and the results show there's a way using angularjs and requirejs together, however, it needs me to re-define my controllers and services in requirejs way. It takes a lot of effort in my case. And, I don't need to implement the dynamically loading since in production environment, there's just one javascript file that needs to be downloaded.

A very nice solution is to use sourcemaps. so even though you have one file, the browser knows the initial file! Read more html5rocks.
Also I would strongly advise you to use a javascript task runner/build system for these kind of jobs, like grunt or gulp.
To concatenate and create the sourcemaps with gulp is as simple as:
var gulp = require('gulp');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('javascript', function() {
return gulp.src('src/**/*.js')
.pipe(sourcemaps.init())
.pipe(concat('all.js'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist'));
});
A second option is this plugin gulp-preprocess where you can set up different env variables let's say dev, production . And depending on the task load either the concatenated file or each one individually.
Sorry for providing solution based on external plugins but I think they can save you lots of time !

If you want to keep the files separate for testing and debugging, but combine them for production, I'd recommend a build system like Grunt.js or my personal favorite, Gulp.js. Using these automated build tools, you could have it create a task where it will create your project directory, combine, minify, and mangle the JS files and generate maps that allow dev tools to reference the original files. This can do wonders for your workflow. This does require Node.js, so you will have to install that as well. It's still a good amount of effort, but I'd think it would be better than rewriting all your code.
An example build file for your specific case might look like:
//this(my-js) will grab all js file in the js directory, combine, minify, mangle,
//rename and create sourcemaps. it also has a dependency(js-libs) to handle
//your libraries first.
gulp.task('my-js', ['js-libs'], function() {
return gulp.src([
'src/js/*.js',
])
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(concat('app.min.js'))
.pipe(uglify())
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('build/js'));
});
//this task will run the js task and then start a task that watches the files for
//changes and reruns the my-js task anytime something changes
gulp.task('default', ['my-js'], function () {
gulp.watch('src/js/*.js', ['my-js']);
});
Now this is just an example I came up with off the top of my head. Untested. Once es6 comes out we'll have access to import 'somefile'. Also, keep in mind that when http/2 is fully implemented, we'll have multiplexing, which will allow for multiple file requests in a single connection: https://http2.github.io/faq/#why-is-http2-multiplexed

Related

how to use webpack to load CDN or external vendor javascript lib in js file, not in html file

I am using react starter kit for client side programming. It uses react and webpack. No index.html or any html to edit, all js files. My question is if I want to load a vendor js lib from cloud, how to do I do that?
It would be easy to do that in a html file. <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
However, in js file, it only uses npm installed packages. How can I import the above lib with no html file? I tried import and require, they only work for local files.
update 10/21/15
So far I tried two directions, neither is ideal.
#minheq yes there is a html file sort of for react start kit. It is html.js under src/components/Html. I can put cloud lib and all its dependencies there like this:
<div id="app" dangerouslySetInnerHTML={{__html: this.props.body}} />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
<script src="/app.js"></script>
<script dangerouslySetInnerHTML={this.trackingCode()} />
</body>
Good news is it works, I don't need do anything else in js file, no import or require. However, now I have two jquery libs loaded in different ways. One in here, the other through npm and webpack. I wonder it will give me trouble later. The react-routing I use give me 'undefined variable' error if I type a none home path in browser window due to the server side loading I guess. So this solution is not very good.
Use webpack externals feature. This is documented as: link. "You can use the externals options for applications too, when you want to import an existing API into the bundle. I.e. you want to use jquery from CDN (separate tag) and still want to require("jquery") in your bundle. Just specify it as external: { externals: { jquery: "jQuery" } }."
However, the documentation I found a few places are all fussy about how to do this exactly. So far I have no idea how to use it to replace <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script> in html.
externals is not intended to let you do this. It means "don't compile this resource into the final bundle because I will include it myself"
What you need is a script loader implementation such as script.js. I also wrote a simple app to compare different script loader implementations: link.
var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", function() {
$('body').html('It works!')
});
You can create a script tag in your JS as
$("body").append($("<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>"))
There is one html file that is definitely being used to serve to users with your js bundle attached. Probably you could attach the script tag into that html file
Use webpack's externals:
externals allows you to specify dependencies for your library that are
not resolved by webpack, but become dependencies of the output. This
means they are imported from the environment during runtime.
I have looked around for a solution and most of all proposals were based on externals, which is not valid in my case.
In this other post, I have posted my solution: https://stackoverflow.com/a/62603539/8650621
In other words, I finished using a separate JS file which is responsible for downloading the desired file into a local directory. Then WebPack scans this directory and bundles the downloaded files together with the application.

Trying to precompile html files in to one js file using grunt/gulp

In my Single page application project, I am using client templates. After searching on Internet, I came to know that client side templates can be organized in three ways:
Writing Script tags in index.html with type, say: text/template.
Writing the templates in separate .html files and write the code in server to gather all the .html files and create one .js file.
Watching changes in template on client side: In this there will be a compiler which watches the .html template files for changes and generates/updates a .js file. (just like SCSS or less)
The first approach is making my index.html look messed up. So, I decided to go with the third approach because it keeps client side code to itself without any interference from sever code (like second approach).
I am using Backbone and lodash.js for templates.
I found that I can watch the templates for changes using grunt's grunt-template-module.
I have gone through getting started of grunt and added Gruntfile.js and package.json in the root folder of my project.
Gruntfile.js
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
template-module: {
myTemplates:{
files: {
"demo.js": ["app/templates/header.html"]
}
options: {
module: true
}
}
}
});
};
package.json
// empty file
Now, I am not able to understand what should I do next? Any help would be really appreciated?
PS: The backend technology is PHP. I am using node.js just to access grunt. Please also let me know if there is any better alternative.

Gulp: running through all dependencies without duplicating?

gulp.task("compile-vendor-js", function() {
return gulp.src("./bower_components/*/*.js")
.pipe(concat("vendor.js"))
.pipe(gulp.dest("./build"))
});
This gulp task will compile bower solved dependencies.
The problem is, it will consider all JS files, including the minified ones, making my build file have duplicated code.
I know that one solution for this is having a variable array containing all file paths, but this isn't good.
try something like:
gulp.src(["./bower_components/*/*.js", "!./bower_components/*/*.min.js"])
where you can find a common denominator between all the minified js files (eg, .min.js)
I think that a blacklist of files is going to be shorter than a whitelist in this case.
Also, you might consider looking into the main-bower-files project, which will read your bower.json file and pull out the main js files from each project for you.

Handling concat in prod with non-concat in dev

I'm currently working on a large angular.js project. I use grunt to concat/uglify all of my files into one large target.js file, which I then include on my index page.
I've come to the realization that this makes it quite difficult to debug in the dev environment since the code is then all on one line and super ugly. I thought about setting up a second grunt task that leaves the files pretty and separate (see this answer: Alternate grunt.js tasks for dev/prod environments) but then I have the problem of having to include all of the files on the index page when in dev, but removing those references and referencing just the ugly concat target in production.
I'm hoping to find a solution that allows me to keep pretty code in dev and ugly concat code in prod using grunt. I considered just adding script tags on the fly when in dev, and then somehow removing them when I use the production task, but this seems like a headache that might not be necessary, as well as I don't know how I would determine what script tags need to be removed/replaced.
I'm not 100% sold on this approach, as I'm just beginning the project and want to get this right the first time, so if you have a suggestion that better handles this situation, I would be open to accepting that answer as well.
A solution is to use the grunt-usemin and grunt-contrib-concat . That way you can define a block of ressources (css / js) that will be concatenated only when you execute the usemin task.
For example :
index.html :
<!-- build:js js/app.js -->
<script src="js/app.js"></script>
<script src="js/controllers/thing-controller.js"></script>
<script src="js/models/thing-model.js"></script>
<script src="js/views/thing-view.js"></script>
<!-- endbuild -->
Gruntfile.js :
// simple build task
grunt.registerTask('build', [
'useminPrepare',
'concat:generated',
'usemin'
]);
That way, files will be concatenated only at build time, leave your index.html will all the single references untouched in DEV mode
For more details / example, see : https://github.com/yeoman/grunt-usemin

How to use RequireJS build profile + r.js in a multi-page project

I am currently learning RequireJS fundamentals and have some questions regarding a build profile, main files, and use of RequireJS with multi-page projects.
My project's directory structure is as follows:
httpdocs_siteroot/
app/
php files...
media/
css/
css files...
js/
libs/
jquery.js
require.js
mustache.js
mains/
main.page1.js
main.page2.js
main.page3.js
plugins/
jquery.plugin1.js
jquery.plugin2.js
jquery.plugin3.js
utils/
util1.js
util2.js
images/
Since this project is not a single-page app, I have a separate main file for each page (although some pages use the same main file).
My questions are:
Is RequireJS even practical for projects that are not single-page?
Without using the optimizer, each of my main files start with essentially the same config options:
requirejs.config({
paths: {
'jquery': 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min'
},
baseUrl: '/media/js/',
// etc...
});
require(['deps'], function() { /* main code */ });
Is there a way to avoid this? Like having each main file include the same build profile without having to actually build it?
Should r.js go in httpdocs_siteroot's parent directory?
Is there something glaringly wrong with my app dir structure or my use of RequireJS?
First of all, this is not a question with a unique solution. I'll explain the way I use RequireJS that works for me, and may work for you :)
Second, English is not my mother language. Corrections and tips about the language will be very appreciated. Feel free, guys :)
1) Is require js even practical for projects that are not single-page?
It depends. If your project does not have shared code between pages for example, RequireJS help will be modest. The main idea of RequireJS is modularize the application into chunks of reusable code. If your application uses only page-specific code, then using RequireJS may not be a good idea.
2) Without using the optimizer, each of my main files start with essentially the same config options. Is there a way to avoid this? Like having each main file include the same build profile without having to actually build it?
The only way I see is making the configuration on the main file, or create a module that will configure RequireJS and then use that module as the first dependency on main.js. But this can be tricky. I do not use many main.js files in my applications; I use only one that acts as a loader (see below).
3) Should r.js go in httpdocs_siteroot's parent directory?
Not necessarily. You can put it inside the /media directory, since all your client stuff is there.
4) Is there something glaringly wrong with my app dir structure or my use of requirejs?
I would not say that. On the other hand, the structure is perhaps a bit too fragmented. For example, you can put all '3rd party stuff' inside a /vendor directory. But this is just sugar; your structure will work well and seems right. I think the major problem is the requirejs.config() call in multiple main files.
I had the same problems you are having now and I ended up with the following solution:
1) Do not wrap the non-AMD-compliant files with a define. Although it works, you can achieve the same results using the "shim" property in requirejs.config (see below).
2) In a multi-page application, the solution for me is not to require the page-specific modules from the optimized main.js file. Instead, I require all the shared code (3rd party and my own) from the main file, leaving the page-specific code to load on each page. The main file ends up only being a loader that starts the page-specific code after loading all shared/lib files.
This is the boilerplate I use to build a multi-page application with requirejs
Directory structure:
/src - I put all the client stuff inside a src directory, so I can run the optimizer inside this directory (this is your media directory).
/src/vendor - Here I place all 3rd party files and plugins, including require.js.
/src/lib - Here I place all my own code that is shared by the entire application or by some pages. In other words, modules that are not page-specific.
/src/page-module-xx - And then, I create one directory for each page that I have. This is not a strict rule.
/src/main.js: This is the only main file for the entire application. It will:
configure RequireJS, including shims
load shared libraries/modules
load the page-specific main module
This is an example of a requirejs.config call:
requirejs.config({
baseUrl: ".",
paths: {
// libraries path
"json": "vendor/json2",
"jquery": "vendor/jquery",
"somejqueryplugion": "vendor/jquery.somejqueryplufin",
"hogan": "vendor/hogan",
// require plugins
"templ": "vendor/require.hogan",
"text": "vendor/require.text"
},
// The shim section allows you to specify
// dependencies between non AMD compliant files.
// For example, "somejqueryplugin" must be loaded after "jquery".
// The 'exports' attribute tells RequireJS what global variable
// it must assign as the module value for each shim.
// For example: By using the configutation below for jquery,
// when you request the "jquery" module, RequireJS will
// give the value of global "$" (this value will be cached, so it is
// ok to modify/delete the global '$' after all plugins are loaded.
shim: {
"jquery": { exports: "$" },
"util": { exports: "_" },
"json": { exports: "JSON" },
"somejqueryplugin": { exports: "$", deps: ["jquery"] }
}
});
And then, after configuration we can make the first require() request
for all those libraries and after that do the request for our "page main" module.
//libs
require([
"templ", //require plugins
"text",
"json", //3rd libraries
"jquery",
"hogan",
"lib/util" // app lib modules
],
function () {
var $ = require("jquery"),
// the start module is defined on the same script tag of data-main.
// example: <script data-main="main.js" data-start="pagemodule/main" src="vendor/require.js"/>
startModuleName = $("script[data-main][data-start]").attr("data-start");
if (startModuleName) {
require([startModuleName], function (startModule) {
$(function(){
var fn = $.isFunction(startModule) ? startModule : startModule.init;
if (fn) { fn(); }
});
});
}
});
As you can see in the body of the require() above, we're expecting another attribute on the require.js script tag. The data-start attribute will hold the name of the module for the current page.
Thus, on the HTML page we must add this extra attribute:
<script data-main="main" data-start="pagemodule/main" src="vendor/require.js"></script>
By doing this, we will end up with an optimized main.js that contains all the files in "/vendor" and "/lib" directories (the shared resources), but not the page-specific scripts/modules, as they are not hard-coded in the main.js as dependencies. The page-specific modules will be loaded separately on each page of the application.
The "page main" module should return a function() that will be executed by the "app main" above.
define(function(require, exports, module) {
var util = require("lib/util");
return function() {
console.log("initializing page xyz module");
};
});
EDIT
Here is example of how you can use build profile to optimize the page-specific modules that have more than one file.
For example, let's say we have the following page module:
/page1/main.js
/page1/dep1.js
/page1/dep2.js
If we do not optimize this module, then the browser will make 3 requests, one for each script.
We can avoid this by instructing r.js to create a package and include these 3 files.
On the "modules" attribute of the build profile:
...
"modules": [
{
name: "main" // this is our main file
},
{
// create a module for page1/main and include in it
// all its dependencies (dep1, dep2...)
name: "page1/main",
// excluding any dependency that is already included on main module
// i.e. all our shared stuff, like jquery and plugins should not
// be included in this module again.
exclude: ["main"]
}
]
By doing this, we create another per-page main file with all its dependencies. But, since we already have a main file that will load all our shared stuff, we don't need to include them again in page1/main module.
The config is a little verbose since you have to do this for each page module where you have more than one script file.
I uploaded the code of the boilerplate in GitHub: https://github.com/mdezem/MultiPageAppBoilerplate.
It is a working boilerplate, just install node and r.js module for node and execute build.cmd (inside the /build directory, otherwise it will fail because it uses relative paths)
I hope I have been clear. Let me know if something sounds strange ;)
Regards!
<script data-main="js/main" src="js/lib/require.js"></script>
// file: js/main
require(['./global.config'], function(){
require(['./view/home'], function() {
// do something
});
});
This is what I used in my project.

Categories

Resources