Important:
Необходимое условие:Установите StrongLoop, как описано в Установка StrongLoop.
Рекомендации: Прочитайте Основные понятия LoopBack.
Note:
Чтоб выполнить данный шаг, у вас должно быть базовое понимание AngularJS.
LoopBack AngularJS SDK автоматически создает клиентский API, который позволяет вам вызывать ваш LoopBack Node API.
Генерация lb-services.js
Для генерации Angular сервисов для LoopBack приложения, используйте инструмент командной строки AngularJS SDK lb-ng
. В корневой директории проекта, введите следующую команду:
$ lb-ng server/server.js client/js/services/lb-services.js
Это команда создает client/js/services/lb-services.js
.
Получение других клиентских файлов
REVIEW COMMENT from Rand
Need to set up a release in the repo, where the client directory contains everything BUT lb-services.js, since they generate it themselves.</div>
Если выполнили все предыдущие шаги из Введение в Coffee Shop Reviews приложение, то вы наверное уже клонировали себе репозиторий loopback-getting-started-intermediate. Если нет, то сделайте это сейчас.
Затем скопируйте client
подпапку в папку вашего проекта:
$ git clone https://github.com/strongloop/loopback-getting-started-intermediate.git
$ cp -r loopback-getting-started-intermediate/client <your-app-dir>
Теперь давайте посмотрим на то, что теперь у вас есть в папке client:
index.html
- css - стили
style.css
- js - JavaScript файлы приложения
app.js
- controllers - AngularJS контролеры
auth.js
review.js
- services - AngularJS сервисы
auth.js
lb-services.js
- vendor - AngularJS библиотеки (зависимости)
-
angular-resource.js
-
angular-ui-router.js
-
angular.js
-
- views - HTML файлы
-
all-reviews.html
-
forbidden.html
-
my-reviews.html
-
sign-up-form.html
-
login.html
-
review-form.html
-
sign-up-success.html
-
Каждый файл и каталог кратко описан ниже.
index.html
Файл index.html
единственный файл в верхнем уровне папки /client
, и определяет основную целевую страницу приложенияand. Давайте откроем его в редакторе:
client/index.html
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>loopback-getting-started-intermediate</title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<header>
<h1>Coffee shop reviews</h1>
<h2 ng-show="currentUser">Hello </h2>
<nav>
<ul>
<li>
<a ui-sref="all-reviews" ui-sref-active="active">All reviews</a>
</li>
<li ng-hide="currentUser">
<a ui-sref="sign-up" ui-sref-active="active">Sign up</a>
</li>
<li ng-show="currentUser">
<a ui-sref="my-reviews" ui-sref-active="active">My Reviews</a>
</li>
<li ng-show="currentUser">
<a ui-sref="add-review" ui-sref-active="active">Add Review</a>
</li>
<li ng-hide="currentUser">
<a ui-sref="login" ui-sref-active="active">Log in</a>
</li>
<li ng-show="currentUser">
<a ui-sref="logout" ui-sref-active="active">Log out</a>
</li>
</ul>
</nav>
</header>
<main ui-view></main>
<script src="vendor/angular.js"></script>
<script src="vendor/angular-resource.js"></script>
<script src="vendor/angular-ui-router.js"></script>
<script src="js/app.js"></script>
<script src="js/services/lb-services.js"></script>
<script src="js/controllers/auth.js"></script>
<script src="js/controllers/review.js"></script>
<script src="js/services/auth.js"></script>
</body>
</html>
Просматривая файл вы можите увидеть ссылки на стили в папке /css, файлы клиентского
JavaScript в папке /vendor
и /js
.
REVIEW COMMENT from Rand
Do we need to go over the CSS file?</div>
Основной клиентский JavaScript файл (app.js)
В js/app.js
файле определяется конфигурация приложения.
js/app.js
angular
.module('app', [
'ui.router',
'lbServices'
])
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider,
$urlRouterProvider) {
$stateProvider
.state('add-review', {
url: '/add-review',
templateUrl: 'views/review-form.html',
controller: 'AddReviewController',
authenticate: true
})
.state('all-reviews', {
url: '/all-reviews',
templateUrl: 'views/all-reviews.html',
controller: 'AllReviewsController'
})
.state('edit-review', {
url: '/edit-review/:id',
templateUrl: 'views/review-form.html',
controller: 'EditReviewController',
authenticate: true
})
.state('delete-review', {
url: '/delete-review/:id',
controller: 'DeleteReviewController',
authenticate: true
})
.state('forbidden', {
url: '/forbidden',
templateUrl: 'views/forbidden.html',
})
.state('login', {
url: '/login',
templateUrl: 'views/login.html',
controller: 'AuthLoginController'
})
.state('logout', {
url: '/logout',
controller: 'AuthLogoutController'
})
.state('my-reviews', {
url: '/my-reviews',
templateUrl: 'views/my-reviews.html',
controller: 'MyReviewsController',
authenticate: true
})
.state('sign-up', {
url: '/sign-up',
templateUrl: 'views/sign-up-form.html',
controller: 'SignUpController',
})
.state('sign-up-success', {
url: '/sign-up/success',
templateUrl: 'views/sign-up-success.html'
});
$urlRouterProvider.otherwise('all-reviews');
}])
.run(['$rootScope', '$state', function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(event, next) {
// redirect to login page if not logged in
if (next.authenticate && !$rootScope.currentUser) {
event.preventDefault(); //prevent current page from loading
$state.go('forbidden');
}
});
}]);
Строки 2 - 4 включают в себя зависимости приложения: ui.router
и lbServices
. Последний является сервисом AngularJS библиотеки который вы сгенерировали ранее используя lb-ng
.
Строки 61 - 66 определения перехватчика, который вызывается когда происходит изменения состояния: если пользователь не авторизован его перенаправит на страницу с запретом.
Остальные строки определяют состояние приложения. Состоянием определяться какие страницы будут видны, когда пользователь будет переходить по URLs или нажмет на ссылку. Любое состояние для которого authenticate
является true,
требует, чтоб вы авторизовались сначала. Если вы перейдете по одному из этих адресов непосредственно то вы увидите страницу с ошибкой запрета доступа (state = forbidden
, url = /forbidden)
. Каждый вызов state()
задает шаблон для состояния и контролера и вызывает проверку на требование авторизации.
В следующей таблице приведены состояния и как они соответствуют контролерам, шаблонам и URL.
Состояние | URL | Описнаие | Контроллер | Просмотр/ Шаблон | Должен ли быть авторизованым? |
---|---|---|---|---|---|
'add-review' |
/add-review |
Добавление отзыва. | AddReviewController |
review-form.html |
Да |
'all-reviews' | /all-reviews | Список отзывов. | AllReviewsController | all-reviews.html | Нет |
'edit-review' | /edit-review/:id | Редактирование выбранного отзыва | EditReviewController | review-form.html | Да |
'delete-review' | /delete-review/:id | Удаление выбранного отзыва | DeleteReviewController | None | Да |
'forbidden' | /forbidden |
Ошибка запрещенного URL.
|
EditReviewController | forbidden.html | Нет |
'login' | /login |
Авторизация Перенаправляет на страницу добавления отзыва после авторизации |
AuthLoginController | login.html | Нет |
'logout' | /logout |
Выйти
|
AuthLogoutController | Нет | Нет |
'my-reviews' | /my-reviews | Список юзера который авторизовался | MyReviewsController | my-reviews.html | Да |
'sign-up' | /sign-up | Регестрация | SignUpController | sign-up-form.html | Нет |
'sign-up-success' | /sign-up/success |
Успешная регистрация. Отображает ссылку на страницу всех отзывов. |
Нет | sign-up-success.html | Нет |
Контроллеры
ВAngular, контроллер это функция JavaScript конструктора которая используется для усиления Angular Scope.
Когда контролер подключен к DOM через директивы ng-controller, Angular будет создавать новый Controller объект, используя функцию конструктора. Новый дочерняя область (scope) будут доступны в виде инъекционного параметра функции конструктора $scope
. Для получения более, see Understanding Controllers (AngularJS documentation).
client/js/controllers папка содержит два файла которые определяют контролеры
: auth.js и
review.js
.
Контролер auth.js обрабатывает регистрацию пользователей, авторизацию и выход
. Когда пользователь входит в систему, в currentUser
объект установлен в корневой области (scope). Другие части приложения проверяют currentUser
объект при выполнении действии. Когда пользователь выходит, currentUser объект разрушается
.
js/controllers/auth.js
angular
.module('app')
.controller('AuthLoginController', ['$scope', 'AuthService', '$state',
function($scope, AuthService, $state) {
$scope.user = {
email: 'foo@bar.com',
password: 'foobar'
};
$scope.login = function() {
AuthService.login($scope.user.email, $scope.user.password)
.then(function() {
$state.go('add-review');
});
};
}
])
.controller('AuthLogoutController', ['$scope', 'AuthService', '$state',
function($scope, AuthService, $state) {
AuthService.logout()
.then(function() {
$state.go('all-reviews');
});
}
])
.controller('SignUpController', ['$scope', 'AuthService', '$state',
function($scope, AuthService, $state) {
$scope.user = {
email: 'baz@qux.com',
password: 'bazqux'
};
$scope.register = function() {
AuthService.register($scope.user.email, $scope.user.password)
.then(function() {
$state.transitionTo('sign-up-success');
});
};
}
]);
Другой файл, review.js
, определяет контролеры для действий отзывов (review).
Expand source
angular
.module('app')
.controller('AllReviewsController', ['$scope', 'Review', function($scope,
Review) {
$scope.reviews = Review.find({
filter: {
include: [
'coffeeShop',
'reviewer'
]
}
});
}])
.controller('AddReviewController', ['$scope', 'CoffeeShop', 'Review',
'$state',
function($scope, CoffeeShop, Review, $state) {
$scope.action = 'Add';
$scope.coffeeShops = [];
$scope.selectedShop;
$scope.review = {};
$scope.isDisabled = false;
CoffeeShop
.find()
.$promise
.then(function(coffeeShops) {
$scope.coffeeShops = coffeeShops;
$scope.selectedShop = $scope.selectedShop || coffeeShops[0];
});
$scope.submitForm = function() {
Review
.create({
rating: $scope.review.rating,
comments: $scope.review.comments,
coffeeShopId: $scope.selectedShop.id
})
.$promise
.then(function() {
$state.go('all-reviews');
});
};
}
])
.controller('DeleteReviewController', ['$scope', 'Review', '$state',
'$stateParams',
function($scope, Review, $state, $stateParams) {
Review
.deleteById({
id: $stateParams.id
})
.$promise
.then(function() {
$state.go('my-reviews');
});
}
])
.controller('EditReviewController', ['$scope', '$q', 'CoffeeShop', 'Review',
'$stateParams', '$state',
function($scope, $q, CoffeeShop, Review,
$stateParams, $state) {
$scope.action = 'Edit';
$scope.coffeeShops = [];
$scope.selectedShop;
$scope.review = {};
$scope.isDisabled = true;
$q
.all([
CoffeeShop.find().$promise,
Review.findById({
id: $stateParams.id
}).$promise
])
.then(function(data) {
var coffeeShops = $scope.coffeeShops = data[0];
$scope.review = data[1];
$scope.selectedShop;
var selectedShopIndex = coffeeShops
.map(function(coffeeShop) {
return coffeeShop.id;
})
.indexOf($scope.review.coffeeShopId);
$scope.selectedShop = coffeeShops[selectedShopIndex];
});
$scope.submitForm = function() {
$scope.review.coffeeShopId = $scope.selectedShop.id;
$scope.review
.$save()
.then(function(review) {
$state.go('all-reviews');
});
};
}
])
.controller('MyReviewsController', ['$scope', 'Review', '$rootScope',
function($scope, Review, $rootScope) {
$scope.reviews = Review.find({
filter: {
where: {
publisherId: $rootScope.currentUser.id
},
include: [
'coffeeShop',
'reviewer'
]
}
});
}
]);
Следующая таблица описывает контроллеры, определенные в review.js.
Контролеры | Описание |
---|---|
AllReviewsController | Выполняет Review.find() для получение отзывов . Использует включаемый модуль для добавления coffeeShop и модели отзыва. Это возможно из-за связи определенной выше. |
AddReviewController |
Кофейни заполняются с сервера при первой загрузке странице, через CoffeeShop.find() меню. Когда форма будет отправлена, мы создаем отзыв и изменяем страницу всех отзывов когда права позволяют. |
DeleteReviewController | Не отображает при соответствии этому состоянию, когда вызывается; соответствующий данному ID отзыв удаляется. ID находится в URL. |
EditReviewController |
Похож на AddReviewController когда страница первый раз загружается. Приложение выполнит два запроса одновременно используя $q, чтобы получить необходимые модели. С помощью этих модели мы получаем выпадающее меню с доступными кофейнями. После того, как приложение отображает кофейне в выпадающем списке, и выбирает кофейню ранее выбранную в первоначальном обзоре. Затем приложение компонует |
MyReviewController |
Как AllReviewsController, этот контроллер использует "where" фильтр, чтобы ограничить результирующий набор, основанный на publisherId, где publisherId определяется текущим вошедшим в систему пользователем. Затем он использует подключаемый фильтр включающий Coffeeshop и модель рецензент (reviewer ). |
Сервисы
Anоgular сервисы взаимозаменяемые объекты, которые соединены друг с другом с помощью зависимых иньекций (DI). Вы можете воспользоваться сервисами чтоб организовать совместное использование кода посредством вашего приложения.
Папка js/services
содержит две AngularJS сервис библиотеки: auth.js
и lb-services.js
.
Вы сгенерировали lb-services.js
ранее, и это описано в Генерация lb-services.js.
Другой файл, auth.js, предоставляет простой интерфейс для механизмов аутентификации низкого уровня. Он использует модель Reviewer (
рецензент) (которая расширяет базовую User модель ) и определяет следующие сервисы:
login
: регистрирует пользователя в inLoopback автоматически управляет ключом безопасности (authentication token), который хранится в локальном HTML5 хранилище браузера.logout
: регистрирует выход пользователя. Сохраняет ключ в локальном HTML5 хранилище браузера localstorage.register
: регистрирует нового пользователя с помощью прилагаемого email и пароля, с минимальными требованиями для создания нового пользователя LoopBack.
js/services/auth.js Expand source
angular
.module('app')
.factory('AuthService', ['Reviewer', '$q', '$rootScope', function(User, $q,
$rootScope) {
function login(email, password) {
return User
.login({
email: email,
password: password
})
.$promise
.then(function(response) {
$rootScope.currentUser = {
id: response.user.id,
tokenId: response.id,
email: email
};
});
}
function logout() {
return User
.logout()
.$promise
.then(function() {
$rootScope.currentUser = null;
});
}
function register(email, password) {
return User
.create({
email: email,
password: password
})
.$promise;
}
return {
login: login,
logout: logout,
register: register
};
}]);
Вид
Папка client/views
содержит семь “partial” шаблонов для просмотра, которые client/index.html
использует ngView директиве. ”partial” сегмент шаблона в самом файле HTML.
Приведенная выше таблица описывает, каким образом вид соответствует состояниям и контроллерам.