Статьи

MEAN Stack: создание приложения с Angular 2+ и угловым интерфейсом командной строки

На онлайн-курсах Angular, проводимых экспертами, нельзя пройти мимо Ultimate Angular от Todd Motto. Попробуйте его курсы здесь и используйте код SITEPOINT, чтобы получить скидку 25% и помочь в поддержке SitePoint.

Стек MEAN содержит передовые технологии, используемые для разработки как серверной, так и клиентской части веб-приложения в среде JavaScript. Компоненты стека MEAN включают базу данных MongoDB, Express.js (веб-инфраструктуру), Angular (интерфейсную среду) и среду выполнения Node.js. Взяв под свой контроль стек MEAN и ознакомившись с различными технологиями JavaScript, вы сможете стать полноценным JavaScript-разработчиком.

Сфера влияния JavaScript значительно выросла за эти годы, и с этим ростом существует постоянное желание идти в ногу с последними тенденциями в программировании. Появились новые технологии, и существующие технологии были переписаны с нуля (я смотрю на вас, Angular).

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

Введение в MEAN Stack

Node.jsNode.js — это среда выполнения на стороне сервера, созданная на основе движка Chrome V8 JavaScript. Node.js основан на управляемой событиями архитектуре, которая работает на одном потоке и неблокирующем вводе-выводе. Эти варианты дизайна позволяют создавать веб-приложения в реальном времени на JavaScript, которые хорошо масштабируются.

Express.jsExpress — это минималистичная, но надежная платформа веб-приложений для Node.js. Express.js использует функции промежуточного программного обеспечения для обработки HTTP-запросов, а затем либо возвращает ответ, либо передает параметры другому промежуточному программному обеспечению. Промежуточное программное обеспечение уровня приложения, уровня маршрутизатора и обработки ошибок доступно в Express.js.

MongoDBMongoDB — это ориентированная на документы программа базы данных, в которой документы хранятся в гибком JSON-подобном формате. Будучи программой базы данных NoSQL, MongoDB освобождает вас от табличного жаргона реляционной базы данных.

AngularAngular — это платформа приложений, разработанная Google для создания интерактивных одностраничных приложений. Angular, первоначально AngularJS, был переписан с нуля, чтобы перейти на архитектуру на основе компонентов со старой платформы MVC. Angular рекомендует использовать TypeScript, который, на мой взгляд, является хорошей идеей, поскольку он улучшает рабочий процесс разработки.

Теперь, когда мы познакомились с кусочками MEAN-головоломки, давайте посмотрим, как мы можем их совместить, не так ли?

обзор

Вот общий обзор нашего приложения.

Общий обзор нашего стекового приложения MEAN

Мы будем создавать Awesome Bucket List Application с нуля без использования шаблонного шаблона. Внешний интерфейс будет включать в себя форму, которая принимает ваши элементы списка сегментов, и представление, которое обновляет и отображает весь список сегментов в режиме реального времени.

Любое обновление представления будет интерпретировано как событие, и это инициирует HTTP-запрос. Сервер обработает запрос, при необходимости обновит / извлечет MongoDB, а затем вернет объект JSON. Внешний интерфейс будет использовать это для обновления нашего представления. К концу этого урока у вас должно появиться приложение со списком корзин, которое выглядит следующим образом.

Снимок экрана приложения со списком ведра, которое мы собираемся создать

Весь код приложения Bucket List доступен на GitHub .

Предпосылки

Перво-наперво, для начала вам необходимо установить Node.js и MongoDB. Если вы новичок в Node, я бы порекомендовал прочитать Руководство для начинающих по Node, чтобы все началось. Аналогично, настройка MongoDB проста, и вы можете проверить их документацию для инструкций по установке, специфичных для вашей платформы.

$ node -v # v8.0.0 

Запустите mongo daemon с помощью команды.

 sudo service mongod start 

Чтобы установить последнюю версию Angular, я бы порекомендовал использовать Angular CLI. Он предлагает все необходимое для создания и развертывания приложения Angular. Если вы еще не знакомы с Angular CLI, обязательно ознакомьтесь с The Ultimate Angular CLI Reference .

 npm install -g @angular/cli 

