Статьи

Как использовать API Laravel с AngularJS

В первой части мы создали нашу серверную часть. Теперь мы можем построить нашего клиента. Мы собираемся использовать AngularJS для создания одностраничного приложения . Мы будем использовать очень простой шаблон начальной загрузки, построенный на лету.

AngularJS логотип

планирование

Наше приложение будет состоять из трех экранов.

  • Экран входа в систему : простой экран входа в систему. Пользователь вставит свой адрес электронной почты и пароль. Если что-то пойдет не так, появится сообщение об ошибке. В противном случае пользователь будет перенаправлен на главный экран . Они также смогут щелкнуть ссылку «Регистрация», чтобы открыть экран регистрации ;
  • Экран регистрации : простой экран регистрации, используемый для создания новой учетной записи в приложении. После указания всех необходимых данных пользователь автоматически войдет в систему;
  • Главный экран : основной экран приложения. Отсюда пользователь сможет получить список своих книг, добавить новые названия, а также обновить и удалить существующие;

Подготовка базовой рабочей среды Frontend

Нам нужно планировать, где мы размещаем наше приложение. Мы можем делать как нам угодно, но для этой серии статей и ради продолжения подойдут очень простые и «интегрированные» настройки.

Сначала мы создадим базовое представление Laravel Blade с именем index.blade.php , которое будет «размещать» приложение. Зависимости внешнего интерфейса будут обрабатываться Bower , который уже включен в Homestead Improved . Еще одна веская причина для перехода на виртуальные машины, если вы еще этого не сделали.

Давайте подготовим наш основной взгляд. В терминале заходим в public папку проекта и набираем, по порядку:

 bower install jquery bootstrap angular angular - route angular - local - storage restangular 

Мы уже знаем первые три элемента: jquery , bootstrap и angular . Четвертый, angular-route , будет использоваться в качестве маршрутизатора для нашего одностраничного приложения. Пятый, angular-local-storage , будет использоваться для локального хранения нашего токена аутентификации. Мы будем использовать последний, restangular , чтобы создать «ресурс» в Angular, который будет напрямую связываться с нашим сервером через HTTP-запросы.

Давайте вернемся к Laravel на секунду сейчас. Давайте перейдем к файлу app/Http/routes.php и добавим эту запись:

 Route : : get ( '/' , function ( ) { return view ( 'index' ) ; } ) ; 

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

Примечание: не путайте файл api_routes.php файлом api_routes.php .

Давайте создадим шаблон Blade, который мы будем использовать, и создадим новый файл с именем index.blade.php в resources/views .

 <!DOCTYPE html> < html lang = " en " > < head > < meta charset = " utf-8 " > < meta http-equiv = " X-UA-Compatible " content = " IE = edge " > < meta name = " viewport " content = " width = device-width, initial-scale = 1 " > < title > Book Wishlist Application </ title > < link href = " bower_components/bootstrap/dist/css/bootstrap.min.css " rel = " stylesheet " > < script src = " bower_components/angular/angular.min.js " > </ script > < script src = " bower_components/lodash/lodash.min.js " > </ script > < script src = " bower_components/angular-route/angular-route.min.js " > </ script > < script src = " bower_components/angular-local-storage/dist/angular-local-storage.min.js " > </ script > < script src = " bower_components/restangular/dist/restangular.min.js " > </ script > < style > li { padding-bottom : 8 px ; } </ style > </ head > < body > < div class = " container " > < div class = " row " > < div class = " col-md-12 " > < h1 > Book Wishlist Application </ h1 > </ div > </ div > </ div > < script src = " bower_components/jquery/dist/jquery.min.js " > </ script > < script src = " bower_components/bootstrap/dist/js/bootstrap.min.js " > </ script > </ body > </ html > 

В этом файле мы можем найти все, что нам нужно.

Базовая маршрутизация

Мы начнем с первых двух экранов: экран входа в систему и экран регистрации. Это будет связано с нашим angular-роутером. Основной принцип очень похож на процесс маршрутизации Laravel: мы назначаем определенный «экран» определенному маршруту.

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

