templates/ _login.html _feed.html app/ app.js controllers/ LoginController.js FeedController.js directives/ FeedEntryDirective.js services/ LoginService.js FeedService.js filters/ CapatalizeFilter.js
This approach is common, especially among developers who have experience in RoR development. However, as the application grows, using a similar folder structure results in having to keep several folders open at any given time. Whatever you use - Sublime, Visual Studio or Vim with NerdTree - you will constantly spend time scrolling through the directory tree. To avoid this, you can group files by functionality, rather than by type: app/ app.js Feed/ _feed.html FeedController.js FeedEntryDirective.js FeedService.js Login/ _login.html LoginController.js LoginService.js Shared/ CapatalizeFilter.js
This folder structure makes it much easier to search for related files belonging to the same feature, which can speed up the development process. Yes, it may seem controversial - to store in the same folder html files with js, but the effect of saving time may be more important. var app = angular.module('app',[]); app.service('MyService', function(){ //service code }); app.controller('MyCtrl', function($scope, MyService){ //controller code });
The next most common approach is to group objects by their 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 does not scale well either, like the directory structure of clause 1. In order to achieve better scalability, we follow the same concept of breaking the code into features: 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']);
Spreading functionality across modules also allows code reuse across projects. var app = angular.module('app',[]); app.controller('MainCtrl', function($scope, $timeout){ $timeout(function(){ console.log($scope); }, 1000); });
From the code it is clear that MainCtrl depends on $ scope and $ timeout. This works great until the project goes into production and you want to minimize your code. Using UglifyJS to the above code will result in the following: var app=angular.module("app",[]);app.controller("MainCtrl",function(e,t){t(function(){console.log(e)},1e3)})
Now AngularJS doesn’t know what MainCtrl really depends on. To prevent this from happening, there is a very simple solution - transfer dependencies as an array of strings, with the last element as a function that accepts all the dependencies listed as parameters: app.controller('MainCtrl', ['$scope', '$timeout', function($scope, $timeout){ $timeout(function(){ console.log($scope); }, 1000); }]);
The code above will be converted by minifiers into code that AngularJS can already correctly interpret: app.controller("MainCtrl",["$scope","$timeout",function(e,t){t(function(){console.log(e)},1e3)}])
var underscore = angular.module('underscore', []); underscore.factory('_', function() { return window._; //Underscore must already be loaded on the page }); var app = angular.module('app', ['underscore']); app.controller('MainCtrl', ['$scope', '_', function($scope, _) { init = function() { _.keys($scope); } init(); }]);
This allows the application to use a single style with mandatory dependency injection and leaves the opportunity to test modules in isolation from the functionality of their dependencies. function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); }
The service function simply calls the factory function, which wraps the call to the provider function. If service simply calls the factory function, what is the difference between them? The meaning is in $ injector.instantiate, within which $ injector creates a new instance of the service constructor function. An example of a service and a factory performing the same actions: 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"; } } });
The moment helloWorldService or helloWorldFactory is injected into the controller, both of them will have a single method that returns “Hello World”. Since all providers are singletons, we will always have only one instance of the service and one instance of the factory. So why are there both factories and services at the same time, if they perform the same function? Factories offer more flexibility because they can return a function that can create new objects. In OOP, a factory is an object that creates other objects: app.factory('helloFactory', function() { return function(name) { this.name = name; this.hello = function() { return "Hello " + this.name; }; }; });
Here is an example of a controller using a service and two factories: 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(); });
Factories can also be useful when developing classes with 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); } }; });
(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); })();
Using the code above and the observer tree of the battery, you can see if you have duplicate observers or observers on immutable data. In the case of immutable data, you can use the bindonce directive so as not to increase the number of observers per page. <div ng-controller="navCtrl"> <span>{{user}}</span> <div ng-controller="loginCtrl"> <span>{{user}}</span> <input ng-model="user"></input> </div> </div>
Now the question is: when the user enters his name in the text field, in which elements it will be displayed: navCtrl, loginCtrl, or both? If your answer is loginCtrl, you understand how prototype-based inheritance works. In the search for string fields, a chain of prototypes is not used. To achieve the desired behavior, we want to use the object to correctly update the user name in the child and parent $ scope. (Remember, in JS, functions and arrays are also objects.) <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 the user variable is an object, the prototype chain will work and the span element in navCtrl will be updated correctly along with loginCtrl. This may look like an unnatural example, but when working with directives that create child scopes (like ngRepeat), such moments will arise.Source: https://habr.com/ru/post/253103/
All Articles