Создайте новый каталог для нашего проекта списка корзин. Вот куда пойдут и код передний, и внутренний.

 mkdir awesome-bucketlist cd awesome-bucketlist 

Создание бэкэнда с использованием Express.js и MongoDB

Express не накладывает никаких структурных ограничений на ваше веб-приложение. Вы можете поместить весь код приложения в один файл и заставить его работать теоретически. Тем не менее, ваша кодовая база была бы полным беспорядком. Вместо этого мы собираемся сделать это способом MVC (Модель, Представление и Контроллер) — минус часть вида.

MVC — это архитектурный паттерн, который отделяет ваши модели (серверная часть) и представления (пользовательский интерфейс) от контроллера (все, что находится между ними), следовательно, MVC. Поскольку Angular позаботится о внешнем интерфейсе для нас, у нас будет три каталога, один для моделей, другой для контроллеров, и общедоступный каталог, в который мы поместим скомпилированный угловой код.

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

Структура каталогов MEAN-стека

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

Инициализация npm

Нам не хватает файла package.json для нашего бэкенда. Введите npm init и после того, как вы ответите на вопросы, у вас должен быть создан package.json для вас.

Мы объявим наши зависимости внутри файла package.json . Для этого проекта нам понадобятся следующие модули:

  • Экспресс : Экспресс модуль для веб-сервера
  • Мангуст : популярная библиотека для MongoDB
  • bodyparser : анализирует тело входящих запросов и делает его доступным в req.body.
  • cors : промежуточное программное обеспечение CORS обеспечивает контроль доступа к нашему веб-серверу.