Давайте создадим public папку с именем js . Затем мы app.js новый файл app.js внутри. Это будет наш основной одностраничный файл приложения:

 var bookWishlistApp = angular . module ( 'bookWishlistApp' , [ 'ngRoute' , 'bookWishlistAppControllers' ] ) ; bookWishlistApp . config ( [ '$routeProvider' , function ( $routeProvider ) { $routeProvider . when ( '/login' , { templateUrl : 'partials/login.html' , controller : 'LoginController' } ) . when ( '/signup' , { templateUrl : 'partials/signup.html' , controller : 'SignupController' } ) . when ( '/' , { templateUrl : 'partials/index.html' , controller : 'MainController' } ) . otherwise ( { redirectTo : '/' } ) ; } ] ) ; 

Мы определяем структуру маршрутизации нашего приложения. Как видите, у нас есть три возможных маршрута.

  • /login маршрут для экрана /login в систему;
  • /signup маршрут, для экрана регистрации;
  • / , для главного экрана приложения;

Объявляя модуль в верхней части файла, мы вводим две зависимости. Первый — это ngRoute , используемый для функциональности маршрутизации. Второй — bookWishlistAppControllers , модуль, который будет содержать все наши контроллеры приложений.

Давайте создадим еще один файл в той же папке с именем controllers.js .

 var bookWishlistAppControllers = angular . module ( 'bookWishlistAppControllers' , [ ] ) ; bookWishlistAppControllers . controller ( 'LoginController' , [ '$scope' , '$http' , function ( $scope , $http ) { } ] ) ; bookWishlistAppControllers . controller ( 'SignupController' , [ '$scope' , '$http' , function ( $scope , $http ) { } ] ) ; bookWishlistAppControllers . controller ( 'MainController' , [ '$scope' , '$http' , function ( $scope , $http ) { } ] ) ; 

Как мы видим, все они сейчас пусты. Мы заполним их позже. Прямо сейчас мы просто хотим протестировать нашу систему маршрутизации.

Давайте создадим еще одну папку в public : partials . Мы разместим три файла внутри: index.html , login.html и signup.html . Внутри каждого из них просто поместите демонстрационный текст.

В index.html вставьте:

 < p > main screen </ p > 

В login.html :

 < p > login screen </ p > 

… И в signup.html :

 < p > signup screen </ p > 

Теперь мы изменим наш взгляд Blade:

 <!DOCTYPE html> < html lang = " en " ng-app = " bookWishlistApp " > < head > < meta charset = " utf-8 " > < meta http-equiv = " X-UA-Compatible " content = " IE = edge " > < meta name = " viewport " content = " width = device-width, initial-scale = 1 " > < title > Book Wishlist Application </ title > < link href = " bower_components/bootstrap/dist/css/bootstrap.min.css " rel = " stylesheet " > < script src = " bower_components/angular/angular.min.js " > </ script > < script src = " bower_components/lodash/lodash.min.js " > </ script > < script src = " bower_components/angular-route/angular-route.min.js " > </ script > < script src = " bower_components/angular-local-storage/dist/angular-local-storage.min.js " > </ script > < script src = " bower_components/restangular/dist/restangular.min.js " > </ script > < script src = " js/app.js " > </ script > < script src = " js/controllers.js " > </ script > < style > li { padding-bottom : 8 px ; } </ style > </ head > < body > < div class = " container " > < div class = " row " > < div class = " col-md-12 " > < h1 > Book Wishlist Application </ h1 > </ div > </ div > < div ng-view > </ div > </ div > < script src = " bower_components/jquery/dist/jquery.min.js " > </ script > < script src = " bower_components/bootstrap/dist/js/bootstrap.min.js " > </ script > </ body > </ html > 

Мы добавили атрибут ng-app="bookWishlistApp" в элемент html и атрибут ng-view к новому элементу div . Это будет «контейнер» для наших частичек.

