📜 ⬆️ ⬇️

Best ways to use Angular.js

From the translator:


Hi, Habr! We continue to share with the community useful materials about the design and development. This time, the TrackDuck team prepared a translation of the article about Angular by Jeff Dickey, which we really liked and in due time made me look closer at Gulp. This article will be useful for developers who want to save time on routine operations and build quality processes when developing web applications. We actively use Angular to develop our own product for visual commenting on websites , so we are ready to answer in the comments questions that interest you!




I used Angular in quite a large number of applications and saw many ways of structuring applications using this framework. Now I am writing a book on designing Angular applications using the MEAN stack, and most of the research I have done in this direction. As a result, I stopped at a rather original structure of the application. I think my approach is simpler than the one proposed by Burke Holland .
Before I begin, I would like to talk about the existing approach to the implementation of modularity in Angular.
')

What is a module in JavaScript


JavaScript applications by default do not have the ability to load modules. First, let's understand what a “Module” is, since everyone can understand this in their own way.
The module allows developers to divide the application into logical parts. Also in JavaScript, the division of an application into modules allows you to avoid conflicts of global variables.

Newbies to JavaScript may be surprised by the fact that so much attention is paid to modules. I want to immediately note one thing - the main purpose of the modules is NOT the organization of the delayed (lazy-loading) loading of application components. Require.js allows you to implement this, and this is not the main thing in modules.

So, as I already noted, JS does not support modules, so over time, several ways to solve this problem have appeared. You can skip the next section if you are already familiar with the organization of modularity in JS.

.noConflict ()


I would like to illustrate the problem: let's say you want to include jQuery in your project. jQuery must define a global variable '$'. If your code already uses a variable with the same name, naturally, you will get a variable conflict. Usually this problem is solved using the .noConflict () function. Simply put, .noConflict () allows you to change the name of a variable that you use for a library.

<script> var $ = 'myobject that jquery will conflict with' </script> <script src='//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script> <script> // $ is jQuery since we added that script tag var jq = jQuery.noConflict(); // $ is back to 'myobject that jquery will conflict with' // jq is now jQuery </script> 


This is quite common, but not the most convenient solution for most JavaScript libraries. It does not provide good code separation - you have to declare modules before using them, and in fact this approach forces you to use a method similar to .noConflict ()

If you still have questions about the operation of the .noConflict function, follow the link and read this useful material .

There are alternative solutions to the problem, and I would like to tell you about 4 main areas:


Each of them has its own specifics, pros and cons. You can also use several of these solutions at the same time (for example, Burke Holland uses two). Let's look at each of the solutions.

Sample App


Let's create a small application on Angular and try to figure it out with a real example.
Here is a link to the GitHub repository with ready-made code . For convenience, let's start working with JavaScript in a single app.js file:
 var app = angular.module('app', []) app.factory('GithubSvc', function ($http) { return { fetchStories: function () { return $http.get('https://api.github.com/users') } } }) app.controller('GithubCtrl', function ($scope, GithubSvc) { GithubSvc.fetchStories().success(function (users) { $scope.users = users }) }) 


Define the object 'app' - this is our module. Next, we define the 'GithubSvc' service with a single function that will help us get GitHub users.
Finally, we define a controller that will use the service to load users into an array located in $ scope. Here is the template with which we will display the list.

Splitting into separate files


The problem is that now our code is in one file, which is completely inapplicable for a real application. From the very beginning of my acquaintance with Angular, every time I saw code samples, I wanted to see a more realistic code with the division into files, which is used in real life.

In our example, I would suggest this structure:


If your application is small, use an alternative way to split files:


In any case, without using a module like browserify or require.js, we must include each of these files using the script tag. It is important to understand that your code can easily grow to hundreds of files and it will be just inconvenient to manage their connection in this way.

When a large number of files are connected, there is also a performance problem - the browser, when loading the application, should simultaneously establish many connections to the server. With a slow internet connection, your users will encounter problems and long download times.

