Objective
Angularjs easy to develop, more features and better results have led to more applications, accompanied by a number of traps. This article lists some common problems of ANGULARJS, let's take a look.
I. MVC directory Structure
Angularjs, frankly, is an MVC framework. Its model does not define as clearly as the backbone.js framework, but its architecture is appropriate. When you work in an MVC framework, it is common practice to categorize it by file type:
templates/
_login.html
_feed.html
app/
app.js
controllers/ Feedcontroller.js
directives/
feedentrydirective.js
services/
loginservice.js
Feedservice.js
filters/
capatalizefilter.js
It seems to be an obvious structure, not to mention the way rails do it. However, once app size starts to expand, this structure can cause you to open a lot of catalogs at once, whether you're using Sublime,visual studio or vim combined with nerd tree, you'll spend a lot of time sliding down the directory trees.
Instead of dividing files by type, we can classify files according to attributes:
app/
app.js
feed/
_feed.html
feedcontroller.js
feedentrydirective.js
login/
_login.html
logincontroller.js
loginservice.js
shared/
capatalizefilter.js
This directory structure makes it easier for us to find all the files associated with a feature, and then speed up our development progress. While it may be controversial to place. html and. js files in one place, the time saved is more valuable.
Second, module
It's very common to put everything in one brain under the main module, but for small apps, there's no problem at first, but soon you'll find out about the pit dad.
var app = Angular.module (' app ', []);
App.service (' MyService ', function () {
//service code
});
App.controller (' Myctrl ', function ($scope, myservice) {
//controller code
});
After this, a common strategy is to categorize objects of the same type.
var services = angular.module (' Services ', []);
Services.service (' MyService ', function () {
//service code
});
var controllers = Angular.module (' Controllers ', [' services ']);
Controllers.controller (' Myctrl ', function ($scope, myservice) {
//controller code
});
var app = Angular.module (' app ', [' Controllers ', ' services ']);
This approach is similar to the directory structure mentioned in the first part of the previous section: not good enough. According to the same idea, you can categorize by attributes, which can lead to scalability.
var sharedservicesmodule = angular.module (' SharedServices ', []);
Sharedservices.service (' NetworkService ', function ($http) {});
var loginmodule = angular.module (' login ', [' sharedservices ']);
Loginmodule.service (' Loginservice ', function (NetworkService) {});
Loginmodule.controller (' Loginctrl ', function ($scope, loginservice) {});
var app = Angular.module (' app ', [' sharedservices ', ' login ']);
When we develop a large application, it is possible that not everything is contained on one page. Placing the same class of features within a module makes it easier to reuse modules across apps.
Iii. Dependency Injection
Dependency injection is one of the best models for ANGULARJS, which makes testing simpler and relies on any given object to be clear. The ANGULARJS injection is very flexible, and the simplest way is to simply pass the dependent name into the modulefunction:
var app = Angular.module (' app ', []);
App.controller (' Mainctrl ', function ($scope, $timeout) {
$timeout (function () {
console.log ($scope);
}, 1000);
};
Here, obviously, Mainctrl dependent$scopeand$timeout.
Everything is fine until you are ready to deploy it to a production environment and want to streamline the code. If you use UGLIFYJS, the previous example becomes the following:
var app=angular.module ("App", []);
App.controller ("Mainctrl", function (e,t) {T (function () {Console.log (e)},1e3)})
Now how does Angularjs know Mainctrl depends on who? ANGULARJS provides a very simple workaround, which will depend on being passed as an array, the last element of the array is a function, and all the dependencies are its arguments.
App.controller (' Mainctrl ', [' $scope ', ' $timeout ', function ($scope, $timeout) {
$timeout (function () {
Console.log ($scope);
}, 1000);
}];
Doing so will streamline the code and ANGULARJS know how to interpret these explicit dependencies:
App.controller ("Mainctrl", ["$scope", "$timeout", function (e,t) {T (function () {Console.log (e)},1e3)}])
3.1 Global Dependencies
When writing a ANGULARJS program, this is often the case when an object has a dependency that binds itself to the global scope, which means that the dependency is available in any ANGULARJS code, but it destroys the dependency injection model and causes problems, Especially in the testing process.
Using ANGULARJS makes it easy to encapsulate these global dependencies into modules, so they can be injected like ANGULARJS standard modules.
Underscrore.js is a great library that simplifies JavaScript code in a functional style, and you can turn it into a module in the following ways:
var underscore = Angular.module (' underscore ', []);
Underscore.factory (' _ ', function () {return
window._;//underscore must already is loaded on the page
});
var app = Angular.module (' app ', [' underscore ']);
App.controller (' Mainctrl ', [' $scope ', ' _ ', function ($scope, _) {
init = function () {
_.keys ($scope);
}
Init ();
}]);
Such practices allow applications to continue to develop in a angularjs-dependent manner, while at the same time exchanging underscore in the testing phase.
This may seem trivial and unnecessary, but if you are using use strict (and must) in your code, that is necessary.
Iv. expansion of the controller
The controller is a angularjs of meat and potatoes, and accidentally adds too much logic to it, especially at first. The controller should never be able to manipulate the DOM or hold the DOM selector, which is where we need to use instructions and Ng-model. Similarly, the business logic should exist in the service rather than the controller.
The data should also be stored in the service, unless they are already bound to the $scope. The service itself is a single example that exists throughout the lifecycle of the application, but the controller is transient across the state of the application. If the data is stored in the controller, it needs to retrieve the data from somewhere when it is instantiated again. Even if the data is stored in localstorage, the retrieval speed is one order of magnitude slower than the JavaScript variable.
ANGULARJS works well when following a single responsibility principle (SRP), and if the controller is the coordinator between the view and the model, it should contain as few logic as possible, which will also facilitate testing.
V. Service vs. Factory
Almost every ANGULARJS developer is bothered by these nouns when they are beginners, which is really not a good thing to do because they are just grammatical sugars for almost the same things!
The following are their definitions in the Angularjs source code:
function factory (name, FACTORYFN) {return
provider (name, {$get: Factoryfn});
}
function service (name, constructor) {return
factory (name, [' $injector ', function ($injector) {return
$ Injector.instantiate (constructor);
}]
As you can see from the source code, the service simply calls thefactoryfunction, and the latter callsproviderthe function. In fact, ANGULARJS also provides additional encapsulation for values, constants, and decorationsprovider, which do not cause similar confusion, and their documentation is very clear.
What's the difference between a service that simply calls afactoryfunction? Clue in$injector.instantiate: In this function,$injectorserviceA new instance is created in the constructor.
Here's an example that shows oneserviceand onefactoryhow to do the same thing:
var app = Angular.module (' app ', []);
App.service (' HelloWorldService ', function () {
This.hello = function () {return
"Hello World";
};}
);
App.factory (' Helloworldfactory ', function () {return
{
hello:function () {return
"Hello World";
}
}
});
WhenhelloWorldServiceorhelloWorldFactoryinjected into a controller, they all have a Hello method that returns "Hello World".serviceconstructor is instantiated at the time of declaration, while the object isfactorypassed every time it is injected, but there is still only onefactoryinstance. All ofprovidersthem are single cases.
If you can do the same thing, why do you need two different styles? As opposed toservice,factoryprovides more flexibility, because it can return functions, these functions can be created after the new. This caters to the concept of factory patterns in object-oriented programming, where a factory can be an object that can create other objects.
App.factory (' Hellofactory ', function () {return
function (name) {
this.name = name;
This.hello = function () {return
"hello" + this.name;
};}
);
Here is a controller example, usingserviceand twofactory,helloFactoryreturns a function that will be set when the new object is createdname.
App.controller (' Helloctrl ', function ($scope, HelloWorldService, Helloworldfactory, hellofactory) {
init = function () {
Helloworldservice.hello ();//' Hello World '
helloworldfactory.hello ();//' Hello World '
New Hellofactory (' Readers '). Hello ()//' Hello Readers '
}
init ();
});
When it comes to beginners, it's best to use only service.
FactoryIt is also useful when designing a class that contains many private methods:
App.factory (' Privatefactory ', function () {
var privatefunc = function (name) {return
name.split (""). Reverse () . Join (""); Reverses the name
};
return {
hello:function (name) {return
"Hello" + privatefunc (name);
}
};
});
With this example, we can makeprivateFactorythe public API inaccessible toprivateFuncmethods, whichservicecan be done in, but easier infactory.
Vi. no use of Batarang
Batarang is an excellent chrome plugin for developing and testing the Angularjs app.
Batarang provides the ability to browse the model, which gives us the ability to see how ANGULARJS internally determines which models are bound to the scope, which is useful for processing instructions and for isolating a range of bound values.
Batarang also provides a dependency graph, which is useful if we are in contact with an untested code base that determines which services should be focused.
Finally, Batarang provides performance analysis. Angular can be used to open the package, good performance, but for a full of custom instructions and complex logic applications, sometimes not so fluent. Using the Batarang Performance tool, you can directly observe which function runs for the longest time in a digest cycle. Performance tools can also show a complete watch tree, which is useful when we have a lot of watcher.
Seven, too much of the watcher
In the last point we mentioned that ANGULARJS can be used to open the package, the performance is good. Because of the need to complete dirty data checks in a digest cycle, this cycle can have significant performance problems once the number of watcher increases to around 2000. (2000 this number cannot be said to cause a significant drop in performance, but this is a good empirical value.) In the Angularjs 1.3 release version, there are already some changes that allow strict control over the digest cycle. )
The following "Executed function expression (iife)" will print out the number of watcher on the current page, and you can simply paste it into the console and observe the results. The Iife comes from Jared's answer on StackOverflow:
(function () {
var root = $ (document.getelementsbytagname (' body '));
var watchers = [];
var f = function (Element) {
if (Element.data (). hasOwnProperty (' $scope ')) {
Angular.foreach (Element.data (). $ scope.$ $watchers, function (watcher) {
watchers.push (watcher);}
);
Angular.foreach (Element.children (), function (childelement) {
f ($ (childelement));
};
f (root);
Console.log (watchers.length);
}) ();
In this way the number of watcher, combined with the Batarang performance Plate watch tree, should be able to see where there is duplication of code, or where there is invariant data with watch.
When there is invariant data, and you want to use Angularjs to template it, you can consider the use of bindonce. Bindonce is a simple instruction that allows you to use the template in Angularjs, but it does not add watch, which guarantees that the number of watch will not grow.
Viii. limit the scope of $scope
JavaScript-based inheritance is subtly different from object-oriented inheritance, which is usually not a problem, but the subtleties$scopeare displayed when used. In Angularjs, each$scopewill inherit the parent$scope, which is called the highest level$rootScope. (unlike$scopetraditional directives, they have a certain scope I and inherit only explicitly declared properties.) )
Because of the characteristics of prototype inheritance, it is not important to share data between parent classes and subclasses, but it is easy to misuse a parent's attributes if you are not careful$scope.
For example, we need to display a user name on a navigation bar that is entered in the login form, and the following should work:
<div ng-controller= "Navctrl" >
<span>{{user}}</span>
<div ng-controller= "Loginctrl" >
<span>{{user}}</span>
<input ng-model= "user" ></input>
</div>
</div>
So here's the problem ... : In text input set the user's Ng-model, when the user entered content, which template will be updated? Navctrl or Loginctrl, or all of them?
If you choose Loginctrl, you may have understood how the prototype inheritance works.
When you retrieve the literal value, the prototype chain does not work. Retrieving the prototype chain is necessary if the Navctrl is also updated, but this can happen if the value is an object. (Remember, in JavaScript, functions, arrays, and objects are objects)
So in order to get the expected behavior, you need to create an object in Navctrl that can be referenced by Loginctrl.
<div ng-controller= "Navctrl" >
<span>{{user.name}}</span>
<div ng-controller= " Loginctrl ">
<span>{{user.name}}</span>
<input ng-model=" User.Name "></input>
</div>
</div>
Now, since user is an object, the prototype chain will work, and the Navctrl template and the$scopeandloginCtrlwill be updated.
This may seem like a very contrived example, but when you use certain instructions to create a child$scope, such asngRepeatwhen, this problem is easily generated.
Nine, manual Test
Since TDD may not be the way each developer would like to develop, they do manual tests when the developer checks whether the code is working or if it affects something else.
It doesn't make sense to not test the Angularjs app. Angularjs's design makes it Conto testable, as evidenced by the dependency injection and Ngmock modules. ANGULARJS Core team has developed a number of tools to make testing a higher level.
9.1 Protractor
Unit testing is the foundation of a test effort, but given the complexity of the app, integration testing is closer to the actual situation. Fortunately, Angularjs's core team has provided the necessary tools.
We have established protractor, an end-to-end tester to simulate user interaction, which can help you verify the health of your ANGULARJS program.
Protractor uses the Jasmine test framework to define tests, and protractor has a very robust API for different page interaction behaviors.
We also have some other end-to-end testing tools, but the advantage of protractor is that it understands how to work with ANGULARJS code, especially in the $digest cycle.
9.2 Karma
Once we have completed the compilation of the integration test with protractor, the next thing is to execute the test. Waiting for test execution, especially integration testing, is a touch of sadness for every developer. Angularjs's core team also felt extremely sore, so they developed karma.
Karma is a tester that helps close the feedback loop. Karma is able to do this because it runs tests when the specified file is changed. Karma also runs tests on multiple browsers, and different devices can point to Karma servers to better cover real-world scenarios.
Ten, using jquery
jquery is a cool library that has standardized cross-platform development and has almost become a necessity for modern web development. But despite all the good features of jquery, the idea is inconsistent with angularjs.
Angularjs is a framework for creating apps, and jquery is a library that simplifies "HTML document manipulation, event handling, animation, and Ajax." This is the basic difference between the two, Angularjs is committed to the architecture of the program, and the HTML page independent.
To better understand how to build a ANGULARJS program, stop using jquery. jquery allows developers to think about problems with existing HTML standards, but as the document says, "ANGULARJS allows you to expand the word HTML in your application."
Dom operations should only be done in instructions, but that does not mean they can only be encapsulated with jquery. Before you use jquery, you should always think about whether this feature is Angularjs already available. It's really powerful to be able to create powerful tools when the instructions are interdependent.
But a very good jquery is a necessity when this day may come, but it is a common mistake to introduce it at the outset.
Summarize
Angularjs is a remarkable framework that has been progressing with the help of the community. Although Angularjs is still an evolving concept, I hope that people will follow these conventions and avoid developing ANGULARJS applications. Hope that the content of this article can be helpful to everyone, if there are questions to communicate, thank you for your support cloud habitat community.