Нам также нужно будет добавить

 < script src = " js/app.js " > </ script > < script src = " js/controllers.js " > </ script > 

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

Если мы проверим это, вот что мы увидим:

Основная главная страница

Компонент угловой маршрутизации автоматически добавляет /#/ в URL. Теперь, если мы вручную добавим login в строку, это то, что мы получим:

Тестирование маршрута

Ура! Наша маршрутизация работает отлично.

Регистрация и вход

Чтобы создать экраны входа и регистрации, мы будем инкапсулировать некоторую логику, связанную с доступом пользователей, в отдельный userService .

Давайте создадим новый файл в public/js и public/js его services.js :

 var bookWishlistAppServices = angular . module ( 'bookWishlistAppServices' , [ 'LocalStorageModule' ] ) ; bookWishlistAppServices . factory ( 'userService' , [ '$http' , 'localStorageService' , function ( $http , localStorageService ) { function checkIfLoggedIn ( ) { if ( localStorageService . get ( 'token' ) ) return true ; else return false ; } function signup ( name , email , password , onSuccess , onError ) { $http . post ( '/api/auth/signup' , { name : name , email : email , password : password } ) . then ( function ( response ) { localStorageService . set ( 'token' , response . data . token ) ; onSuccess ( response ) ; } , function ( response ) { onError ( response ) ; } ) ; } function login ( email , password , onSuccess , onError ) { $http . post ( '/api/auth/login' , { email : email , password : password } ) . then ( function ( response ) { localStorageService . set ( 'token' , response . data . token ) ; onSuccess ( response ) ; } , function ( response ) { onError ( response ) ; } ) ; } function logout ( ) { localStorageService . remove ( 'token' ) ; } function getCurrentToken ( ) { return localStorageService . get ( 'token' ) ; } return { checkIfLoggedIn : checkIfLoggedIn , signup : signup , login : login , logout : logout , getCurrentToken : getCurrentToken } } ] ) ; 

Мы использовали базовую функциональность Angular $http для выполнения некоторых HTTP-вызовов. Чтобы быть более точным, мы реализовали:

  • метод checkIfLoggedIn который проверяет, присутствует ли токен на самом деле;
  • метод signup который принимает имя, адрес электронной почты и пароль в качестве параметров. Если процесс регистрации проходит успешно, токен автоматически сохраняется в локальном хранилище и готов к использованию;
  • метод login в login который принимает электронную почту и пароль в качестве параметров. Если все идет хорошо, токен хранится в локальном хранилище;
  • метод logout из logout для удаления сохраненного токена;
  • метод getCurrentToken , используемый для получения фактического сохраненного токена. Мы будем использовать его позже, когда будем делать запросы к защищенным конечным точкам наших API;

Очевидно, что нам также нужно будет добавить этот файл в основное resources/views/index.blade.php : resources/views/index.blade.php .

 < script src = " js/app.js " > </ script > < script src = " js/controllers.js " > </ script > < script src = " js/services.js " > </ script > 

Теперь, когда у нас есть наш сервис, мы можем начать с экрана регистрации. Это будет очень просто: просто текст приветствия, три текстовых поля и кнопка отправки.

Начнем с представления. Мы откроем public/partials/signup.html и добавим:

 < div class = " row " > < div class = " col-md-4 col-md-offset-4 " > < h2 > Signup </ h2 > < p > Welcome! If you want to sign up to our awesome service, fill this form and press on "Signup"! </ p > < hr > < p > < input type = " text " class = " form-control " placeholder = " Name... " ng-model = " name " required /> </ p > < p > < input type = " text " class = " form-control " placeholder = " Email Address... " ng-model = " email " required /> </ p > < p > < input type = " password " class = " form-control " placeholder = " Password... " ng-model = " password " required /> </ p > < hr > < p > < button type = " button " class = " btn btn-success form-control " ng-click = " signup() " > Signup </ button > </ p > < hr > < p > < a href = " #login " > Already signed up? < b > Log in! </ b > </ a > </ p > </ div > </ div > 

