В данном уроке мы с вами разберем основы работы с Angular 1.5.8: работу шаблонизатором, объектом $scope, циклом ng-repeat, директивой ng-click.

Шаблонизатор Angular

В Angular встроен специальный шаблонизатор, который позволяет выводить значения переменных в нужное место внутри HTML кода. Для этого переменную следует взять в скобки {{ }} и вставить эту конструкцию в нужное место HTML кода. В это место и выведется значение этой переменной.

К примеру, пусть у нас есть переменная message, внутри которой хранится сообщение пользователя (пусть там хранится текст 'Привет!'). В этом случае наша верстка будет выглядеть примерно так:

<body ng-controller="MyController">
	<p>{{message}}</p>
</body>

А после выполнения яваскрипта мы увидим следующее - вместо {{message}} вставится ее значение ('Привет!'):

<body ng-controller="MyController">
	<p>Привет!</p>
</body>

Объект $scope

Давайте теперь поговорим о том, откуда возьмется эта переменная message. На самом деле нельзя вывести значение любой переменной. Для вывода доступна только одна глобальная переменная $scope. Эта переменная является объектом и выводить можно только свойства этого объекта.

К примеру, если в HTML коде у нас доступна переменная {{message}}, то в контроллере она должна быть задана как $scope.message - то есть быть свойством объекта $scope.

Давайте сделаем это - в контроллере зададим $scope.message, в HTML коде укажем {{message}} и после запуска этой страницы в браузере вместо {{message}} будет стоять заданное значение 'Привет!':

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.message = 'Привет!';
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<p>{{message}}</p>
	</body>
</html>

Скопируйте этот код себе, запустите и в результате вы получите следующее:

<body ng-controller="MyController">
	<p>Привет!</p>
</body>

Объекты

В переменную $scope можно сохранять не только обычные строки и числа, но также массивы и объекты. Для примера сохраним в $scope.user объект {name: 'Иван', age: 25}:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.user = {name: 'Иван', age: 25};
			});
		</script>
	</head>
	<body ng-controller="MyController">

	</body>
</html>

В этом случае в HTML коде будет доступна переменная {{user}}, которая будет является объектом с ключами name и age. Если мы захотим вывести имя пользователя - то это нужно будет сделать так {{user.name}} или {{user['name']}}, а если возраст - вот так - {{user.age}} или {{user['age']}} (в общем-то обычная работа с объектами в JavaScript).

Давайте запустим следующий код в браузере и посмотрим, что получится:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.user = {name: 'Иван', age: 25};
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<p>Имя: {{user.name}}, возраст: {{user.age}}</p>
	</body>
</html>

Скопируйте этот код себе, запустите и в результате вы получите следующее:

<body ng-controller="MyController">
	<p>Имя: Иван, возраст: 25</p>
</body>

Цикл

Пусть у нас в переменной $scope.users хранится массив пользователей:

myApp.controller('MyController', function MyController($scope) {
	$scope.users = ['Коля', 'Вася', 'Петя'];
});

Давайте выведем этих пользователей в виде списка <ul>, в котором каждое из имен будет лежать в своем теге <li>.

Angular предоставляет нам возможность написать только одну <li> и запустить цикл, который переберет все элементы массива $scope.users и сделает столько <li>, сколько элементов в нашем массиве. При этом в каждой <li> окажется свое имя.

Это делается следующим образом: делается <li> и в нее добавляется атрибут ng-repeat, который собственно и запускает цикл. Этот атрибут не входит в HTML, а является командой Angular.

Значением атрибута указывается такая конструкция: элемент in массив, где вместо 'массив' пишется название массива, который мы перебираем (в нашем случае users, так как массив лежит в $scope.users), а вместо 'элемент' пишется любая выдуманная вами переменная (например, мы можем задать ее как user - единственное число от users, но можем и test, и value - любой ваш каприз). В эту переменную будут попадать элементы массива (в нашем случае сначала 'Коля', потом 'Вася', потом 'Петя').

К примеру, если написать ng-repeat="user in users" то внутри нашей <li>, будет доступна переменная {{user}}, в которую будут попадать имена пользователей.

Итак, если запустить такой цикл (с учетом того, что в $scope.users лежит ['Коля', 'Вася', 'Петя']):

