저는 최근 남부 캘리포니아에서 열린 두 개의 다른 밋업 그룹에서 Ottoman.js로 구성된 OCEAN 스택에 대해 발표했습니다, 카우치베이스 서버, Express Framework, Angular 및 Node.js.
NoSQL 스택을 사용하는 JavaScript에 대해 배우고 싶어하는 개발자들이 많이 참여했습니다.

그룹과의 약속에 따라 제가 공유한 내용을 튜토리얼로 작성하여 모두가 도움을 받을 수 있도록 했습니다. 다양한 자바스크립트 기술과 NoSQL을 사용하여 기능적인 웹 애플리케이션을 개발하는 방법을 살펴보겠습니다.
코드를 살펴보기 전에 Couchbase Server와 Node.js가 설치 및 구성되어 있다고 가정합니다. 여기서는 환경 구성이 아닌 개발에 초점을 맞출 것입니다.
Ottoman.js를 사용하여 API를 빌드한 다음 Ottoman.js 대신 N1QL을 사용하여 API를 빌드하겠습니다. 두 API 모두 Angular로 개발된 프런트엔드에서 사용하게 됩니다.
Ottoman.js, Express 프레임워크 및 Couchbase NoSQL로 API 개발하기
Node.js 프로젝트의 첫 번째 단계는 프로젝트를 구성하고 모든 종속 요소를 다운로드하는 것입니다. 새 프로젝트를 생성하고 종속 요소를 다운로드하는 작업은 다음 명령어로 처리할 수 있습니다:
|
1 2 |
npm init --y npm 설치 오스만 카우치베이스 express body-파서 cors --저장 |
첫 번째 명령은 프로젝트의 package.json 두 번째 명령은 종속성을 가져오는데, 여기에는 본문 파서 요청에서 JSON 본문을 수락하고 cors 패키지를 사용하여 다른 도메인이나 포트에서 Angular를 사용할 때 발생할 수 있는 교차 출처 관련 문제를 해결하세요.
애플리케이션을 부트스트랩하기 위해 다음과 같은 파일을 만들겠습니다. app.js 에 다음 자바스크립트 코드가 포함되어 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var 카우치베이스 = require("couchbase"); var Express = require("express"); var Cors = require("cors"); var 바디파서 = require("body-parser"); var 오스만 = require("ottoman"); var 앱 = Express(); 앱.사용(바디파서.json()); 앱.사용(바디파서.urlencoded({ 확장: true })); 앱.사용(Cors()); var 클러스터 = new 카우치베이스.클러스터("couchbase://localhost"); var 버킷 = 클러스터.오픈버킷("default", ""); 오스만.store = new 오스만.CbStoreAdapter(버킷, 카우치베이스); 앱.get("/사람", 함수(요청, 응답) { }); 앱.get("/사람/:id", 함수(요청, 응답) { }); 앱.post("/사람/:id?", 함수(요청, 응답) { }); 앱.삭제("/사람/:id", 함수(요청, 응답) { }); var 서버 = 앱.듣기(3000, 함수() { 콘솔.로그("포트에서 듣기" + 서버.주소().포트 + "..."); }); |
기본적으로 다운로드한 종속성을 가져오고, API 엔드포인트를 정의하고, Couchbase Server에 연결을 설정하고, 애플리케이션 서비스를 시작했습니다.
API 엔드포인트는 데이터베이스에 대한 기본적인 CRUD 작업을 위한 것입니다. 객체 문서 모델러(ODM)인 Ottoman.js를 사용하므로 모델을 정의해야 합니다. 엔드포인트 정의 위에 다음을 추가합니다:
|
1 2 3 4 5 6 7 8 |
var 사람 = 오스만.모델("사람", { 이름: "문자열", 성: "문자열", 소셜 미디어: { 웹사이트: "문자열", 트위터: "문자열" } }); |
이 모델은 무한히 복잡할 수 있지만 여기서는 간단한 사용자 프로필을 표현하겠습니다. 이것이 우리가 코드와 데이터베이스에서 조작할 모델입니다.
현재 데이터베이스가 비어 있으므로 데이터를 생성하기 위한 엔드포인트에서 작업하는 것이 좋습니다. 프로젝트 내에서 다음 자바스크립트 청크를 편집합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
앱.post("/사람/:id?", 함수(요청, 응답) { 만약(!요청.body) { 반환 응답.상태(401).보내기({ "메시지": "POST 본문이 필요합니다!" }); } 사람.getById(요청.매개변수.id, 함수(오류, 결과) { 만약(오류) { 결과 = new 사람(요청.body); } else { 개체.할당(결과, 요청.body); } 결과.저장(함수(오류) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(요청.body); }); }); }); |
위의 엔드포인트는 아마도 우리 프로젝트에서 가장 복잡한 부분일 것입니다. 우리는 POST 본문이 필요하다고 말하고 있습니다. 그것이 무엇이든 존재하기만 한다면 상관없습니다.
이 엔드포인트에서는 두 마리 토끼를 잡았습니다. 우리는 업데이트뿐만 아니라 생성을 수행하고 있습니다. 먼저 전달된 ID로 조회를 수행합니다. ID가 전달되었는지 여부는 중요하지 않습니다. ID가 존재하지 않는 등의 이유로 오류가 발생하면 정의에 따라 새 ODM 모델이 생성되고, 그렇지 않으면 ID를 기준으로 조회한 결과와 병합됩니다.
모델이 완성되면 결과를 저장하고 사용자에게 반환할 수 있습니다.
다음 논리적 단계는 ID 값으로 특정 사람을 찾기 위한 엔드포인트를 완성하는 것입니다. 프로젝트에 다음 JavaScript를 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 |
앱.get("/사람/:id", 함수(요청, 응답) { 만약(!요청.매개변수.id) { 반환 응답.상태(401).보내기({ "메시지": "`ID`가 필요합니다!" }); } 사람.getById(요청.매개변수.id, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
전달된 ID 값을 사용하여 데이터베이스에서 사람을 가져와 요청을 발행한 클라이언트에게 다시 반환할 수 있습니다.
모델과 일치하는 모든 레코드를 가져오고 싶을 때가 있을 수 있습니다. ID 값 이외의 속성을 기반으로 쿼리하고 싶을 때도 있을 수 있습니다. 이 경우 찾기 명령을 사용합니다:
|
1 2 3 4 5 6 7 8 |
앱.get("/사람", 함수(요청, 응답) { 사람.찾기({}, { 일관성: 오스만.일관성.로컬 }, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
명령에 빈 객체를 제공함으로써 모델과 일치하는 모든 문서를 요청하고 있습니다. 이것은 특정 일치하는 속성으로 쉽게 확장할 수 있었습니다. 또한 쿼리를 완료하기 전에 인덱스가 업데이트되었는지 확인하고 싶습니다. 기본적으로 Couchbase는 성능에 중점을 두므로 인덱스가 아직 업데이트되지 않은 경우 결과에 일부 데이터가 누락될 수 있습니다. 상황에 따라 위와 같이 스캔 일관성을 변경할 수 있습니다.
마지막으로 데이터베이스에서 문서를 제거하기 위한 엔드포인트를 구축할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
앱.삭제("/사람/:id", 함수(요청, 응답) { 만약(!요청.매개변수.id) { 반환 응답.상태(401).보내기({ "메시지": "`ID`가 필요합니다!" }); } 사람.getById(요청.매개변수.id, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 결과.제거(함수(오류) { 응답.보내기({ "메시지": "삭제됨 `" + 요청.매개변수.id + "`"}); }); }); }); |
먼저 전달된 아이디로 문서를 가져오고, 문서가 존재하면 제거 를 클릭하여 삭제합니다.
이 애플리케이션을 실행하고 다음과 같은 도구를 사용하는 경우 우편 배달원를 사용하여 기본이지만 모든 기능을 갖춘 API를 테스트할 수 있습니다.
Express 프레임워크, N1QL 및 Couchbase NoSQL로 API 개발하기
Ottoman.js와 같은 ODM을 사용하는 것이 마음에 들지 않고 직접 쿼리를 제작하고 싶다고 가정해 보겠습니다. Ottoman.js를 제거하면 CEAN 스택을 얻을 수 있으며, 이는 N1QL을 통해 가능합니다.
프로젝트 구조는 매우 유사하지만, 저희만의 새로운 프로젝트를 만들려고 합니다.
|
1 2 |
npm init --y npm 설치 카우치베이스 express body-파서 cors uuid --저장 |
이전 프로젝트에서 보았던 것과 비슷한 종속성을 가지고 있지만 이번에는 UUID 값을 생성하기 위한 패키지를 가져옵니다. Ottoman.js는 우리를 위해 ID 값을 생성했지만 N1QL을 사용하면 개발자의 몫입니다. 아이디를 생성하는 여러 가지 방법 중 하나는 UUID를 사용하는 것입니다.
새로운 app.js 파일에 다음과 같은 상용구 코드가 있을 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var Express = require("express"); var 카우치베이스 = require("couchbase"); var 바디파서 = require("body-parser"); var UUID = require("uuid"); var Cors = require("cors"); var 앱 = Express(); var N1qlQuery = 카우치베이스.N1qlQuery; 앱.사용(바디파서.json()); 앱.사용(바디파서.urlencoded({ 확장: true })); 앱.사용(Cors()); var 클러스터 = new 카우치베이스.클러스터("couchbase://localhost"); var 버킷 = 클러스터.오픈버킷("default", ""); 앱.get("/사람", 함수(요청, 응답) { }); 앱.get("/사람/:id?", 함수(요청, 응답) { }); 앱.post("/사람/:id?", 함수(요청, 응답) { }); 앱.삭제("/사람/:id?", 함수(요청, 응답) { }); var 서버 = 앱.듣기(3000, 함수() { 콘솔.로그("포트에서 듣기" + 서버.주소().포트 + "..."); }); |
꽤 익숙해 보이지만 이번에는 Ottoman.js를 N1QL 준비로 교체했습니다.
이전 예제와 마찬가지로 데이터베이스에서 읽기를 시도하기 전에 데이터 저장에 대해 고민해 보겠습니다. 다음 엔드포인트 코드를 확인하세요:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
앱.post("/사람/:id?", 함수(요청, 응답) { 만약(!요청.body) { 반환 응답.상태(401).보내기({ "메시지": "POST 본문이 필요합니다!" }); } var id = 요청.매개변수.id ? 요청.매개변수.id : UUID.v4(); 요청.body._id = id; var 문 = N1qlQuery.fromString("`로 업서트" + 버킷._name + "(키, 값) 값 ($id, $데이터) 반환 `" + 버킷._name + "`.*"); 버킷.쿼리(문, { "id": id, "데이터": 요청.body }, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
요청과 함께 ID를 받으면 이를 사용하고 업데이트를 수행한다고 가정하고, 그렇지 않으면 새 ID를 생성하고 만들기를 수행합니다. 두 작업 모두 하나의 UPSERT 쿼리를 사용합니다. SQL 인젝션 공격을 방지하기 위해 매개변수화된 쿼리를 사용하여 사용자 정의 데이터를 매개변수화할 것입니다.
ID를 알고 있고 특정 문서를 찾고자 하는 경우 다음을 사용할 수 있습니다. 선택 작업에 대한 쿼리를 작성합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
앱.get("/사람/:id?", 함수(요청, 응답) { 만약(!요청.매개변수.id) { 반환 응답.상태(401).보내기({ "메시지": "`ID`가 필요합니다!" }); } var 문 = N1qlQuery.fromString("SELECT `" + 버킷._name + "`.* FROM `" + 버킷._name + "` WHERE META().id = $id"); 버킷.쿼리(문, { "id": 요청.매개변수.id }, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
두 쿼리 모두에서 오류 또는 결과를 요청을 발행한 클라이언트에 반환합니다.
모든 문서에 대해 쿼리하려는 경우 어디 조건을 설정하고 스캔 일관성을 다음과 같이 정의합니다:
|
1 2 3 4 5 6 7 8 9 |
앱.get("/사람", 함수(요청, 응답) { var 문 = N1qlQuery.fromString("SELECT META().id, `" + 버킷._name + "`.* FROM `" + 버킷._name + "`").일관성(N1qlQuery.일관성.REQUEST_PLUS); 버킷.쿼리(문, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
N1QL은 SQL의 또 다른 형태이기 때문에 쿼리 가능성은 매우 큽니다. 이 API 예제에서는 가능한 것의 일부도 다루지 않았습니다.
마지막으로 ID 값으로 문서를 제거하려면 다음과 같이 할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
앱.삭제("/사람/:id?", 함수(요청, 응답) { 만약(!요청.매개변수.id) { 반환 응답.상태(401).보내기({ "메시지": "`ID`가 필요합니다!" }); } var 문 = N1qlQuery.fromString("`에서 삭제" + 버킷._name + "` WHERE META().id = $id"); 버킷.쿼리(문, { "id": 요청.매개변수.id }, 함수(오류, 결과) { 만약(오류) { 반환 응답.상태(500).보내기(오류); } 응답.보내기(결과); }); }); |
이러한 N1QL 기반 엔드포인트 중 어느 것도 어렵지 않았습니다. Ottoman.js와 N1QL 중 어느 것을 선택하든 잘못된 선택은 없으며, 단지 선호도에 따라 달라질 뿐입니다.
Angular 및 TypeScript로 클라이언트 프론트엔드 구축하기
백엔드 API를 제거하면 매우 간단하지만 기능적인 클라이언트 프론트엔드에 집중할 수 있습니다. 이 프론트엔드는 OCEAN 스택과 CEAN 스택 예제 모두에서 작동합니다. 모듈화된 것을 사용하는 것은 언제나 좋은 일입니다.
다음과 같이 가정합니다. 앵귤러 CLI 설치 및 구성이 완료되면 새 프로젝트가 필요합니다:
|
1 |
ng new 카우치베이스-프로젝트 |
위의 명령은 필요한 모든 Angular 종속성을 포함하는 새 프로젝트를 생성합니다.
이 시점에서 CLI를 사용하여 각 페이지를 생성하거나 수동으로 생성할 수 있습니다. 두 가지 방법 모두 같은 위치에 놓이게 됩니다.
프로젝트에 다음 파일과 디렉터리를 원하는 대로 생성하세요:
|
1 2 3 4 5 6 |
mkdir -p src/앱/목록 mkdir -p src/앱/변경 터치 src/앱/목록/목록.컴포넌트.ts 터치 src/앱/목록/목록.컴포넌트.html 터치 src/앱/변경/변경.컴포넌트.ts 터치 src/앱/변경/변경.컴포넌트.html |
무엇부터 시작할까요? 알터 컴포넌트를 클릭하고 프로젝트의 src/app/alter/alter.component.ts 파일을 열고 다음 TypeScript 코드를 포함합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
가져오기 { 구성 요소, OnInit } 에서 "@angular/core"; 가져오기 { Http, 헤더, 요청 옵션 } 에서 "@angular/http"; 가져오기 { 위치 } 에서 "@각형/공통"; 가져오기 { 활성화된 경로 } 에서 "@각/라우터"; 가져오기 "rxjs/Rx"; @구성 요소({ moduleId: 모듈.id, templateUl: "alter.component.html" }) 내보내기 클래스 알터 컴포넌트 구현 OnInit { public id: 문자열; public 입력: any; public 생성자(비공개 http: Http, 비공개 위치: 위치, 비공개 경로: 활성화된 경로) { 이.id = ""; 이.입력 = { "이름": "", "성": "", "소셜 미디어": { "웹사이트": "", "twitter": "" } }; } public ngOnInit() { 이.경로.매개변수.구독(매개변수 => { 이.id = 매개변수["id"]; 만약(이.id) { 이.http.get("http://localhost:3000/person/" + 이.id) .지도(결과 => 결과.json()) .구독(결과 => { 이.입력 = 결과 인스턴스 오브 배열 ? 결과[0] : 결과; }); } }); } public 저장() { let 헤더 = new 헤더({ "콘텐츠 유형": "application/json" }); let 옵션 = new 요청 옵션({ 헤더: 헤더 }); let URL = "http://localhost:3000/person/"; 만약(이.id) { URL += 이.id; } 만약(이.입력.이름 && 이.입력.성) { 이.http.post(URL, JSON.문자열화(이.입력), 옵션) .지도(결과 => 결과.json()) .구독(결과 => { 이.위치.뒤로(); }); } } } |
위의 코드에는 많은 일이 일어나고 있으므로 이를 세분화해 보겠습니다.
내부 생성자 메서드에는 다음과 같은 것이 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 |
public 생성자(비공개 http: Http, 비공개 위치: 위치, 비공개 경로: 활성화된 경로) { 이.id = ""; 이.입력 = { "이름": "", "성": "", "소셜 미디어": { "웹사이트": "", "twitter": "" } }; } |
양식 데이터를 나타내는 공개 변수와 기존 문서의 가능한 ID를 나타내는 공개 변수를 초기화하고 있습니다. 이 외에도 컴포넌트에서 사용할 여러 Angular 서비스도 주입하고 있습니다.
다시 한 번, 우리는 일석이조의 효과를 노리려고 합니다. 이 구성 요소는 문서를 만들고 업데이트하는 화면을 나타냅니다.
내 ngOnInit 메서드를 사용하여 잠재적인 문서 ID를 가져와 해당 문서에 있는 데이터를 조회합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public ngOnInit() { 이.경로.매개변수.구독(매개변수 => { 이.id = 매개변수["id"]; 만약(이.id) { 이.http.get("http://localhost:3000/person/" + 이.id) .지도(결과 => 결과.json()) .구독(결과 => { 이.입력 = 결과 인스턴스 오브 배열 ? 결과[0] : 결과; }); } }); } |
아이디가 없으면 아무 일도 일어나지 않으며, 저희의 입력 변수는 빈 문자열 외에는 아무것도 채워지지 않습니다. 조회는 이전에 Ottoman.js 또는 N1QL로 생성한 API 엔드포인트에 대한 HTTP 요청이 됩니다.
이 구성 요소의 마지막 작업은 저장입니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public 저장() { let 헤더 = new 헤더({ "콘텐츠 유형": "application/json" }); let 옵션 = new 요청 옵션({ 헤더: 헤더 }); let URL = "http://localhost:3000/person/"; 만약(이.id) { URL += 이.id; } 만약(이.입력.이름 && 이.입력.성) { 이.http.post(URL, JSON.문자열화(이.입력), 옵션) .지도(결과 => 결과.json()) .구독(결과 => { 이.위치.뒤로(); }); } } |
ID가 있는 경우 POST 요청과 함께 전달합니다. POST 요청에는 직렬화된 양식 데이터가 포함됩니다. 요청이 성공하면 이전 화면으로 이동할 수 있습니다.
이 타입스크립트 로직과 함께 사용되는 HTML은 프로젝트의 src/app/alter/alter.component.html 파일을 열면 다음과 같이 표시됩니다:
|
1 2 3 4 5 6 7 |
<양식> <입력 유형="text" 이름="이름" 플레이스홀더="이름" [(ngModel)]="입력.이름" /> <입력 유형="text" 이름="성" 플레이스홀더="성" [(ngModel)]="입력.성" /> <입력 유형="text" 이름="웹사이트" 플레이스홀더="웹사이트" [(ngModel)]="input.social_media.website" /> <입력 유형="text" 이름="twitter" 플레이스홀더="Twitter" [(ngModel)]="input.social_media.twitter" /> <버튼 유형="버튼" (클릭)="save()">저장</버튼> </양식> |
저는 아티스트가 아니기 때문에 이 HTML은 매우 기본적이고 스타일이 전혀 적용되지 않았습니다. 각 양식 요소는 공개적으로 바인딩되어 있습니다. 입력 객체입니다.
프로젝트의 두 번째이자 마지막 구성 요소는 저장된 문서를 나열하는 것입니다. 프로젝트의 src/app/list/list.component.ts 파일을 열고 다음 TypeScript 코드를 포함합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
가져오기 { 구성 요소, OnInit } 에서 "@angular/core"; 가져오기 { Http } 에서 "@angular/http"; 가져오기 { 위치 } 에서 "@각형/공통"; 가져오기 "rxjs/Rx"; @구성 요소({ moduleId: 모듈.id, templateUl: "list.component.html" }) 내보내기 클래스 목록 컴포넌트 구현 OnInit { public 사람들: 배열<any>; public 생성자(비공개 http: Http, 비공개 위치: 위치) { 이.사람들 = []; } public ngOnInit() { 이.위치.구독(() => { 이.getPeople(); }); 이.getPeople(); } 비공개 getPeople() { 이.http.get("http://localhost:3000/people") .지도(결과 => 결과.json()) .구독(결과 => { 이.사람들 = 결과; }); } 비공개 삭제(id: 문자열) { 이.http.삭제("http://localhost:3000/person/" + id) .지도(결과 => 결과.json()) .구독(결과 => { 에 대한(let i = 0; i < 이.사람들.길이; i++) { 만약(이.사람들[i]._id == id) { 이.사람들.스플라이스(i, 1); break; } } }); } } |
다시 한 번 자세히 설명해 드리겠습니다.
내 생성자 메서드를 사용하여 공용 변수를 초기화합니다:
|
1 2 3 |
public 생성자(비공개 http: Http, 비공개 위치: 위치) { 이.사람들 = []; } |
이 경우 공개 변수는 화면에 표시되는 문서 목록입니다. 다음 단계는 존재할 가능성이 있는 사람을 쿼리하여 현재 비어 있는 배열을 채우는 것입니다:
|
1 2 3 4 5 6 7 |
비공개 getPeople() { 이.http.get("http://localhost:3000/people") .지도(결과 => 결과.json()) .구독(결과 => { 이.사람들 = 결과; }); } |
지금쯤이면 프론트엔드는 대부분 API에 대한 HTTP 요청에 불과하다는 것을 눈치채셨을 것입니다. 백엔드는 대부분의 무거운 작업을 처리합니다.
애플리케이션이 로드될 때와 Angular 탐색 스택에서 뒤로 이동할 때 모두 문서를 쿼리해야 합니다. 이는 ngOnInit 메서드를 사용합니다:
|
1 2 3 4 5 6 |
public ngOnInit() { 이.위치.구독(() => { 이.getPeople(); }); 이.getPeople(); } |
거꾸로 이동한 시점을 확인할 수 있도록 위치 리스너를 구독해야 합니다. 이렇게 하는 이유는 생성자 그리고 ngOnInit 메서드는 탐색할 때만 트리거되고 다시 탐색할 때는 트리거되지 않습니다.
문서 제거로 이동합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
비공개 삭제(id: 문자열) { 이.http.삭제("http://localhost:3000/person/" + id) .지도(결과 => 결과.json()) .구독(결과 => { 에 대한(let i = 0; i < 이.사람들.길이; i++) { 만약(이.사람들[i]._id == id) { 이.사람들.스플라이스(i, 1); break; } } }); } |
HTML에서는 모든 목록 항목 옆에 버튼이 있습니다. 이 버튼을 사용하면 문서를 제거할 수 있습니다. 삭제 메서드를 호출하고 특정 문서 ID를 전달합니다. 성공하면 해당 항목이 데이터베이스와 배열에서 로컬로 제거됩니다.
이 로직 뒤에 있는 HTML은 프로젝트의 src/app/list/list.component.html 다음과 같이 표시됩니다:
|
1 2 3 4 5 6 |
<a [라우터링크]="['/alter']">신규</a> <ul> <li *ngFor="사람의 사람"> <a [라우터링크]="['/alter', person._id]">{{ 사람.이름 }}</a> - <a 스타일="커서: 포인터" (클릭)="delete(person._id)">삭제</a> </li> </ul> |
그리고 라우터링크 속성은 데이터를 생성하거나 업데이트할 수 있는 페이지로 이동합니다. 그런 다음 배열을 반복하여 각 문서를 화면에 표시합니다. 항목을 클릭하면 업데이트 페이지로 이동하여 클릭한 문서의 ID를 전달합니다. 이렇게 하면 양식을 미리 채울 수 있습니다. 그렇지 않으면 문서를 삭제하려면 다른 버튼을 클릭합니다.
핵심 콘텐츠는 앵귤러를 통해 이루어지지만, 앵귤러 라우터를 통해 이를 통합해야 합니다.
프로젝트의 src/app/app.module.ts 파일을 열고 다음을 포함하세요:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
가져오기 { 브라우저 모듈 } 에서 '@각/플랫폼-브라우저'; 가져오기 { NgModule } 에서 '@angular/core'; 가져오기 { 라우터 모듈 } 에서 "@각/라우터"; 가져오기 { HttpModule } 에서 "@angular/http"; 가져오기 { 폼 모듈 } 에서 "@각형/양식"; 가져오기 { 앱 컴포넌트 } 에서 './app.component'; 가져오기 { 목록 컴포넌트 } 에서 "./list/list.component"; 가져오기 { 알터 컴포넌트 } 에서 "./alter/alter.component"; const 경로 = [ { 경로: "", 컴포넌트: 목록 컴포넌트 }, { 경로: "변경", 컴포넌트: 알터 컴포넌트 }, { 경로: "alter/:id", 컴포넌트: 알터 컴포넌트 } ]; @NgModule({ 선언: [ 앱 컴포넌트, 목록 컴포넌트, 알터 컴포넌트 ], 수입: [ 브라우저 모듈, HttpModule, 라우터 모듈, 폼 모듈, 라우터 모듈.forRoot(경로) ], 공급자: [], 부트스트랩: [앱 컴포넌트] }) 내보내기 클래스 앱 모듈 { } |
방금 컴포넌트를 가져오고 각 컴포넌트에 대한 경로를 정의한 것을 알 수 있습니다. 너무 거창한 것은 없습니다.
사용 중인 Angular CLI 버전에 따라 프로젝트의 src/app/app.component.html 파일에 다음을 포함하도록 합니다:
|
1 |
<라우터-아울렛></라우터-아울렛> |
이렇게 하면 프로젝트에서 내비게이션이 작동합니다.
결론
방금 제가 OCEAN 스택과 CEAN 스택에 대해 발표했던 자료를 보셨을 겁니다. 앵귤러 오렌지 카운티 그룹과 카우치베이스 로스앤젤레스 그룹.
MongoDB 개발자이고 Couchbase를 처음 살펴보고 계신다면 요리책 MongoDB에서 이동하는 방법을 설명하면서 Ottoman.js와 N1QL에 대해 자세히 설명하는 글을 썼던 적이 있습니다.
Couchbase 및 Node.js에 대한 자세한 내용은 다음을 확인하세요. 카우치베이스 개발자 포털.