ng-click на кнопку ng-click , мы вызовем метод signup() нашего контроллера.

Теперь давайте откроем файл js/controllers.js и заполним SignupController

 bookWishlistAppControllers . controller ( 'SignupController' , [ '$scope' , '$location' , 'userService' , function ( $scope , $location , userService ) { $scope . signup = function ( ) { userService . signup ( $scope . name , $scope . email , $scope . password , function ( response ) { alert ( 'Great! You are now signed in! Welcome, ' + $scope . name + '!' ) ; $location . path ( '/' ) ; } , function ( response ) { alert ( 'Something went wrong with the signup process. Try again later.' ) ; } ) ; } $scope . name = '' ; $scope . email = '' ; $scope . password = '' ; if ( userService . checkIfLoggedIn ( ) ) $location . path ( '/' ) ; } ] ) ; 

Давай объясним

Сначала мы сделали метод signup . Из-за того, что это уже очень длинный урок, мы пропустили проверку формы. Единственное, что он делает — это вызывает метод signup только что userService нами userService . Если все в порядке, он показывает предупреждение и перенаправляет пользователя на главный экран приложения.

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

Наконец, мы делаем очень простую проверку входа в систему:

 if ( userService . checkIfLoggedIn ( ) ) $location . path ( '/' ) ; 

Если токен уже присутствует, пользователь будет автоматически перенаправлен на главную страницу.

Страница входа будет очень похожа. Давайте отредактируем public/partials/login.html :

 < div class = " row " > < div class = " col-md-4 col-md-offset-4 " > < h2 > Login </ h2 > < p > Welcome! Use this form to log into your application. </ p > < hr > < p > < input type = " text " class = " form-control " placeholder = " Email Address... " ng-model = " email " required /> </ p > < p > < input type = " password " class = " form-control " placeholder = " Password... " ng-model = " password " required /> </ p > < hr > < p > < button type = " button " class = " btn btn-success form-control " ng-click = " login() " > Login </ button > </ p > < hr > < p > < a href = " #signup " > First time here? < b > Sign up! </ b > </ a > </ p > </ div > </ div > 

Мы сейчас создадим метод login() в LoginController . Откройте public/js/controllers.js и добавьте:

 bookWishlistAppControllers . controller ( 'LoginController' , [ '$scope' , '$http' , '$location' , 'userService' , function ( $scope , $http , $location , userService ) { $scope . login = function ( ) { userService . login ( $scope . email , $scope . password , function ( response ) { $location . path ( '/' ) ; } , function ( response ) { alert ( 'Something went wrong with the login process. Try again later!' ) ; } ) ; } $scope . email = '' ; $scope . password = '' ; if ( userService . checkIfLoggedIn ( ) ) $location . path ( '/' ) ; } ] ) ; 

Мы снова использовали метод userService login() . Если все идет хорошо, пользователь перенаправляется на главную страницу. В противном случае отображается сообщение об ошибке.

Управление книгами

Последнее, что нам нужно сделать, это реализовать часть управления книгами. Мы сделаем все в MainController , начиная с отображения списка и процесса создания, до обновления и удаления.