<body ng-controller="MyController">
	<ul>
		<li ng-repeat="user in users">{{user}}</li>
	</ul>
</body>

То на выходе мы получим следующий HTML код:

<body ng-controller="MyController">
	<ul>
		<li>Коля</li>
		<li>Вася</li>
		<li>Петя</li>
	</ul>
</body>

Давайте соберем все вместе:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.users = ['Коля', 'Вася', 'Петя'];
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<ul>
			<li ng-repeat="user in users">{{user}}</li>
		</ul>
	</body>
</html>

Скопируйте этот код себе, запустите и в результате вы получите следующее:

<body ng-controller="MyController">
	<ul>
		<li>Коля</li>
		<li>Вася</li>
		<li>Петя</li>
	</ul>
</body>

Цикл и объект

Циклом можно перебирать не только массивы, но и более сложные объекты, например такой:

$scope.users = [
	{name: 'Коля', age: 25},
	{name: 'Вася', age: 26},
	{name: 'Петя', age: 27},
];

В этом случае переменная {{user}} будет не строкой, а объектом с ключами name и age. И соответственно к имени можно будет получить доступ так - {{user.name}}, а к возрасту - так - {{user.age}}.

Давайте попробуем:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.users = [
					{name: 'Коля', age: 25},
					{name: 'Вася', age: 26},
					{name: 'Петя', age: 27},
				];
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<ul>
			<li ng-repeat="user in users">Имя: {{user.name}}, возраст: {{user.age}}</li>
		</ul>
	</body>
</html>

Скопируйте этот код себе, запустите и в результате вы получите следующее:

<body ng-controller="MyController">
	<ul>
		<li>Имя: Коля, возраст: 25</li>
		<li>Имя: Вася, возраст: 26</li>
		<li>Имя: Петя, возраст: 27</li>
	</ul>
</body>

Обращение к ключам

Как известно, в массиве могут быть не только значения, но и ключи. Чтобы обратиться к ключам внутри цикла ng-repeat следует использовать следующий синтаксис: ng-repeat="(ключ, элемент) in массив". Значения 'элемент' и 'массив' понятно что (если не понятно см. предыдущий пункт), а в вместо значения 'ключ' следует написать переменную, в которую будут ложится ключи.

Давайте посмотрим на реальном примере:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.users = {
					Коля: '300$',
					Вася: '400$',
					Петя: '500$',
				};
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<ul>
			<li ng-repeat="(name, salary) in users">Имя: {{name}}, зарплата: {{salary}}</li>
		</ul>
	</body>
</html>

Скопируйте этот код себе, запустите и в результате вы получите следующее:

<body ng-controller="MyController">
	<ul>
		<li>Имя: Коля, зарплата: 300$</li>
		<li>Имя: Вася, зарплата: 400$</li>
		<li>Имя: Петя, зарплата: 500$</li>
	</ul>
</body>

События

Давайте теперь добавим динамики нашей странице. Пусть у нас есть переменная {{message}}, однако, изначально она пустая, а текст в ней появится только по нажатию на кнопку.

Для этого нам надо научится привязывать события к элементам.

Пока мы изучим только одно событие - клик на элемент. Это событие привязывается к элементу с помощью атрибута ng-click. В этот атрибут пишется имя функции с круглыми скобками на конце.

К примеру, если сделать так: ng-click="show()", то по клику на элемент запустится функция $scope.show(). То есть так же, как и при работе с переменными, все сводится к объекту $scope: переменные - его свойства, а функции - его методы.

Давайте по нажатию на кнопку выведем алертом некоторое сообщение:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.show = function() {
					alert('Нажатие по кнопке!');
				}
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<button ng-click="show()">Вывести сообщение</button>
	</body>
</html>

Если запустить этот код (сделайте это) и нажать на кнопку - мы увидим алерт с текстом 'Нажатие по кнопке!'. Отлично, теперь осталось

Теперь осталось сделать так, чтобы вместо алерта менялось сообщение в абзаце. Для этого внутри функции $scope.show поменяем переменную $scope.message на нужное нам значение:

myApp.controller('MyController', function MyController($scope) {
	$scope.show = function() {
		$scope.message = 'Привет!';
	}
});

А также добавим абзац с {{message}}, в которое будет выводиться наше сообщение:

