- Directives - HTML annotations that trigger Javascript behaviors
- Modules - Where our application components live
- Controllers - Where we add application behavior
- Expressions - ow values get displayed within the page
Angular snipets:
Application:
app.js:
(function(){
var app = angular.module('gemStore', []);
})();
snipet.html:<!DOCTYPE html>
<html np-app="gemStore">
<head></head>
<body>
...
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
Controller:
app.js:
(function(){
var app = angular.module('gemStore', []);
app.controller('StoreController', function(){});
})();
snipet.html:<div ng-controller="StoreController as store"> ... </div>
Controller fields:
app.js:
(function(){
var app = angular.module('gemStore', []);
app.controller('StoreController', function(){
this.product = {
name: "Diamant",
price: 20,
description: "Very beautiful"
canPurchase: true,
soldOut: true
}
});
})();
snipet.html:<div ng-controller="StoreController as store">
<h1>{{store.product.name}}</h1>
<h2>{{store.product.price}}</h2>
<p>{{store.product.description}}</p>
</div>
Build-in directives:
show:
snipet.html:<div ng-controller="StoreController as store">
<h1>{{store.product.name}}</h1>
<h2>{{store.product.price}}</h2>
<p>{{store.product.description}}</p>
<button ng-show="store.product.canPurchase">Buy</button>
</div>
hide:
snipet.html:<div ng-controller="StoreController as store" ng-hide="store.product.soldOut">
<h1>{{store.product.name}}</h1>
<h2>{{store.product.price}}</h2>
<p>{{store.product.description}}</p>
<button ng-show="store.product.canPurchase">Buy</button>
</div>
repeat:
app.js:...
this.products = [{...},{...}, ...];
...
snipet.html:<div ng-controller="StoreController as store">
<div ng-repeat="product in store.products" ng-hide="store.product.soldOut">
<h1>{{product.name}}</h1>
<h2>{{product.price}}</h2>
<p>{{product.description}}</p>
<button ng-show="product.canPurchase">Buy</button>
</div>
</div>
Filters:
Filters works like pipes in unix
<div>{{ product.price | currency }}</div>
pipe(sign |) sends output into "currency"-filter which formats its into currencyGeneral way of writing:
{{ data* | filter:options*}}date
{{'1388123412323' | date:'MM/dd/yyyy @ h:mma'}} => 12/27/2013 @ 12:50AM
uppercase & lowercase
{{'octogon gem' | uppercase}} => OCTOGON GEM
limitTo
{{My Description | limitTo:8}} => My Descr
<li ng-repeat="product in store.products | limitTo:3">
orderBy
<li ng-repeat="product in store.products | orderBy:'-price'"> -descending price, +ascending price
More directives
using ng-src for images
Problem:Using angularjs expression inside a src attribute causes an error because the browser tries to load image before the Expression evaluates.
that's why:
<img ng-src="{{product.images[0].thumb}}"/>
ng-click, ng-init
Presentation of tabs by Bootstrap:<section>
<ul class="nav nav-pills">
<li> <a href>Description</a> </li>
<li> <a href>Specifications</a> </li>
<li> <a href>Reviews</a> </li>
</ul>
</section>
It's cool in design perspective but to get it alive we should apply next build-in Angularjs directive<section>
<ul class="nav nav-pills">
<li> <a href ng-click="tab = 1">Description</a> </li>
<li> <a href ng-click="tab = 2>Specifications</a> </li>
<li> <a href ng-click="tab = 3">Reviews</a> </li>
</ul>
{{tab}}
</section>
We will se in place of {{tab}} 1, 2 or 3 depending on which tab we clicked last time.When ng-click changes value of tab the...
...{{tab}} expression automatically gets updated!
Expressions define a 2-way Data Binding...
this means Expressions are re-evaluated when a property changes.
More realistic example
<section ng-init="tab = 1">
<ul class="nav nav-pills">
<li> <a href ng-click="tab = 1">Description</a> </li>
<li> <a href ng-click="tab = 2">Specifications</a> </li>
<li> <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>
<div class="panel" ng-show="tab === 2">
<h4>Specifications</h4>
<blockquote>None yet</blockquote>
</div>
<div class="panel" ng-show="tab === 3">
<h4>Reviews</h4>
<blockquote>None yet</blockquote>
</div>
</section>
В описанный выше способ мы конечно же покажем нужный таб по нажатию, но у нас еще остается не
применена верстка активного таба бутстрапа, поэтому нужно придумать как активный таб будет получать
подходящий css класс
для этого применяется директива ng-class="{ CSS-CLASSNAME:EXPRESSION}"
<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>
...
Но если взглянуть на этоу вью, то оно начинает понемного попахивать - у нас куча логики в презентации,
а это не хорошо
Для этого создадим контроллер и вынесем логику в него:
<section ng-controller="PanelController as panel">
<ul class="nav nav-pills">
<li ng-class="{ active:panel.isActive(1)} ">
<a href ng-click="panel.selectTab(1)">Description</a>
</li>
<li ng-class="{ active:panel.isActive(2)} ">
<a href ng-click="panel.selectTab(2)">Specifications</a>
</li>
<li ng-class="{ active:panel.isActive(3)} ">
<a href ng-click="panel.selectTab(3)">Reviews</a>
</li>
</ul>
...
app.js:
app.controller("PanelController", function(){
this.tab = 1; //for replacing of ng-init="tab = 1"
this.selectTab = function(setTab) {
this.tab = setTab;
};
this.isActive = function(tab){
return this.tab == tab;
};
});
Так мы разгрузили нашу вьюху, это не заменимо если бы логика была намного сложнее чем номер таба,
тогда мы ее прячем внутри метода контроллера, который вызываем на вьюхе.
Forms and tow-way binding by ng-model directive:
<form name="reviewForm">
<blockquote>
<b>Starts: {{review.stars}}</b>
{{review.body}}
<cite>by: {{review.author}}</cite>
</blockquote>
<select ng-model="review.stars">
<option value="1">1 start</option>
<option value="2">2 starts</option>
...
</select>
<textarea ng-model="review.body"></textarea>
<label>by:</label>
<input ng-model="review.author" type="email" />
<input type="submit" value="Submit" />
</form>
Directive ng-model binds form element value to the property - in this way we get TWO-WAY BINDING
Previous code already works, but it is better practice to initialize thing obviously,
it can be done by ng-init, but better way use cotrollers;)
And also let's define processing of submission:
<form name="reviewForm" ng-controller="ReviewController as reviewCtrl"
ng-submit="reviewCtrl.addReview(product)">
<blockquote>
<b>Starts: {{reviewCtrl.review.stars}}</b>
{{review.body}}
<cite>by: {{reviewCtrl.review.author}}</cite>
</blockquote>
<select ng-model="reviewCtrl.review.stars">
<option value="1">1 start</option>
<option value="2">2 starts</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>
app.js:
...
app.controller("ReviewController", function(){
this.review = {};
this.addReview = function(product) {
product.reviews.push(this.review); // product.reviews is Array, push is build-in js function of Array
this.review = {}; // let's clean up form after saving new review
};
});
Validation:
Хороший тон это отключать браузерную валидацию $lt;form novalidate$gt;
Важные поля стоит помечать атрибутам required - их проглотит Angular.
Валидационная информация выводится так:
<form name="reviewForm"...>
...
<div> reviewForm is {{reviewForm.$valid}} </div>
<input type="submit" value="Submit" />
</form>
$valid - встроенная проперти у форм AngularjsА теперь рассмотрим как нам не допустить сабмита формы, если поля не прошли валидацию:
<form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewForm.$valid && reviewCtrl.addReview(product)">
Angularjs также имеет свои классы статуса валидации
Вот что находоится в первичном хтмл:
<input name="author" ng-model="reviewCtrl.review.author" type="email" required />
А вот что мы получаем после загрузки на страницу скрипта angularjs
<input name="author" ... class="ng-pristine ng-invalid" />*pristine - девственный
Когда мы ввели что-то, но оно не проходит валидацию для конкретного поля, мы получаем
<input name="author" ... class="ng-dirty ng-invalid" />
Когда же валидация проходит успешно:
<input name="author" ... class="ng-dirty ng-valid" />
HTML5-based type validations
Web forms usually have rules around valid input: * Angular JS has built-in validations for common input types:<input type="email" name="email" /">
<input type="url" name="homepage" /">
<input type="number" name="quantity" /">
Собственные директивы:
Первое что рассмотрим это упорядочивание кода.
Начнем с переиспользования снипета хтмл, для этого есть директива ng-include.
Снипет выносится в отдельный хтмл файл:
product-title.html:
{{product.name}}
<em class="pull-right">${{product.price}}</em>
Чтобы подключить снипет в страницу нужно воспользоваться директивой ng-include="variableWithUrl".
index.html:
... <h3 ng-include="'product-title.html'"></em>
Теперь сделаем тоже самое но с помошью своей директивы + кое-что еще.
Ключевой вопрос - зачем вообще использовать директивы? Фишка в том, что по ним мы можем видеть поведение HTML, структура с директивами может нам расказать что происходит на странице. Директивы какбы выражают поведение HTML.
Кастомные директивы бывают следующих видов:
- Расширяющие шаблон, они самые простые.
- определяем кастомный тег или атрибут, который расширяется или заменяется;
- может влючать логику из контроллера, если необходимо.
- Директивы могут также использоваться для:
- выражения сложного UI;
- вызова событий и регистрации обработчиков событий;
- переиспользования общих компонентов.
КАК СОЗДАТЬ КАСТОМНУЮ ДИРЕКТИВУ:
Хотим чтобы следующий тег заменялся содержимым product-title.html
... <product-title></product-title>
Для этого в модуле создаем директиву:
app.directive('productTitle', function(){
return {
restrict: 'E', //Type of Directive (E for Element)
templateUrl: 'product-title.html'
};
});
Расширяющие шаблон директивы бывают Element Directive и Attribute Directive.Рекомендуется использовать Element Directive для UI widgets,
а Attribute Directive для "вмешивания"(mixin) поведений(например tooltip).
Тоже самое но в виде Attribute Directive:
... <h3 product-title></h3>
app.directive('productTitle', function() {
return {
restrict: 'A',
templateUrl: 'product-title.html'
};
});
Когда мы начинаем писать реальное приложение, то в нем накапливается много кода с директивами в хтмл, и наш ранее читабельный шаблон превращается в месс, именно для этого и выносят отдельные чанки в расширяющие директивы. По факту это нормальный процесс написания кода - сначала мы все создаем в отном шаблоне и по мере решения подзадачи конкретной страницы, выносим их снипеты.
Так у нас в снипете и оказываются теги с атрибутами контроллера Какже с ними быть? А быть с ними просто, контроллер мы реализуем в дерективе и даем ему тамже алиас, а потом в снипете удаляем атрибут ng-controller.
... <h3 product-title></h3>app.js
app.directive('productGallery', function(){
return {
restrict: 'E',
templateUrl: 'product-gallery.html',
controller: function(){
this.current = 0;
this.setCurrent = function(imageNumber){
this.current = imageNumber || 0;
};
},
controllerAs: 'gallery'
};
});
product-gallery.html:
<div ng-show="product.images.length">
<div class="img-wrap">
<img ng-src="{{product.images[gallery.current]}}" />
</div>
<ul class="img-thumbnails clearfix">
<li class="small-image pull-left thumbnail" ng-repeat="image in product.images">
<img ng-src="{{image}}" />
</li>
</ul>
</div>
index.html:
<product-gallery"></product-gallery>
Modules and dependencies:
Лучше всего разбивать приложения на модули вокруг функциональности: - app.js - модуль верхнего уровня, подсоединяющийся через ng-app - products.js - вся функцинальность для продуктов и только продуктов
app.js:
(function(){
var app = angular.module('store', ['store-products']);
app.controller('StoreController', function(){ . . . });
...
})();
products.js:
(function(){
var app = angular.module('store-products', []);
app.directive('productTitle', function(){ . . . });
app.directive('productGallery', function(){ . . . });
app.directive('productPanels', function(){ . . . });
})();
index.html:
<!DOCTYPE hyml>
<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>
Services:
Сервисы предоставляют контроллерам дополнительную функциональность.
Ангулар имеет набор уже предопределенных сервисов, которые помечены знаком $:
- $http для выборки JSON-data из веб-сервиса;
- $log для логирования;
- $filter для фильрования массивов.
$http
1) Использования $http как функции из опциональными параметрами:
$http({method: 'GET', url: '/products.json'});
2) Или можно испльзовать один из сокращенных методов:
$http.get('/products.json', {apiKey: 'myApiKey'});
И первый и второй метод возвращает Promise обьект c методами .success() .error() для подписи на то или другую событие.
Если в ответ прилетает JSON, он автоматически декодируется в массивы/обьекты.
Чтобы контроллер смог восользоваться каким-нибудь сервисом, мы должны заинджектить
такую депернденси в него, это делается при помощи funky array syntax:
(function(){
var app = angular.module('store', ['store-products']);
app.controller('SomeController', ['$http', '$log', function($http, $log){
var store = this;
store.products = [];
$http.get('/products.json').success(function(data){
store.products = data;
});
} ]);
$http({method: 'GET', url: '/products.json'});
})();
Для реализации DI в angularjs существует Injector, который регестрирует указанные
встроенные библиотеки. Когда ангулар регистрирует контроллер, он натыкается на
имена существующих зареганыйх сервисов, которые инджектяться инджектором как параметры функции определяющей поведение контроллера.
Дополнительная функциональность $http:
+ к .get() мы можем заюзать:
$http.post('/path/to/resource.json', {param: 'value'});
$http.delete('/path/to/resource.json');
Другие HTTP методы вызываются черзе обьект config:
$http.({method: 'OPTIONS', url: '/path/to/resource.json'});
$http.({method: 'PATCH', url: '/path/to/resource.json'});
$http.({method: 'TRACE', url: '/path/to/resource.json'});
Комментариев нет:
Отправить комментарий