Опять же, мы создадим сервис для всего, что нам нужно о постоянстве книг — что-то вроде хранилища. В public/js/services.js мы добавляем новый сервис: bookService .

 bookWishlistAppServices . factory ( 'bookService' , [ 'Restangular' , 'userService' , function ( Restangular , userService ) { function getAll ( onSuccess , onError ) { Restangular . all ( 'api/books' ) . getList ( ) . then ( function ( response ) { onSuccess ( response ) ; } , function ( ) { onError ( response ) ; } ) ; } function getById ( bookId , onSuccess , onError ) { Restangular . one ( 'api/books' , bookId ) . get ( ) . then ( function ( response ) { onSuccess ( response ) ; } , function ( response ) { onError ( response ) ; } ) ; } function create ( data , onSuccess , onError ) { Restangular . all ( 'api/books' ) . post ( data ) . then ( function ( response ) { onSuccess ( response ) ; } , function ( response ) { onError ( response ) ; } ) ; } function update ( bookId , data , onSuccess , onError ) { Restangular . one ( "api/books" ) . customPUT ( data , bookId ) . then ( function ( response ) { onSuccess ( response ) ; } , function ( response ) { onError ( response ) ; } ) ; } function remove ( bookId , onSuccess , onError ) { Restangular . one ( 'api/books/' , bookId ) . remove ( ) . then ( function ( ) { onSuccess ( ) ; } , function ( response ) { onError ( response ) ; } ) ; } Restangular . setDefaultHeaders ( { 'Authorization' : 'Bearer ' + userService . getCurrentToken ( ) } ) ; return { getAll : getAll , getById : getById , create : create , update : update , remove : remove } } ] ) ; 

Примечание: не забудьте добавить restangular как зависимость модуля.

Как мы видим, Restangular использовалось для работы с конечными точками API. Чтобы быть более точным, у нас есть пять методов.

  • метод getAll , чтобы получить полный список книг для текущего пользователя;
  • метод getById для извлечения конкретной книги по ее идентификатору;
  • метод create , чтобы сохранить новую книгу;
  • метод update , чтобы обновить существующий, учитывая его идентификатор;
  • метод remove , чтобы удалить существующую книгу из списка, учитывая ее идентификатор;

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

Примечание: стоит отметить наличие специального метода обновления. Обычно процесс обновления состоит из двух этапов. Получение объекта, а затем его обновление. У нас уже есть метод getById , поэтому нам не нужна часть get потому что мы уже знаем идентификатор книги. Используя customPUT нам удалось создать «альтернативную» версию процедуры обновления без необходимости дополнительного вызова службы API.

Мы можем начать с показа списка. public/partials/index.html :

 < hr > < div class = " pull-right " > < button type = " button " class = " btn btn-info " ng-click = " logout() " > Logout! </ button > </ div > < div class = " clearfix " > </ div > < hr > < div class = " row " > < div class = " col-md-12 " > < p > You currently have < b > {{ books.length }} </ b > books in your wishlist. </ p > < ul > < li ng-repeat = " book in books " > < b > {{ book.title }} </ b > by < i > {{ book.author_name }} </ i > </ li > </ ul > </ div > </ div > 