<body ng-controller="MyController">
	<button ng-click="show()">Вывести сообщение</button>
	<p>Ваше сообщение: {{message}}</p>
</body>

Окончательный вариант будет выглядеть так:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.show = function() {
					$scope.message = 'Привет!';
				}
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<button ng-click="show()">Вывести сообщение</button>
		<p>Ваше сообщение: {{message}}</p>
	</body>
</html>

Если после запуска этого кода нажать на кнопку, то вместо {{message}} вставится заданный нами текст (а по заходу на страницу вместо message вставится пустота):

<body ng-controller="MyController">
	<button>Вывести сообщение</button>
	<p>Ваше сообщение: Привет!</p>
</body>

Вот пример того, что у нас должно получиться:

Вы можете открыть этот пример в отдельной вкладке браузера.

Изменение на лету

Angular устроен внутри замечательным образом: если у нас есть, к примеру, переменная $scope.message и соответствующая переменная {{message}}, то они связаны друг с другом постоянно в процессе работы скрипта. Это значит, что при любом изменении $scope.message переменная {{message}} будет принимать новое значение.

Рассмотрим следующий скрипт - по загрузке страницы переменная {{message}} будет иметь значение 'Привет!' (такое же, как и в $scope.message), а по нажатию на кнопку выполнится функция $scope.show и переменная $scope.message сменит значение на 'Пока!' и то же самое сделает {{message}}:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.message = 'Привет!';

				$scope.show = function() {
					$scope.message = 'Пока!';
				}
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<button ng-click="show()">Изменить сообщение</button>
		<p>{{message}}</p>
	</body>
</html>

Если запустить приведенный выше код, то его результатом станет:

<body ng-controller="MyController">
	<button>Изменить сообщение</button>
	<p>Привет!</p>
</body>

Если после запуска этого кода нажать на кнопку, то текст абзаца сменится на 'Пока!':

<body ng-controller="MyController">
	<a href="#">Изменить сообщение</a>
	<p>Пока!</p>
</body>

Вот пример того, что у нас должно получиться:

Вы можете открыть этот пример в отдельной вкладке браузера.

Изменение массива на лету

Самое интересное то, что можно изменять не только строки и числа, но и массивы. Если массив перебирается циклом ng-repeat, то при любых изменениях этого массива мы увидим автоматическое изменение HTML кода.

В следующем примере с помощью ng-repeat выводится список пользователей. По нажатию на кнопку нулевой элемент массива $scope.users поменяется с 'Коля' на 'Иван' и то же самое произойдет и в HTML коде:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.users = ['Коля', 'Вася', 'Петя'];

				$scope.changeUser = function() {
					$scope.users[0] = 'Иван';
				}
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<button ng-click="changeUser()">Изменить пользователя</button>
		<ul>
			<li ng-repeat="user in users">{{user}}</li>
		</ul>
	</body>
</html>

Если запустить приведенный выше код, то по его загрузке вы увидите следующее:

<body>
	<button>Изменить пользователя</button>
	<ul>
		<li>Коля</li>
		<li>Вася</li>
		<li>Петя</li>
	</ul>
</body>

А если нажать на кнопку - то первый пользователь сменит свое имя:

<body>
	<button>Изменить пользователя</button>
	<ul>
		<li>Иван</li>
		<li>Вася</li>
		<li>Петя</li>
	</ul>
</body>

Вот пример того, что у нас должно получиться:

Вы можете открыть этот пример в отдельной вкладке браузера.

Добавление или удаление элементов на лету

Из предыдущего пункта возможно не совсем ясно, что даже при удалении одного из элементов или добавлении нового элемента в массив, мы увидим изменение HTML кода.

Давайте попробуем это на практике - сделаем так, чтобы при нажатии на кнопку, в конец массива добавлялся новый элемент:

<!DOCTYPE html>
<html ng-app="myApp">
	<head>
		<meta charset="utf-8">
		<script src="angular.js"></script>
		<script>
			var myApp = angular.module('myApp', []);

			myApp.controller('MyController', function MyController($scope) {
				$scope.users = ['Коля', 'Вася', 'Петя'];

				$scope.addUser = function() {
					$scope.users.push('Иван');
				}
			});
		</script>
	</head>
	<body ng-controller="MyController">
		<button ng-click="addUser()">Добавить пользователя</button>
		<ul>
			<li ng-repeat="user in users">{{user}}</li>
		</ul>
	</body>
