Создание приложения CRUD в считанные минуты с помощью $ Angular ресурса
Июль 25, 2018
Большинство одностраничных приложений включают операции CRUD . Если вы строите CRUD-операции с использованием AngularJS, вы можете использовать возможности службы $resource . Созданный на основе сервиса $http , $resource Angular — это фабрика, которая позволяет легко взаимодействовать с бэкэндами RESTful. Итак, давайте исследуем $resource и используем его для реализации CRUD-операций в Angular.
Кроме того, ваш основной модуль приложения должен объявить зависимость от модуля ngResource для использования $resource . Следующий пример демонстрирует, как это сделать:
angular.module('mainApp',['ngResource']);//mainApp is our main module
Начиная
$resource ожидает классический RESTful бэкэнд. Это означает, что у вас должны быть конечные точки REST в следующем формате:
Вы можете создавать конечные точки, используя выбранный язык на стороне сервера. Однако я использовал Node + Express + MongoDB для разработки RESTful API для демонстрационного приложения. Как только вы подготовите URL-адреса, вы можете воспользоваться помощью $resource для взаимодействия с этими URL-адресами. Итак, давайте посмотрим, как именно работает $resource .
Как работает $ resource?
Чтобы использовать $resource внутри вашего контроллера / сервиса, вам нужно объявить зависимость от $resource . Следующим шагом является вызов функции $resource() с конечной точкой REST, как показано в следующем примере. Этот вызов функции возвращает представление класса $resource которое можно использовать для взаимодействия с бэкэндом REST.
angular.module('myApp.services').factory('Entry',function($resource){return$resource('/api/entries/:id');// Note the full endpoint address });
Результатом вызова функции является объект класса ресурсов, который по умолчанию имеет следующие пять методов:
get()
query()
save()
remove()
delete()
Теперь давайте посмотрим, как мы можем использовать методы get() , query() и save() в контроллере:
angular.module('myApp.controllers',[]); angular.module('myApp.controllers').controller('ResourceController',function($scope,Entry){var entry =Entry.get({ id: $scope.id},function(){console.log(entry);});// get() returns a single entry var entries = Entry.query(function() { console.log(entries); }); //query() returns all the entries $scope.entry = new Entry(); //You can instantiate resource class $scope.entry.data = 'some data'; Entry.save($scope.entry, function() { //data saved. do something here. }); //saves an entry. Assuming $scope.entry is the Entry object });
Функция get() в приведенном выше фрагменте кода отправляет запрос GET в /api/entries/:id . Параметр :id в URL-адресе заменяется на $scope.id . Следует также отметить, что функция get() возвращает пустой объект, который заполняется автоматически, когда фактические данные поступают с сервера. Второй аргумент get() — это обратный вызов, который выполняется, когда данные поступают с сервера. Это полезный прием, потому что вы можете установить пустой объект, возвращаемый get() в $scope и ссылаться на него в представлении. Когда поступают фактические данные и объект заполняется, привязка данных включается, и ваше представление также обновляется.
Функция query() выдает GET-запрос к /api/entries (обратите внимание, что id отсутствует) и возвращает пустой массив. Этот массив заполняется, когда данные поступают с сервера. Опять же, вы можете установить этот массив как модель $scope и ссылаться на него в представлении, используя ng-repeat . Вы также можете передать обратный вызов в query() который вызывается, когда данные поступают с сервера.
Функция save() отправляет POST-запрос в /api/entries с первым аргументом в качестве тела сообщения. Второй аргумент — это обратный вызов, который вызывается при сохранении данных. Возможно, вы помните, что возвращаемое значение функции $resource() является классом ресурсов. Таким образом, в нашем случае мы можем вызвать new Entry() чтобы создать экземпляр реального объекта из этого класса, установить для него различные свойства и, наконец, сохранить объект в бэкэнд.
В идеале вы должны использовать get() и query() только для класса ресурсов (в нашем случае это Entry ). Все не GET-методы, такие как save() и delete() , также доступны в экземпляре, полученном вызовом new Entry() (назовите его экземпляром $resource ). Но разница в том, что эти методы имеют префикс $ . Итак, методы, доступные в экземпляре $resource (в отличие от класса $resource ):
$save()
$delete()
$remove()
Например, метод $save() используется следующим образом:
$scope.entry=newEntry();//this object now has a $save() method $scope.entry.$save(function() { //data saved. $scope.entry is sent as the post body. });
Мы исследовали создание, чтение и удаление частей CRUD. Осталось только обновить. Чтобы поддержать операцию обновления, нам нужно изменить нашу собственную фабричную Entity как показано ниже.
angular.module('myApp.services').factory('Entry',function($resource){return$resource('/api/entries/:id',{ id:'@_id'},{ update:{ method:'PUT'// this method issues a PUT request } }); });
Второй аргумент $resource() — это хеш, указывающий, каким должно быть значение параметра :id в URL. Установка его в @_id означает, что всякий раз, когда мы будем вызывать такие методы, как $update() и $delete() для экземпляра ресурса, значение :id будет установлено в свойство _id экземпляра. Это полезно для запросов PUT и DELETE. Также обратите внимание на третий аргумент. Это хеш, который позволяет нам добавлять любые пользовательские методы в класс ресурсов. Если метод выдает запрос не GET, он становится доступным для экземпляра $resource с префиксом $ . Итак, давайте посмотрим, как использовать наш метод $update . Предполагая, что мы находимся в контроллере:
$scope.entry=Movie.get({ id: $scope.id},function(){// $scope.entry is fetched from server and is an instance of Entry $scope.entry.data = 'something else'; $scope.entry.$update(function() { //updated in the backend }); });
Когда вызывается функция $update() , она делает следующее:
AngularJS знает, что функция $update() будет инициировать запрос PUT на URL /api/entries/:id .
Он читает значение $scope.entry._id , присваивает значение :id и генерирует URL.
Посылает запрос PUT на URL с $scope.entry в качестве тела сообщения.
Точно так же, если вы хотите удалить запись, это можно сделать следующим образом:
$scope.entry=Movie.get({ id: $scope.id},function(){// $scope.entry is fetched from server and is an instance of Entry $scope.entry.data = 'something else'; $scope.entry.$delete(function() { //gone forever! }); });
Он выполняет те же действия, что и выше, за исключением того, что тип запроса — DELETE вместо PUT.
Мы покрыли все операции в CRUD, но остались с небольшой вещью. Функция $resource также имеет необязательный четвертый параметр. Это хеш с пользовательскими настройками. В настоящее время доступен только один параметр — stripTrailingSlashes . По умолчанию для этого параметра установлено значение true , что означает, что конечные слеши будут удалены из URL-адреса, который вы передаете $resource() . Если вы хотите отключить это, вы можете сделать это так:
angular.module('myApp.services').factory('Entry',function($resource){return$resource('/api/entries/:id',{ id:'@_id'},{ update:{ method:'PUT'// this method issues a PUT request } }, { stripTrailingSlashes: false }); });
Между прочим, я не охватил все до $resource . Здесь мы рассмотрели основы, которые помогут вам легко начать работу с приложениями CRUD. Если вы хотите подробно изучить $resource , вы можете просмотреть документацию .
Создание приложения для фильма
Чтобы укрепить концепцию $resource давайте создадим приложение для любителей кино. Это будет SPA, где пользователи могут добавить новый фильм в нашу базу данных, обновить существующий фильм и, наконец, удалить его. Мы будем использовать $resource для взаимодействия с REST API. Вы можете проверить живую демонстрацию того, что мы собираемся построить здесь .
Просто обратите внимание, что API, который я создал, поддерживает CORS, поэтому вы можете создать приложение Angular отдельно и использовать URL-адрес http://movieapp-sitepointdemos.rhcloud.com/ в качестве API. Вы можете разработать приложение Angular и поэкспериментировать с ним, не беспокоясь о бэкенде.
Наш API
Я создал RESTful бэкэнд, используя Node и Express. Посмотрите на следующий скриншот, чтобы узнать API.
Структура каталогов
Давайте начнем со следующей структуры каталогов для нашего приложения AngularJS:
Просто отметьте, что мы будем использовать Angular UI Router для маршрутизации.
Создание нашего сервиса для взаимодействия с конечными точками REST
Как обсуждалось в предыдущих разделах, мы создадим пользовательский сервис, который будет использовать $resource для взаимодействия с REST API. Служба определена в js/services.js .
Название нашей фабрики — Movie . Поскольку мы используем MongoDB, у каждого экземпляра фильма есть свойство с именем _id . Все остальное просто и понятно.
Теперь, когда наш сервис готов, давайте создадим представления и контроллеры.
index.html : создание страницы входа в приложение
index.html — это точка входа в наше приложение. Для начала нам нужно включить все необходимые скрипты и таблицы стилей на этой странице. Мы будем использовать Bootstrap для быстрого создания макета. Вот содержание index.html .
<!DOCTYPE html><htmldata-ng-app="movieApp"><headlang="en"><metacharset="utf-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1"><basehref="/"/><title>The Movie App</title><linkrel="stylesheet"type="text/css"href="css/bootstrap.min.css"/><linkrel="stylesheet"type="text/css"href="css/app.css"/></head><body><navclass="navbar navbar-default"role="navigation"><divclass="container-fluid"><divclass="navbar-header"><aclass="navbar-brand"ui-sref="movies">The Movie App</a></div><divclass="collapse navbar-collapse"><ulclass="nav navbar-nav"><liclass="active"><aui-sref="movies">Home</a></li></ul></div></div></nav><divclass="container"><divclass="row top-buffer"><divclass="col-xs-8 col-xs-offset-2"><divui-view></div><!-- This is where our views will load --></div></div></div><scripttype="text/javascript"src="lib/angular.min.js"></script><scripttype="text/javascript"src="js/app.js"></script><scripttype="text/javascript"src="js/controllers.js"></script><scripttype="text/javascript"src="js/services.js"></script><scripttype="text/javascript"src="lib/angular-ui-router.min.js"></script><scripttype="text/javascript"src="lib/angular-resource.min.js"></script></body></html>
Разметка довольно понятна. Просто обратите особое внимание на <div ui-view></div> . Директива ui-view исходит из модуля UI Router и действует как контейнер для наших представлений.
Создание основного модуля и состояний
Наш основной модуль и состояния определены в js/app.js :
app.js:
angular.module('movieApp',['ui.router','ngResource','movieApp.controllers','movieApp.services']); angular.module('movieApp').config(function($stateProvider){ $stateProvider.state('movies',{// state for showing all movies url: '/movies', templateUrl: 'partials/movies.html', controller: 'MovieListController' }).state('viewMovie', { //state for showing single movie url: '/movies/:id/view', templateUrl: 'partials/movie-view.html', controller: 'MovieViewController' }).state('newMovie', { //state for adding a new movie url: '/movies/new', templateUrl: 'partials/movie-add.html', controller: 'MovieCreateController' }).state('editMovie', { //state for updating a movie url: '/movies/:id/edit', templateUrl: 'partials/movie-edit.html', controller: 'MovieEditController' }); }).run(function($state) { $state.go('movies'); //make a transition to movies state when app starts });
Итак, наше приложение имеет следующие четыре состояния:
movies
viewMovie
newMovie
editMovie
Каждое состояние состоит из url , templateUrl и controller . Также обратите внимание, что когда наш основной модуль загружен, мы осуществляем переход к movies о состоянии, показывая все фильмы в нашей системе. Посмотрите на следующий снимок экрана, чтобы узнать, какому состоянию соответствует какой URL.
Создание шаблонов
Все наши шаблоны находятся внутри partials . Давайте посмотрим, что делает каждый из них!
_form.html:
_form.html содержит простую форму, которая позволяет пользователям вводить данные. Обратите внимание, что эта форма будет включена в movie-add.html и movie-edit.html потому что они оба принимают входные данные от пользователей.
Вот содержимое _form.html :
<divclass="form-group"><labelfor="title"class="col-sm-2 control-label">Title</label><divclass="col-sm-10"><inputtype="text"ng-model="movie.title"class="form-control"id="title"placeholder="Movie Title Here"/></div></div><divclass="form-group"><labelfor="year"class="col-sm-2 control-label">Release Year</label><divclass="col-sm-10"><inputtype="text"ng-model="movie.releaseYear"class="form-control"id="year"placeholder="When was the movie released?"/></div></div><divclass="form-group"><labelfor="director"class="col-sm-2 control-label">Director</label><divclass="col-sm-10"><inputtype="text"ng-model="movie.director"class="form-control"id="director"placeholder="Who directed the movie?"/></div></div><divclass="form-group"><labelfor="plot"class="col-sm-2 control-label">Movie Genre</label><divclass="col-sm-10"><inputtype="text"ng-model="movie.genre"class="form-control"id="plot"placeholder="Movie genre here"/></div></div><divclass="form-group"><divclass="col-sm-offset-2 col-sm-10"><inputtype="submit"class="btn btn-primary"value="Save"/></div></div>
Шаблон использует ng-model для привязки различных деталей фильма к различным свойствам модели scope .
movie-add.html
Этот шаблон используется для приема пользовательских данных и добавления нового фильма в нашу систему. Вот содержание:
Как только форма отправлена, updateMovie() функция области updateMovie() которая updateMovie() PUT-запрос на сервер для обновления фильма.
movie-view.html:
Этот шаблон используется для отображения подробностей об одном фильме. Содержание выглядит следующим образом:
<tableclass="table movietable"><tr><td><h3>Details for {{movie.title}}</h3></td><td></td></tr><tr><td>Movie Title</td><td>{{movie.title}}</td></tr><tr><td>Director</td><td>{{movie.director}}</td></tr><tr><td>Release Year</td><td>{{movie.releaseYear}}</td></tr><tr><td>Movie Genre</td><td>{{movie.genre}}</td></tr></table><div><aclass="btn btn-primary"ui-sref="editMovie({id:movie._id})">Edit</a></div>
В конце есть кнопка редактирования. После нажатия он меняет состояние на editMovie с id фильма в $stateParams .
movies.html
Этот шаблон отображает все фильмы в системе.
<aui-sref="newMovie"class="btn-primary btn-lg nodecoration">Add New Movie</a><tableclass="table movietable"><tr><td><h3>All Movies</h3></td><td></td></tr><trng-repeat="movie in movies"><td>{{movie.title}}</td><td><aclass="btn btn-primary"ui-sref="viewMovie({id:movie._id})">View</a><aclass="btn btn-danger"ng-click="deleteMovie(movie)">Delete</a></td></tr></table>
Он просматривает все объекты movie полученные из API, и отображает детали. Также есть кнопка « Add New Movie которая изменяет состояние на « newMovie . В результате загружается новый маршрут, и мы можем создать новую запись в фильме.
Для каждого фильма есть две кнопки, View и Delete .View запускает изменение состояния, так что отображаются подробные сведения о фильме. Кнопка Delete удаляет фильм навсегда.
Создание контроллеров
У каждого государства есть контроллер. Итак, всего у нас есть четыре контроллера для четырех состояний. Все контроллеры идут в js/controllers.js . Контроллеры просто используют наш пользовательский сервис Movie и работают так, как мы обсуждали выше. Итак, вот как выглядят наши контроллеры.
controllers.js:
angular.module('movieApp.controllers',[]).controller('MovieListController',function($scope, $state, popupService, $window,Movie){ $scope.movies=Movie.query();//fetch all movies. Issues a GET to /api/movies $scope.deleteMovie = function(movie) { // Delete a movie. Issues a DELETE to /api/movies/:id if (popupService.showPopup('Really delete this?')) { movie.$delete(function() { $window.location.href = ''; //redirect to home }); } }; }).controller('MovieViewController', function($scope, $stateParams, Movie) { $scope.movie = Movie.get({ id: $stateParams.id }); //Get a single movie.Issues a GET to /api/movies/:id }).controller('MovieCreateController', function($scope, $state, $stateParams, Movie) { $scope.movie = new Movie(); //create new movie instance. Properties will be set via ng-model on UI $scope.addMovie = function() { //create a new movie. Issues a POST to /api/movies $scope.movie.$save(function() { $state.go('movies'); // on success go back to home ie movies state. }); }; }).controller('MovieEditController', function($scope, $state, $stateParams, Movie) { $scope.updateMovie = function() { //Update the edited movie. Issues a PUT to /api/movies/:id $scope.movie.$update(function() { $state.go('movies'); // on success go back to home ie movies state. }); }; $scope.loadMovie = function() { //Issues a GET request to /api/movies/:id to get a movie to update $scope.movie = Movie.get({ id: $stateParams.id }); }; $scope.loadMovie(); // Load a movie which can be edited on UI });
Вывод
Предполагая, что приложение развернуто в localhost/movieApp , вы можете получить к нему доступ по адресу http://localhost/movieApp/index.html . Если вы любитель кино, вы можете начать добавлять свои любимые фильмы тоже! Исходный код приложения, разработанного в этой статье, доступен для скачивания на GitHub .
Я надеюсь, вам понравился этот урок! Не стесняйтесь комментировать, если вы хотите что-то добавить.