В этом первом шаблоне мы просто показываем список книг. Чтобы получить наши данные, мы создадим метод refresh в контроллере. В методе logout controllers.js который вызывает метод с тем же именем в userService , мы добавляем этот код в MainController :

 bookWishlistAppControllers . controller ( 'MainController' , [ '$scope' , '$location' , 'userService' , 'bookService' , function ( $scope , $location , userService , bookService ) { $scope . logout = function ( ) { userService . logout ( ) ; $location . path ( '/login' ) ; } $scope . refresh = function ( ) { bookService . getAll ( function ( response ) { $scope . books = response ; } , function ( ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } if ( ! userService . checkIfLoggedIn ( ) ) $location . path ( '/login' ) ; $scope . books = [ ] ; $scope . refresh ( ) ; } ] ) ; 

Мы добавили два метода: logout вызывает тот же имя в userService , и refresh . Последний вызывает метод getAll в booksService . Затем он присваивает результат переменной $scope.books которая уже связана с представлением. Если что-то идет не так, отображается ошибка.

Теперь мы должны реализовать функциональность создания книг. Для этого вернемся к нашему представлению public/partials/index.html . Давайте добавим модал, который мы будем использовать для добавления новой книги, и кнопку для ее переключения.

 < hr > < div class = " pull-left " > < button type = " button " class = " btn btn-success " data-toggle = " modal " data-target = " #addBookModal " > + Add Book </ button > </ div > < div class = " pull-right " > < button type = " button " class = " btn btn-info " ng-click = " logout() " > Logout! </ button > </ div > < div class = " clearfix " > </ div > < hr > < div class = " row " > < div class = " col-md-12 " > < p > You currently have < b > {{ books.length }} </ b > books in your wishlist. </ p > < ul > < li ng-repeat = " book in books " > < b > {{ book.title }} </ b > by < i > {{ book.author_name }} </ i > </ li > </ ul > </ div > </ div > < div class = " modal fade " id = " addBookModal " > < div class = " modal-dialog " > < div class = " modal-content " > < div class = " modal-header " > < button type = " button " class = " close " data-dismiss = " modal " aria-label = " Close " > < span aria-hidden = " true " > &times; </ span > </ button > < h4 class = " modal-title " > Add a Book </ h4 > </ div > < div class = " modal-body " > < p > < input class = " form-control " ng-model = " currentBookTitle " placeholder = " Title... " type = " text " > </ p > < p > < input class = " form-control " ng-model = " currentBookAuthorName " placeholder = " Author Name... " type = " text " > </ p > < p > < input class = " form-control " ng-model = " currentBookPagesCount " placeholder = " Pages Count... " type = " text " > </ p > </ div > < div class = " modal-footer " > < button type = " button " class = " btn btn-default " data-dismiss = " modal " > Close </ button > < button type = " button " class = " btn btn-primary " ng-click = " create() " > Save Book </ button > </ div > </ div > <!-- /.modal-content --> </ div > <!-- /.modal-dialog --> </ div > <!-- /.modal --> 

Теперь вернемся к MainController и реализуем метод create :

 bookWishlistAppControllers . controller ( 'MainController' , [ '$scope' , '$location' , 'userService' , 'bookService' , function ( $scope , $location , userService , bookService ) { $scope . logout = function ( ) { userService . logout ( ) ; $location . path ( '/login' ) ; } $scope . create = function ( ) { bookService . create ( { title : $scope . currentBookTitle , author_name : $scope . currentBookAuthorName , pages_count : $scope . currentBookPagesCount } , function ( ) { $ ( '#addBookModal' ) . modal ( 'toggle' ) ; $scope . currentBookReset ( ) ; $scope . refresh ( ) ; } , function ( ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } $scope . refresh = function ( ) { bookService . getAll ( function ( response ) { $scope . books = response ; } , function ( ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } $scope . currentBookReset = function ( ) { $scope . currentBookTitle = '' ; $scope . currentBookAuthorName = '' ; $scope . currentBookPagesCount = '' ; } if ( ! userService . checkIfLoggedIn ( ) ) $location . path ( '/login' ) ; $scope . books = [ ] ; $scope . currentBookReset ( ) ; $scope . refresh ( ) ; } ] ) ; 

Наш контроллер растет: мы можем видеть метод create и currentBookReset который сбрасывает все переменные области видимости. Метод create очевидно, использует метод create из bookService . Приложение теперь должно работать хорошо!

Мы все еще должны осуществить обновление и удаление книг. Давайте начнем с более простого метода remove .

В public/partials/index.html мы добавляем кнопку удаления в угловой повторитель, которая будет вызывать метод remove в MainController :

 < ul > < li ng-repeat = " book in books " > < b > {{ book.title }} </ b > by < i > {{ book.author_name }} </ i > | < button ng-click = " delete(book.id) " class = " btn btn-danger btn-xs " > Delete </ button > </ li > </ ul > 

Затем мы добавляем метод remove в MainController :

 $scope . remove = function ( bookId ) { if ( confirm ( 'Are you sure to remove this book from your wishlist?' ) ) { bookService . remove ( bookId , function ( ) { alert ( 'Book removed successfully.' ) ; } , function ( ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } } 

bookService выполнит свою работу. Если что-то пойдет не так, пользователю будет показано предупреждение.

Мы наконец можем реализовать нашу функцию обновления, последнюю. В представлении public/partials/index.html мы добавим кнопку к нашему повторителю. На этот раз синяя информация. Повторитель теперь будет выглядеть так:

 < ul > < li ng-repeat = " book in books " > < b > {{ book.title }} </ b > by < i > {{ book.author_name }} </ i > | < button ng-click = " load(book.id) " class = " btn btn-info btn-xs " > Update </ button > < button ng-click = " remove(book.id) " class = " btn btn-danger btn-xs " > Remove </ button > </ li > </ ul > 

и это модал, который мы собираемся добавить:

 < div class = " modal fade " id = " updateBookModal " > < div class = " modal-dialog " > < div class = " modal-content " > < div class = " modal-header " > < button type = " button " class = " close " data-dismiss = " modal " aria-label = " Close " > < span aria-hidden = " true " > &times; </ span > </ button > < h4 class = " modal-title " > Update a Book </ h4 > </ div > < div class = " modal-body " > < input type = " hidden " ng-model = " currentBookId " /> < p > < input class = " form-control " ng-model = " currentBookTitle " placeholder = " Title... " type = " text " > </ p > < p > < input class = " form-control " ng-model = " currentBookAuthorName " placeholder = " Author Name... " type = " text " > </ p > < p > < input class = " form-control " ng-model = " currentBookPagesCount " placeholder = " Pages Count... " type = " text " > </ p > </ div > < div class = " modal-footer " > < button type = " button " class = " btn btn-default " data-dismiss = " modal " > Close </ button > < button type = " button " class = " btn btn-primary " ng-click = " update() " > Save Changes </ button > </ div > </ div > <!-- /.modal-content --> </ div > <!-- /.modal-dialog --> </ div > <!-- /.modal --> 

Нам также понадобятся некоторые методы в нашем контроллере. Вернемся в MainController и добавим:

 $scope . load = function ( bookId ) { bookService . getById ( bookId , function ( response ) { $scope . currentBookId = response . book . id ; $scope . currentBookTitle = response . book . title ; $scope . currentBookAuthorName = response . book . author_name ; $scope . currentBookPagesCount = response . book . pages_count ; $ ( '#updateBookModal' ) . modal ( 'toggle' ) ; } , function ( ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } $scope . update = function ( ) { bookService . update ( $scope . currentBookId , { title : $scope . currentBookTitle , author_name : $scope . currentBookAuthorName , pages_count : $scope . currentBookPagesCount } , function ( response ) { $ ( '#updateBookModal' ) . modal ( 'toggle' ) ; $scope . currentBookReset ( ) ; $scope . refresh ( ) ; } , function ( response ) { alert ( 'Some errors occurred while communicating with the service. Try again later.' ) ; } ) ; } 

Метод load извлекает данные книги из API и отображает их в модальном режиме. Затем, после процедуры редактирования, пользователь нажмет кнопку «Сохранить изменения», которая вызовет метод update . Этот последний метод будет вызывать bookService соответственно, сохраняя редактирование с помощью API.

Наш контроллер в окончательном виде будет выглядеть следующим образом .

Наше приложение завершено … и мы можем его использовать! Время хранить тонны книг.

Рабочее приложение

Да, я люблю Жюля Верна.

Вывод

В этой серии мы использовали две отдельные технологии для создания законченного (и сложного) приложения очень простым способом. Благодаря некоторым инструментам, таким как Laravel API Boilerplate и Restangular, мы почти полностью сосредоточились на реальной бизнес-логике, вместо того, чтобы тратить время на загрузку.

В этой части мы рассмотрели реализацию интерфейса Angular для нашего интерфейса Laravel API, переходя от ничего к полноценному приложению. Очевидно, что путешествие здесь не заканчивается: в качестве упражнения я предлагаю внедрить механизм, чтобы проверить, не истек ли токен, и создать еще один, чтобы обновить его.

В будущих статьях мы могли бы взглянуть на различные JS-структуры, чтобы достичь одного и того же, и решить, какая из них самая простая. Любые предложения, какие из них мы должны попробовать в первую очередь?