</html>

Если запустить приведенный выше код, то по его загрузке вы увидите следующее:

<body>
	<button>Добавить пользователя</button>
	<ul>
		<li>Коля</li>
		<li>Вася</li>
		<li>Петя</li>
	</ul>
</body>

А если нажать на кнопку - то в конец добавится пользователь 'Иван':

<body>
	<button>Добавить пользователя</button>
	<ul>
		<li>Коля</li>
		<li>Вася</li>
		<li>Петя</li>
		<li>Иван</li>
	</ul>
</body>

Вот пример того, что у нас должно получиться:

Вы можете открыть этот пример в отдельной вкладке браузера.

Дублирование элементов в массиве

Angular устроен таким образом, что дублирование элементов в массиве, который перебирается циклом ng-repeat, недопустимо. Например, если у нас есть массив $scope.users со значением ['Коля', 'Вася', 'Петя'], то добавление, например, элемента 'Коля' в конец этого массива не приведет к изменению HTML кода.

Как с этим бороться, мы пройдем через несколько уроков. Пока просто знайте про такую особенность, чтобы она не стала для вас неожиданностью.

Что еще можно делать в фигурных скобках

В фигурных скобках можно не только выводить значения переменных, но и писать некоторый код.

Математические операции

Например можно выполнять математические операции:

<p>{{2+3}}</p>

В результате при запуске скрипта получим следующее:

<p>5</p>

Работа с массивами

Можно также создавать массивы и получать их элементы:

<p>{{['Коля', 'Вася', 'Петя'][0]}}</p>

В результате при запуске скрипта получим следующее:

<p>Коля</p>

Работа с объектами

Аналогично можно поступать и с объектами:

<p>{{ {name: 'Иван', age: 25}.name }}</p>

В результате при запуске скрипта получим следующее:

<p>Иван</p>

Массивы в циклах

Массивы и объекты также можно засовывать в ng-repeat:

<ul>
	<li ng-repeat="i in [1, 2, 3, 4, 5]">{{i}}</li>
</ul>

В результате при запуске скрипта получим следующее:

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>

Математические операции в циклах

Выведем не числа, а квадраты этих чисел:

<ul>
	<li ng-repeat="i in [1, 2, 3, 4, 5]">{{i*i}}</li>
</ul>

В результате при запуске скрипта получим следующее:

<ul>
	<li>1</li>
	<li>4</li>
	<li>9</li>
	<li>16</li>
	<li>25</li>
</ul>

Номер элемента $index в цикле

Внутри ng-repeat доступна переменная $index, в которой хранится номер элемента в массиве (нумерация с нуля).

Давайте, например выведем список пользователей из массива, добавив им в начало номер в массиве:

<ul>
	<li ng-repeat="user in ['Коля', 'Вася', 'Петя']">
		{{$index}} - {{user}}
	</li>
</ul>

В результате при запуске скрипта получим следующее:

<ul>
	<li>0 - Коля</li>
	<li>1 - Коля</li>
	<li>2 - Коля</li>
</ul>

А теперь давайте сделаем там, чтобы нумерация была не с нуля, а с единицы:

<ul>
	<li ng-repeat="user in ['Коля', 'Вася', 'Петя']">
		{{$index + 1}} - {{user}}
	</li>
</ul>

В результате при запуске скрипта получим следующее:

<ul>
	<li>1 - Коля</li>
	<li>2 - Коля</li>
	<li>3 - Коля</li>
</ul>

Циклы в циклах

Директива ng-repeat может использоваться для того, чтобы сделать цикл в цикле (уровень вложенности циклов может быть любым).

Для примера заполним таблицу <table> числами от 1 до 9:

<table>
	<tr ng-repeat="arr in [[1, 2, 3], [4, 5, 6], [7, 8, 9]]">
		{{i}}
		</tr>
</table>

В результате при запуске скрипта получим следующее:

<table>
	<tr>
		<td>1</td>
		<td>2</td>
		<td>3</td>
	</tr>
	<tr>
		<td>4</td>
		<td>5</td>
		<td>6</td>
	</tr>
	<tr>
		<td>7</td>
		<td>8</td>
		<td>9</td>
	</tr>
</table>