We need a way that allows the developer to connect a large number of js files, and they should not be loaded into the browser at the same time (without connecting via the script tag)

That is why people use the require.js or browserify module loaders. Angular allows you to logically break code, but not files. I want to show a simpler solution, but first, let's look at the well-known ways of managing the loading of modules.

Require.js - very difficult


Require.js was the first attempt to use modules in javascript. The solution allows you to define dependencies directly inside the js file, runs in a browser and is able to load modules as needed.

Thus, this library allows us to achieve two goals at once - to load modules from js and determine the order of loading.

Unfortunately, this library is very difficult to connect and configure. It requires code written in a certain way and has a very high entry threshold for the programmer. In addition, it does not allow enough well handle cyclic dependencies. This can lead to problems when you try to build a modular solution for your Angular application using this library.

More details about require.js can be found in this review .

The ability to require.js to load modules on demand does not work for Angular. Although in reality I have never worked with a project that had this need. In addition, I would like to emphasize once again that this functionality is not vital for Angular developers. It is much more important to be able to properly divide your code into logical parts.

Browserify — A Good Module Loader


Unlike require.js, which uses a browser to load modules, browserify processes your code on the server before it starts executing in the browser. You can not take a browserify file and run it in a browser. First you need to create a 'package'.

Packages use a format similar to that of node.js modules (moreover, they are almost always compatible). It looks like this:
 var moduleA = require('my-module') var moduleB = require('your-module') moduleA.doSomething(moduleB) 


It really looks more comfortable and readable. You simply declare a variable and require () for the module to which it belongs. Writing code to export a module is easy.

In Node, this works well because all the plug-ins are in the file system and take almost no time to load.
So with the help of browserify, you can run the code and it will collect all the files in one package, which can be fully used in the browser. Read more about browserify in this article .

This is a great tool that you can use in your project that does not use Angular. For Angular, I offer you an even simpler solution.

Angular Dependency Injection - solves most of our problems.


Go back and look at app.js. I would like to note this point:
it doesn't matter in which order we create services and controllers. Angular will handle all dependencies for us with the help of internal mechanisms.

When using this method to work with the 'app' object, we must be sure that the module is declared at the beginning of the program. This is the only place where the order of declarations in Angular matters, since I want to merge all the JavaScript files.

Gulp concat


I will use Gulp to merge JS files. Do not worry about the need to learn a new tool, I will use it in a very simple way and you can easily port it to Grunt, Make or the utility you are used to. You just need something that will help you automate the merging of files.

I tried all the modern build systems and Gulp is undoubtedly my favorite. When it comes to working with JS or CSS, it works great.

You might think that I just want to replace one build tool (browserify) with another (Gulp), and you will be right. But, Gulp can solve a wider range of tasks. You can configure and use Gulp for such tasks as minifying, precompiling CoffeeScript, generating sourcemaps, image processing, compiling scss or sass, starting a dev server on node.js. All this will provide us with a platform for further expansion of our project.

First I install Gulp and Gulp-concat
 $ npm install --global gulp $ npm install --save-dev gulp gulp-concat 


You will need to create a package.json file in your project and install node.js. Here is a small trick to help you initiate a new project:
 $ echo '{}' > package.json 


Next, create a gulpfile.js file:
 var gulp = require('gulp') var concat = require('gulp-concat') gulp.task('js', function () { gulp.src(['src/**/module.js', 'src/**/*.js']) .pipe(concat('app.js')) .pipe(gulp.dest('.')) }) 


This is a simple task that combines javascript files from the / src directory into the app.js file. JS files are listed in this order is not surprising - it is with such an entry files *. module.js will be included first. I will tell about it when we take a closer look at minification.

If you want to try experimenting with concatenation in Gulp, download these files and run the command 'gulp js' to run. More information about Gulp can be found by reading my article .

Gulp watch