Я также добавил скрипт запуска, чтобы мы могли запустить наш сервер, используя npm start .

 { "name": "awesome-bucketlist", "version": "1.0.0", "description": "A simple bucketlist app using MEAN stack", "main": "app.js", "scripts": { "start": "node app" }, //The ~ is used to match the most recent minor version (without any breaking changes) "dependencies": { "express": "~4.15.3", "mongoose": "~4.11.0", "cors": "~2.8.3", "body-parser": "~1.17.2" }, "author": "", "license": "ISC" } 

Теперь запустите npm install и он позаботится об установке зависимостей.

Заполнение app.js

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

 // We'll declare all our dependencies here const express = require('express'); const path = require('path'); const bodyParser = require('body-parser'); const cors = require('cors'); const mongoose = require('mongoose'); //Initialize our app variable const app = express(); //Declaring Port const port = 3000; 

Как видите, мы также инициализировали переменную app и объявили номер порта. Объект приложения создается при создании веб-сервера Express. Теперь мы можем загрузить промежуточное ПО на наш сервер Express, указав его с помощью app.use() .

 //Middleware for CORS app.use(cors()); //Middleware for bodyparsing using both json and urlencoding app.use(bodyParser.urlencoded({extended:true})); app.use(bodyParser.json()); /*express.static is a built in middleware function to serve static files. We are telling express server public folder is the place to look for the static files */ app.use(express.static(path.join(__dirname, 'public'))); 

Объект app тоже может понимать маршруты.

 app.get('/', (req,res) => { res.send("Invalid page"); }) 

Здесь метод get вызываемый в приложении, соответствует методу GET HTTP. Он принимает два параметра, первый из которых представляет собой путь или маршрут, для которого должна применяться функция промежуточного программного обеспечения.

Второй — это собственно промежуточное программное обеспечение, которое обычно принимает три аргумента: аргумент req соответствует HTTP-запросу; аргумент res соответствует HTTP-ответу; next — необязательный аргумент обратного вызова, который следует вызывать, если после этого есть другие промежуточные программы. Мы не использовали next здесь, так как res.send() завершает цикл запрос-ответ.

Добавьте эту строку в конец, чтобы наше приложение прослушивало порт, который мы объявили ранее.

 //Listen to port 3000 app.listen(port, () => { console.log(`Starting the server at port ${port}`); }); 

npm start должен запустить наш основной сервер.

По умолчанию npm не отслеживает ваши файлы / каталоги на предмет каких-либо изменений, и вам придется вручную перезагружать сервер каждый раз, когда вы обновляете свой код. Я рекомендую использовать nodemon для отслеживания ваших файлов и автоматического перезапуска сервера при обнаружении любых изменений. Если вы не укажете явно, какой скрипт запускать, nodemon запустит файл, связанный с основным свойством в вашем package.json .

 npm install -g nodemon nodemon 

Мы почти закончили с нашим файлом app.js Что осталось сделать? Мы должны

  1. подключить наш сервер к базе данных
  2. создайте контроллер, который мы можем затем импортировать в наш app.js

Настройка мангуста

Настройка и подключение базы данных просты с MongoDB. Сначала создайте каталог config и файл с именем database.js для хранения наших данных конфигурации. Экспортируйте URI базы данных, используя module.exports .

 // 27017 is the default port number. module.exports = { database: 'mongodb://localhost:27017/bucketlist' } 

И установите соединение с базой данных в app.js используя mongoose.connect() .

 // Connect mongoose to our database const config = require('./config/database'); mongoose.connect(config.database); 

«Но как насчет создания базы данных списков ведра?», Спросите вы. База данных будет создана автоматически при вставке документа в новую коллекцию в этой базе данных.

Работа над контроллером и моделью

Теперь давайте перейдем к созданию нашего контроллера списка блоков. Создайте файл bucketlist.js внутри каталога контроллера . Нам также нужно /bucketlist запросы /bucketlist к нашему контроллеру bucketlist (в app.js ).

 const bucketlist = require('./controllers/bucketlist'); //Routing all HTTP requests to /bucketlist to bucketlist controller app.use('/bucketlist',bucketlist); 

Вот окончательная версия нашего файла app.js.

 // We'll declare all our dependencies here const express = require('express'); const path = require('path'); const bodyParser = require('body-parser'); const cors = require('cors'); const mongoose = require('mongoose'); const config = require('./config/database'); const bucketlist = require('./controllers/bucketlist'); //Connect mongoose to our database mongoose.connect(config.database); //Declaring Port const port = 3000; //Initialize our app variable const app = express(); //Middleware for CORS app.use(cors()); //Middlewares for bodyparsing using both json and urlencoding app.use(bodyParser.urlencoded({extended:true})); app.use(bodyParser.json()); /*express.static is a built in middleware function to serve static files. We are telling express server public folder is the place to look for the static files */ app.use(express.static(path.join(__dirname, 'public'))); app.get('/', (req,res) => { res.send("Invalid page"); }) //Routing all HTTP requests to /bucketlist to bucketlist controller app.use('/bucketlist',bucketlist); //Listen to port 3000 app.listen(port, () => { console.log(`Starting the server at port ${port}`); }); 

Как уже отмечалось в обзоре, наше удивительное приложение со списком корзин будет иметь маршруты для обработки HTTP-запросов с помощью методов GET, POST и DELETE. Вот простой контроллер с маршрутами, определенными для методов GET, POST и DELETE.

 //Require the express package and use express.Router() const express = require('express'); const router = express.Router(); //GET HTTP method to /bucketlist router.get('/',(req,res) => { res.send("GET"); }); //POST HTTP method to /bucketlist router.post('/', (req,res,next) => { res.send("POST"); }); //DELETE HTTP method to /bucketlist. Here, we pass in a params which is the object id. router.delete('/:id', (req,res,next)=> { res.send("DELETE"); }) module.exports = router; 

Я бы порекомендовал использовать приложение Postman или что-то подобное для тестирования API вашего сервера. У Postman есть мощная платформа с графическим интерфейсом, которая делает разработку API быстрее и проще. Попробуйте запрос GET на http: // localhost: 3000 / bucketlist и посмотрите, получите ли вы ожидаемый ответ.

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

Создайте модель list.js для нашего приложения и определите схему списка блоков следующим образом:

 //Require mongoose package const mongoose = require('mongoose'); //Define BucketlistSchema with title, description and category const BucketlistSchema = mongoose.Schema({ title: { type: String, required: true }, description: String, category: { type: String, required: true, enum: ['High', 'Medium', 'Low'] } }); 

При работе с mongoose вы должны сначала определить схему. Мы определили BucketlistSchema с тремя различными ключами (заголовок, категория и описание). Каждый ключ и связанный с ним SchemaType определяет свойство в нашем документе MongoDB. Если вас интересует отсутствие поля id , это потому, что мы будем использовать _id по умолчанию, который будет создан Mongoose.

Mongoose присваивает каждой вашей _id поле _id по умолчанию, если оно не передается в конструктор схемы. Назначенный тип — это ObjectId, совпадающий с поведением MongoDB по умолчанию.

Вы можете прочитать больше об этом в документации Mongoose

Однако, чтобы использовать наше определение схемы, нам нужно преобразовать нашу BucketlistSchema в модель и экспортировать ее с помощью module.exports. Первый аргумент mongoose.model — это имя коллекции, которая будет использоваться для хранения данных в MongoDB.

 const BucketList = module.exports = mongoose.model('BucketList', BucketlistSchema ); 

Помимо схемы, мы также можем размещать запросы к базе данных внутри нашей модели BucketList и экспортировать их как методы.

 //BucketList.find() returns all the lists module.exports.getAllLists = (callback) => { BucketList.find(callback); } 

Здесь мы BucketList.find метод BucketList.find который запрашивает базу данных и возвращает коллекцию BucketList. Поскольку используется функция обратного вызова, результат будет передан обратному вызову.

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

 const bucketlist = require('../models/List'); //GET HTTP method to /bucketlist router.get('/',(req,res) => { bucketlist.getAllLists((err, lists)=> { if(err) { res.json({success:false, message: `Failed to load all lists. Error: ${err}`}); } else { res.write(JSON.stringify({success: true, lists:lists},null,2)); res.end(); } }); }); 

Мы getAllLists метод getAllLists и обратный вызов принимает два аргумента, error и result.

Все обратные вызовы в Mongoose используют шаблон: обратный вызов (ошибка, результат). Если при выполнении запроса возникает ошибка, параметр error будет содержать документ об ошибке, и результат будет нулевым. Если запрос выполнен успешно, параметр ошибки будет нулевым, а результат будет заполнен результатами запроса.

— Документация MongoDB

Аналогично, давайте добавим методы для вставки нового списка и удаления существующего списка из нашей модели.

 //newList.save is used to insert the document into MongoDB module.exports.addList = (newList, callback) => { newList.save(callback); } //Here we need to pass an id parameter to BUcketList.remove module.exports.deleteListById = (id, callback) => { let query = {_id: id}; BucketList.remove(query, callback); } 

Теперь нам нужно обновить промежуточное ПО нашего контроллера для POST и DELETE.

 //POST HTTP method to /bucketlist router.post('/', (req,res,next) => { let newList = new bucketlist({ title: req.body.title, description: req.body.description, category: req.body.category }); bucketlist.addList(newList,(err, list) => { if(err) { res.json({success: false, message: `Failed to create a new list. Error: ${err}`}); } else res.json({success:true, message: "Added successfully."}); }); }); //DELETE HTTP method to /bucketlist. Here, we pass in a param which is the object id. router.delete('/:id', (req,res,next)=> { //access the parameter which is the id of the item to be deleted let id = req.params.id; //Call the model method deleteListById bucketlist.deleteListById(id,(err,list) => { if(err) { res.json({success:false, message: `Failed to delete the list. Error: ${err}`}); } else if(list) { res.json({success:true, message: "Deleted successfully"}); } else res.json({success:false}); }) }); 

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

HTTP-запрос POST с использованием Postman

Теперь перейдем к интерфейсу приложения с помощью Angular.

Сборка внешнего интерфейса с использованием угловых

Давайте сгенерируем внешнее приложение Angular, используя инструмент Angular CLI, который мы настроили ранее. Мы назовем его angular-src и поместим в каталог awesome-bucketlist.

 ng new angular-src 

Теперь у нас есть вся структура Angular 2 внутри нашего каталога awesome-bucketlist. .angular-cli.json к .angular-cli.json и измените «outDir» на «../public».

В следующий раз, когда вы запустите ng build что мы и сделаем ближе к концу этого урока, Angular скомпилирует весь наш интерфейс и поместит его в публичный каталог. Таким образом, сервер Express и внешний интерфейс будут работать на одном и том же порту.

Но на данный момент, ng serve — это то, что нам нужно. Вы можете проверить шаблон приложения Angular по адресу http: // localhost: 4200 .

Структура каталогов нашего приложения Angular выглядит немного сложнее, чем структура каталогов нашего сервера. Тем не менее, 90% времени мы будем работать в каталоге src / app / . Это будет наше рабочее пространство, и все наши компоненты, модели и службы будут размещены в этом каталоге. Давайте посмотрим, как наш интерфейс будет структурирован к концу этого урока.

Угловая архитектура приложения MEAN

Создание компонентов, модели и сервиса

Давайте рассмотрим пошаговый подход к написанию нашего Angular-приложения. Мы должны:

  1. создать два новых компонента с AddListComponent ViewListComponent и AddListComponent
  2. создайте модель для нашего List , которую затем можно будет импортировать в наши компоненты и сервисы
  3. создать сервис, который может обрабатывать все HTTP-запросы к серверу
  4. обновите AppModule с нашими компонентами, сервисом и другими модулями, которые могут быть необходимы для этого приложения.

Вы можете создавать компоненты, используя команду ng generate component .

 ng generate component AddList ng generate component ViewList 

Теперь вы должны увидеть два новых каталога в папке src / app , по одному для наших вновь созданных компонентов. Далее нам нужно создать сервис для нашего List .

 ng generate service List 

Я предпочитаю, чтобы мои службы находились в новом каталоге (внутри src/app/ ).

 mkdir services mv list.service.ts services/ 

Поскольку мы изменили расположение list.service.ts , нам нужно обновить его в нашем AppModule . Короче говоря, AppModule — это место, где мы объявляем все наши компоненты, сервисы и другие модули.

Команда generate уже добавила наши компоненты в appModule . Идем дальше и импортируем ListService и добавляем его в массив providers . Нам также нужно импортировать FormsModule и HTTPModule и объявить их как импортные. FormsModule необходим для создания формы для нашего приложения и HTTPModule для отправки HTTP-запросов на сервер.

 import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpModule } from '@angular/http'; import { FormsModule} from '@angular/forms'; import { AppComponent } from './app.component'; import { AddListComponent } from './add-list/add-list.component'; import { ViewListComponent } from './view-list/view-list.component'; import { ListService } from './services/list.service'; @NgModule({ declarations: [ AppComponent, AddListComponent, ViewListComponent ], //Modules go here imports: [ BrowserModule, HttpModule, FormsModule ], //All the services go here providers: [ListService], bootstrap: [AppComponent] }) export class AppModule { } 

Теперь мы в состоянии начать работу с нашими компонентами. Компоненты являются строительными блоками в приложении Angular 2. AppComponent — это компонент по умолчанию, созданный Angular. Каждый компонент состоит из:

  • класс TypeScript, который содержит логику компонента
  • HTML-файл и таблица стилей, которые определяют интерфейс компонента
  • декоратор @Component , который используется для определения метаданных компонента.

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

Обзор иерархии компонентов в нашем приложении

Теперь у нас есть иерархия компонентов — AppComponent вверху, затем ViewListComponent и затем AddListComponent .

 /*app.component.html*/ <!--The whole content below can be removed with the new code.--> <div style="text-align:center"> <h1> {{title}}! </h1> <app-view-list> </app-view-list> </div> 
 /*view-list.component.html*/ <app-add-list> </app-add-list> 

Создайте файл с именем List.ts в каталоге моделей . Здесь мы будем хранить модель для нашего List .

 /* List.ts */ export interface List { _id?: string; title: string; description: string; category: string; } 

Компонент просмотра списка

Логика компонента ViewListComponent ‘включает в себя:

  1. List свойство, которое является массивом типа List . Он поддерживает копию списков, полученных с сервера. Используя методы привязки Angular, свойства компонента доступны внутри шаблона.
  2. loadLists() загружает все списки с сервера. Здесь мы вызываем this.ListSev.getAllLists() и subscribe на него. getAllLists() — это сервисный метод (мы его еще не определили), который выполняет фактический запрос http.get и возвращает список; loadLists() затем загружает его в свойство списка Компонента.
  3. deleteList(list) обрабатывает процедуру удаления, когда пользователь нажимает кнопку « Удалить» . Мы будем вызывать метод deleteList службы deleteList с id в качестве аргумента. Когда сервер отвечает, что удаление loadLists() успешно, мы вызываем метод loadLists() для обновления нашего представления.
 /*view-list.component.ts*/ import { Component, OnInit } from '@angular/core'; import { ListService } from '../services/list.service'; import { List } from '../models/List' @Component({ selector: 'app-view-list', templateUrl: './view-list.component.html', styleUrls: ['./view-list.component.css'] }) export class ViewListComponent implements OnInit { //lists propoerty which is an array of List type private lists: List[] = []; constructor(private listServ: ListService) { } ngOnInit() { //Load all list on init this.loadLists(); } public loadLists() { //Get all lists from server and update the lists property this.listServ.getAllLists().subscribe( response => this.lists = response,) } //deleteList. The deleted list is being filtered out using the .filter method public deleteList(list: List) { this.listServ.deleteList(list._id).subscribe( response => this.lists = this.lists.filter(lists => lists !== list),) } } 

Шаблон ( view-list.component.html ) должен иметь следующий код:

  <h2> Awesome Bucketlist App </h2> <!-- Table starts here --> <table id="table"> <thead> <tr> <th>Priority Level</th> <th>Title</th> <th>Description</th> <th> Delete </th> </tr> </thead> <tbody> <tr *ngFor="let list of lists"> <td>{{list.category}}</td> <td>{{list.title}}</td> <td>{{list.description}}</td> <td> <button type="button" (click)="deleteList(list); $event.stopPropagation();">Delete</button></td> </tr> </tbody> </table> <app-add-list> </app-add-list> 

Мы создали таблицу для отображения наших списков. Там есть немного необычного кода, который не является частью стандартного HTML. Angular имеет богатый синтаксис шаблона, который добавляет немного изюминки в ваши обычные HTML-файлы. Следующее является частью синтаксиса углового шаблона.

  • Директива *ngFor позволяет вам просматривать свойство lists .
  • Здесь list является переменной шаблона, тогда как lists является свойством компонента.
  • Затем мы использовали синтаксис интерполяции Angular {{ }} чтобы связать свойство компонента с нашим шаблоном.
  • Синтаксис привязки к событию используется для привязки события click к deleteList() .

Мы близки к тому, чтобы иметь работающее приложение со списком ведра. В настоящее время наш list.service.ts пуст, и нам нужно заполнить его, чтобы наше приложение работало. Как было отмечено ранее, у сервисов есть методы, которые связываются с сервером.

 /*list.service.ts*/ import { Injectable } from '@angular/core'; import { Http,Headers } from '@angular/http'; import {Observable} from 'rxjs/Observable'; import { List } from '../models/List' import 'rxjs/add/operator/map'; @Injectable() export class ListService { constructor(private http: Http) { } private serverApi= 'http://localhost:3000'; public getAllLists():Observable<List[]> { let URI = `${this.serverApi}/bucketlist/`; return this.http.get(URI) .map(res => res.json()) .map(res => <List[]>res.lists); } public deleteList(listId : string) { let URI = `${this.serverApi}/bucketlist/${listId}`; let headers = new Headers; headers.append('Content-Type', 'application/json'); return this.http.delete(URI, {headers}) .map(res => res.json()); } } 

Базовый процесс довольно прост для обоих методов:

  1. мы строим URL на основе адреса нашего сервера
  2. мы создаем новые заголовки и добавляем их с помощью { Content-Type: application/json }
  3. мы выполняем фактический http.get/http.delete по URL
  4. Мы преобразуем ответ в формате json .

Если вы не знакомы со службами написания, которые взаимодействуют с сервером, я бы порекомендовал прочитать учебник по Angular и RxJS: создание службы API для связи с REST Backend .

Зайдите на http: // localhost: 4200 /, чтобы убедиться, что приложение работает. Он должен иметь таблицу, в которой отображаются все списки, которые мы создали ранее.

Компонент Add-List

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

AddListComponent — это место, куда мы поместим код для нашей HTML-формы.

 <div class="container"> <form (ngSubmit)="onSubmit()"> <div> <label for="title">Title</label> <input type="text" [(ngModel)]="newList.title" name="title" required> </div> <div> <label for="category">Select Category</label> <select [(ngModel)]="newList.category" name = "category" > <option value="High">High Priority</option> <option value="Medium">Medium Priority</option> <option value="Low">Low Prioirty</option> </select> </div> <div> <label for="description">description</label> <input type="text" [(ngModel)]="newList.description" name="description" required> </div> <button type="submit">Submit</button> </form> </div> 

Внутри нашего шаблона вы можете увидеть несколько примеров использования [(ngModel)] . Странно выглядящий синтаксис — это директива, которая реализует двустороннее связывание в Angular. Двухстороннее связывание особенно полезно, когда вам нужно обновить свойства компонента из вашего представления и наоборот.

Мы используем механизм привязки событий ( ngSubmit ) для вызова onSubmit() когда пользователь отправляет форму. Метод определен внутри нашего компонента.

 /*add-list.component.ts*/ import { Component, OnInit } from '@angular/core'; import { List } from '../models/List'; import { ListService } from '../services/list.service'; @Component({ selector: 'app-add-list', templateUrl: './add-list.component.html', styleUrls: ['./add-list.component.css'] }) export class AddListComponent implements OnInit { private newList :List; constructor(private listServ: ListService) { } ngOnInit() { this.newList = { title: '', category:'', description:'', _id:'' } } public onSubmit() { this.listServ.addList(this.newList).subscribe( response=> { if(response.success== true) //If success, update the view-list component }, ); } } 

Внутри onSubmit() мы вызываем метод addList для addList который отправляет запрос http.post на сервер. Давайте обновим наш сервис списков, чтобы это произошло.

 /*list.service.ts*/ public addList(list: List) { let URI = `${this.serverApi}/bucketlist/`; let headers = new Headers; let body = JSON.stringify({title: list.title, description: list.description, category: list.category}); console.log(body); headers.append('Content-Type', 'application/json'); return this.http.post(URI, body ,{headers: headers}) .map(res => res.json()); } } 

Если сервер возвращает { success: true } , нам нужно обновить наши списки и включить новый список в нашу таблицу.

Однако проблема здесь заключается в том, что свойство lists находится внутри компонента ViewList . Нам нужно уведомить родительский компонент о том, что список необходимо обновить через событие. Для этого мы используем @Output декоратор @Output .

Во-первых, вам нужно импортировать Output и EventEmitter из @angular/core .

 import { Component, OnInit, Output, EventEmitter } from '@angular/core'; 

Затем объявите @Output декоратором @Output .

 @Output() addList: EventEmitter<List> = new EventEmitter<List>(); 

Если сервер возвращает success: true , addList событие addList .

 public onSubmit() { console.log(this.newList.category); this.listServ.addList(this.newList).subscribe( response=> { console.log(response); if(response.success== true) this.addList.emit(this.newList); }, ); } 

Обновите ваш viewlist.component.html с помощью этого кода.

 <app-add-list (addList)='onAddList($event)'> </app-add-list> 

И наконец, добавьте метод с именем onAddList() который объединяет вновь добавленный список в свойство lists .

 public onAddList(newList) { this.lists = this.lists.concat(newList); } 

Последние штрихи

Я добавил несколько стилей с bootswatch.com, чтобы наше приложение со списком корзин выглядело великолепно.

Создайте свое приложение, используя:

 ng build 

Как упоминалось ранее, артефакты сборки будут храниться в общедоступном каталоге. Запустите npm start из корневого каталога проекта MEAN. Теперь у вас должно быть запущено работающее приложение стека MEAN по адресу http: // localhost: 3000 /

Завершение

В этом руководстве мы рассмотрели много вопросов, создавая стековое приложение MEAN с нуля. Вот краткое изложение того, что мы сделали в этом уроке. Мы:

  • создал серверную часть приложения MEAN с использованием Express и MongoDB
  • написал код для маршрутов GET / POST и DELETE
  • сгенерировал новый проект Angular с использованием Angular CLI
  • разработал два новых компонента, AddList и ViewList
  • реализовал сервис приложения, в котором размещена логика взаимодействия с сервером.

И это все на данный момент. Надеюсь, вам понравилось читать. Я хотел бы прочитать ваши мысли и отзывы в комментариях ниже!

Рекомендуемые курсы

Тодд девиз
Проводимые экспертами онлайн-курсы AngularJS, Angular и TypeScript для отдельных лиц и команд. Используйте код купона «SITEPOINT» при оформлении заказа, чтобы получить скидку 25% .

Эта статья была рецензирована Юргеном Ван де Моером . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!