📜 ⬆️ ⬇️

AngularJS 1.x - translation course from CodeSchool

This publication is a translation of the original CoodSchool course with a few additions that seemed appropriate to me in this context. The publication is designed for those who are just starting to get acquainted with Angular.

Introduction


AngularJS is a popular JavaScript library designed mainly for creating single-page web applications with dynamically updated content. The library was written by Slovak programmer Mishka Heveri, who, working for Google, received a task from his boss to learn JavaScript. He decided that the best way to learn a language is to create its own framework on its basis. Angular (sounds like "Angula" and literally translated as "Angular") uses the concept of data sharing and their presentation, known as MVC (M-model (data), V-view (presentation), C-controller (controller, control layer between they provide the logic of the application).

Different sources describe the purpose of the controller, or so that the controller responds only to user actions and makes changes to the model, and then these changes appear in the interface without him participating. Or so that the controller does not only make changes to the model, but is also responsible for receiving them from the model along with subsequent processing as needed. Angular sticks to the second concept.

Angular does not depend on external libraries and is written in pure JavaScript (this is often referred to as a plus). In this case, as a rule, it is recommended to use it in conjunction with Bootstrap CSS. Also Angular does not limit sharing with jQuery, for example, for visual effects. In principle, any other JS libraries aimed at presentation (design), for example, React from the Facebook team, can be used in conjunction with Angular without much conflict between them.
')

1. We get acquainted with the main entities of the framework: module, controller, directive, expression


The basic structure in Angular is the module. In essence, it is a container for interrelated functions. Therefore, the creation of an application on Angular begins with the creation of a base module in which we indicate the name of the application. First of all, create an app.js file that will contain the following code:

var app = angular.module('AppName', [ ]); 

AppName is the name of our application. It, of course, can be any other to your taste. The square brackets [] indicate dependencies on other modules necessary for the operation of our module. If there are none, simply specify an empty array. More on this later.

Controller [Controler]
Further in the module we define the controller. While he is alone, but there may be many. Simply call each following by a different name.

 app.controller('StoreController', function(){ this.data = {name: 'Gem', //     price : 3.49} //   }); 

Now a few important actions inside the HTML code (which will connect our module with the design). To do this, create an index.html file, in which we first declare that we are using a module with the name chosen by us <html ng-app = "AppName">
Now Angular knows which module to use inside this HTML. Also connect the library itself and the file with our application.

 <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> 


Expression [Expression]
Inside HTML, in curly braces {{}} is indicated a variable. This variable must first be defined in the controller. Also inside {{}} we can specify not only a variable, but also an expression. For example: {{'Price:' + store.data.price + '$'}}.

Please note that expressions cannot be used inside the src attribute of the IMG tag, because the browser tries to load the image before the JS is defined. Therefore, Angular uses the special ng-src directive:

 <img ng-src="{{expression}}"/> 


Directive [Directive]
The directive is a special attribute that is used directly inside HTML tags and is processed by Angular in accordance with the logic specific to the directive. For example, inside the block where we need to display the data, we specify the name of the controller (in this case, StoreController), which will provide us with this very data. This is done using the built-in ng-controller directive.

 <div ng-controller="StoreController as store"> {{store.data.price}} //  3.49 <\div> 

Other commonly used inline directives are:
ng-show - if its value is False hides the HTML element (using display: none). Useful when you need to remove interface elements depending on the state of the model. For example, for goods that are sold automatically remove the Buy button.
ng-hide is the reverse analogue of ng-show. Those. hides an item if its value is True.
ng-repeat is similar to the foreach function. Those. cyclically repeats the HTML tags included in the block. For example: add one more product to the controller:

 app.controller('StoreController', function(){ this.data = [{name: 'Gem', price : 3.49}, {name: 'Emerald', price : 17.99}] }); 

Now, in order to get the prices of both products, use ng-repeat:

 <div ng-controller="StoreController as store"> <div ng-repeat="product in store.data"> <li>{{product.name}}: ${{product.price}} <\div> <\div> 

At the output we get:
Gem: $ 3.49
Emerald: $ 17.99

Angular has many other built-in directives designed to solve various routine tasks.

2. Filters, directives and clean code


Filters are used in expressions and directives and are intended to change the data format.

{{data | filter [: option1] [: option2]}}

 <li>{{product.name}}: {{product.price | currency:"$":0}} // Emerald: $17 ============= = ======== === = | | | | +-     () | | | +-   () | | +-  () | +- pipe (""          ) +-  


Filter examples:

 <b>date</b> {{'1388123412323' | date:'MM/dd/yyyy @ h:mma'}} // 12/27/2013 @ 12:50AM 


 <b>uppercase</b> {{'octagon gem' | uppercase}} // OCTAGON GEM 


 <b>limitTo</b> {{'My Description' | limitTo:8}} // My Descr <li ng-repeat="product in store.products | limitTo:3"> //     


 <b>orderBy</b> <li ng-repeat="product in store.products | orderBy:'-price'"> //    


Full list of built-in Angular filters .

Now a few words about the purity of the code. To do this, use the following example:

 <section ng-init="tab = 1"> <ul class="nav nav-pills"> <li ng-class="{active:tab === 1}"> <a href ng-click="tab = 1">Description</a> </li> <li ng-class="{active:tab === 2}"> <a href ng-click="tab = 2">Specifications</a> </li> <li ng-class="{active:tab === 3}"> <a href ng-click="tab = 3">Reviews</a> </li> </ul> <div class="panel" ng-show="tab === 1"> <h4>Description </h4> <p>{{product.description}}</p> </div> </section> 


As you can see, the logic is directly in HTML, which of course is not correct and should be moved to the controller level. To do this, add the following code to the controller function:

 app.controller("PanelController", function(){ //  this.tab = 1; //    this.selectTab = function(setTab) { this.tab = setTab; }; //      this.isSelected = function(checkTab){ return this.tab === checkTab; }; }); 

The logic is now inside the controller and it remains only to modify HTML accordingly.

 <section ng-controller="PanelController as panel"> <ul class="nav nav-pills"> <li ng-class="{ active: panel.isSelected(1) }"> <a href ng-click="panel.selectTab(1)">Description</a> </li> <li ng-class="{ active: panel.isSelected(2) }"> <a href ng-click="panel.selectTab(2)">Specifications</a> </li> <li ng-class="{ active: panel.isSelected(3) }"> <a href ng-click="panel.selectTab(3)">Reviews</a> </li> </ul> <div class="panel" ng-show="panel.isSelected(1)"> <h4>Description </h4> <p>{{product.description}}</p> </div> </section> 



3. Models and Validators on the example of creating a form


One of the main features of Angular is the so-called two-way binding (Two-way Data Binding), meaning that changing the data on the page (in the form) automatically changes the data in the model (the data storage object), and changing the data in the model also automatically updates the associated information on the page. At first glance it looks a bit confusing, so it’s worth explaining with an example:

 <form name="reviewForm"> <blockquote> <b>Stars: {{review.stars}}</b> {{review.body}} <cite>by: {{review.author}}</cite> </blockquote> <select ng-model="review.stars"> <option value="1">1 star</option> <option value="2">2 stars</option> . . . </select> <textarea ng-model="review.body"></textarea> <label>by:</label> <input ng-model="review.author" type="email" /> <input type="submit" value="Submit" /> </form> 


So, for example, changing data in the selector <select ng-model = "review.stars"> will automatically be displayed on the page using the expression {{review.stars}}

Thus, data from the presentation layer (View) is transmitted to the data storage layer (Model) and back.

It should also be noted that during data transmission of the model using the ng-model directive, data validation occurs, the installation of special classes based on validation results and many other useful events.

Now we will connect the form with the controller, which will ensure its processing. To begin with, we will create the controller itself, containing the addReview method, which should be called when the form is submitted .

 app.controller("ReviewController", function(){ this.review = {}; //     this.addReview = function(product) { product.reviews.push(this.review); //        this.review = {}; //   }; }); 


Then make the necessary adjustments to the form

 <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)"> <blockquote> <b>Stars: {{reviewCtrl.review.stars}}</b> {{reviewCtrl.review.body}} <cite>by: {{reviewCtrl.review.author}}</cite> </blockquote> <select ng-model="reviewCtrl.review.stars"> <option value="1">1 star</option> <option value="2">2 stars</option> . . . </select> <textarea ng-model="reviewCtrl.review.body"></textarea> <label>by:</label> <input ng-model="reviewCtrl.review.author" type="email" /> <input type="submit" value="Submit" /> </form> 


Now add validation. First of all, we mark the form fields as required with the required argument, and also add a condition so that the data is saved only for a valid form. To do this, add the novalidate option to the Form tag, which will turn off the default HTML validation and add the reviewForm. $ Valid condition, due to which the addReview method will be called only on the valid form.

 <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" novalidate ng-submit="reviewForm.$valid && reviewCtrl.addReview(product)"> <blockquote> <b>Stars: {{reviewCtrl.review.stars}}</b> {{reviewCtrl.review.body}} <cite>by: {{reviewCtrl.review.author}}</cite> </blockquote> <select ng-model="reviewCtrl.review.stars" required> <option value="1">1 star</option> <option value="2">2 stars</option> . . . </select> <textarea ng-model="reviewCtrl.review.body" required></textarea> <label>by:</label> <input ng-model="reviewCtrl.review.author" type="email" required/> <div> reviewForm is {{reviewForm.$valid}} </div> <input type="submit" value="Submit" /> </form> 


All fields of a specific type, specified using the type attribute, will be checked for correct filling. At the same time, Angular will add a certain CSS class to the field depending on the state of validation.

So, for example, for an e-mail type, the established classes will change as follows:

.ng-pristine .ng-invalid // initial state (clean, invalid)
.ng-dirty .ng-invalid // invalid e-mail (filled, invalid)
.ng-dirty .ng-valid // valid email (filled, valid)

Accordingly, we can set any given CSS rules for these classes. For example, around the incorrectly filled field will be a red frame, and around the correctly filled green.

 .ng-dirty.ng-invalid { border-color: #FA787E; //   } .ng-dirty.ng-valid { border-color: #78FA89; //   } 


The following validators are currently supported for the Input field:


4. Creating your own directives


One of the great features of Angular is the ability to create your own directives. In other words, you can create your own HTML tags, which can have their own logic and replace whole pieces of HTML. You can, for example, create the <editor> tag, inserting which we will get on the page a text editor.

This allows you to write expressive code that completely conveys the structure of our application. And of course, it allows you to greatly improve code reuse.

In principle, with regard to the reuse of fragments of HTML code, Angular has at its disposal the ng-include directive, which allows you to transfer part of the code into a separate file. It looks like this:

 <h3 ng-include="'product-title.html'"></h3> 


Note the single quotes around the name. They indicate to Angular that it is a string with a name, and not a variable containing the file name.

Accordingly, in the product-title.html file will be the code that Angular inserts as the content of the H3 tag. For example:

 {{product.name}} <em class="pull-right">${{product.price}}</em> 


As you can see, the code inserted in this way may contain expressions that will be processed after inserting such a fragment into the common code.

But back to creating your own directives. This is done as follows:

 app.directive('productTitle', function(){ return { restrict: 'E', // E -      Element templateUrl: 'product-title.html' }; }); 

Now we can use this as an HTML tag. Notice that the name of the directive productTitle in HTML is transformed into product-title.

 <product-title></product-title> 

Another way to customize a tag is to create your own attribute. In this case, instead of E (Element), when creating a directive, we need to specify A (Attribute) for the restrict property. After that, the HTML tag will look like this:

 <h3 product-title></h3> 


It is recommended to use “restrict: E” to insert widgets and other completely independent elements containing their own logic, and “restrict: A” for impurities (mix-in).

In the case of a widget, we cannot do without a controller, which will contain all the necessary logic. Angular allows you to do this in several ways. First of all, you can simply add the ng-controller attribute:

<product-panels ng-controller = "PanelController as panels">

But it would be more appropriate to use the controller property inside a function that creates our directive:

 app.directive('productPanels', function(){ return { restrict: 'E', templateUrl: 'product-panels.html', controller: function(){ ... //  }, controllerAs: 'panels' //   }; }); 


5. Dependencies and Services (Dependencies and Services)


Dependencies are the modules that are necessary to ensure the functionality of the new module we create. They can also be considered as a way to improve the structure of the application by distributing independent parts of logic between different modules. Consider the following example:

 //  app.js (function(){ var app = angular.module('store', []); app.controller('StoreController', function(){ . . . }); app.directive('productTitle', function(){ . . . }); app.directive('productGallery', function(){ . . . }); app.directive('productPanels', function(){ . . . }); })(); 


It would be logical to put the directives in a separate module or even for each to create your own module, depending on the necessary flexibility.

But in this case, in order not to overly complicate the structure of the application, we will render their common module.

 //  products.js (function(){ var app = angular.module('store-products', []); app.directive('productTitle', function(){ . . . }); app.directive('productGallery', function(){ . . . }); app.directive('productPanels', function(){ . . . }); })(); 

Now we specify the store-products module as a dependency for the store module:

 //  app.js (function(){ var app = angular.module('store', [store-products]); app.controller('StoreController', function(){ . . . }); })(); 

The last step is to remember to connect our new file:

 //  index.html <!DOCTYPE html> <html ng-app="store"> <head> . . . </head> <body ng-controller="StoreController as store"> . . . <script src="angular.js"></script> <script src="app.js"></script> <script src="products.js"></script> </body> </html> 


As a result, we have divided the modules based on functionality.

app.js - contains the top-level module that is connected using the ng-app directive;
products.js - contains all the functionality for products and products only.

Also, as dependencies, we can specify built-in Angular services that provide various standard functionality. Embedded service names begin with $.

As an example of the most popular services we can cite the following:

$ http - communication with the server via XMLHttpRequest;
$ log - logging messages in the browser console;
$ filter - filtering data in an array;
... full list .

An example of the inclusion of dependencies (dependency injection) in the controller.

 app.controller('SomeController', [ '$http', '$log', function($http, $log){ var store = this; store.products = []; $http({ method: 'GET', url: '/products.json' }).success(function(data, status, headers, config) { $log.info(status); store.products = data; }) } ]); 

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


All Articles