It is very simple and the code speaks for itself:
 var gulp = require('gulp') var concat = require('gulp-concat') gulp.task('js', function () { gulp.src(['src/**/module.js', 'src/**/*.js']) .pipe(concat('app.js')) .pipe(gulp.dest('.')) }) gulp.task('watch', ['js'], function () { gulp.watch('src/**/*.js', ['js']) }) 


Here we just add a new task 'gulp watch' which will perform the task 'js' every time when any of the javascript files in 'src / ** / *. Js' is changed. Woo-la, we got rid of the routine!

Minification


It's time to start minifying our code. In Gulp, we create a stream for source files that need to be processed with several modules (Minimize, Concatenation, etc.) and then save them.

Let's start with gulp-uglify. First you need to install it using npm:
 npm install -D gulp-uglify 


Now we add our Gulp file:
 var gulp = require('gulp') var concat = require('gulp-concat') var uglify = require('gulp-uglify') gulp.task('js', function () { gulp.src(['src/**/module.js', 'src/**/*.js']) .pipe(concat('app.js')) .pipe(uglify()) .pipe(gulp.dest('.')) }) 


But we have a problem. Gulp-uglify, recognizing the argument names of the Angular function, must inject dependencies. Now our application is not working. If you are not familiar with this problem, read. docs.angularjs.org/guide/di

We can use the cumbersome syntax of arrays in code or a great tool: ng-gulp-annotate

Install it:
 npm install -D gulp-ng-annotate 


And add our gulpfile:
 var gulp = require('gulp') var concat = require('gulp-concat') var uglify = require('gulp-uglify') var ngAnnotate = require('gulp-ng-annotate') gulp.task('js', function () { gulp.src(['src/**/module.js', 'src/**/*.js']) .pipe(concat('app.js')) .pipe(ngAnnotate()) .pipe(uglify()) .pipe(gulp.dest('.')) }) 

I hope you start to see the value in Gulp!

Sourcemaps


Almost everyone uses debugging to diagnose problems with code. The problem is that minified JavaScript cannot be debugged. For this, code maps (sourcemaps) are used. Here is a Gulp plugin that will help generate them:

Install gulp-sourcemaps:
 npm install -D gulp-sourcemaps 


And add the appropriate notation in the gulpfile:
 var gulp = require('gulp') var concat = require('gulp-concat') var sourcemaps = require('gulp-sourcemaps') var uglify = require('gulp-uglify') var ngAnnotate = require('gulp-ng-annotate') gulp.task('js', function () { gulp.src(['src/**/module.js', 'src/**/*.js']) .pipe(sourcemaps.init()) .pipe(concat('app.js')) .pipe(ngAnnotate()) .pipe(uglify()) .pipe(sourcemaps.write()) .pipe(gulp.dest('.')) }) 


Why concatenation is better


Because, first of all it is easier. Angular handles all the download code for us, we just have to help with the files.
It is also great because when creating new files, it’s enough to add them to the directory with existing ones. No additional declaration of these files anywhere, as, for example, in browserify, is required. No dependencies that were in require.js
Actually, the fewer places where we can make a mistake, the fewer errors there will be.

Summary


Here is the final code . This can easily become the starting point for building your Angular application.


I tried to make the description as universal as possible. As I mentioned earlier, it is not only realizable with Gulp. Repeat the same with any collector that supports JS concatenation.

Looking to the future: Angular 2.0 and ES6


It is expected that the next version of JavaScript will solve the problem of modules at the native level. And when ES6 gets widespread support - of course this article will become archaic, but nevertheless, I hope for some time I helped you.

It is worth noting that Angular 2.0 is planning to support ES6 modules, and this is great. I think it will be like a set of packages from which you can easily compose the necessary functionality for your application. But while version 2.0, unfortunately is also far from release.
Angular 2.0 will use a separate di.js library, which will be responsible for the modules. We can easily use it in all applications on Angular. The only problem is that before the release of ES6 and A2.0 you will have to deal with the current implementation of the modules, with which, I hope, my article will help you.

Source: https://habr.com/ru/post/230257/


All Articles