ユーザーインターフェイスを使用してソフトウェアを設計する場合、拡張と保守が容易になるようにコードを構造化することが重要です。 時間の経過とともに、アプリケーションのさまざまなコンポーネントの責任を分離するためのいくつかのアプローチがありました。 これらのデザインパターンに関する文献はたくさんありますが、初心者がさまざまなパターンの制限の特徴とそれらの違いを理解することは非常に混乱する可能性があります。

このチュートリアルでは、主要な2つのアプローチ、Model-View-Controller(MVC)パターンとModel-View-ViewModel(MVVM)パターンについて説明します。 MVVMパターンでは、コントローラーはViewModelに置き換えられます。 これら2つのコンポーネントの主な違いは、一方のビューともう一方のコントローラーまたはViewModelの間の依存関係の方向です。

TypeScriptとAngularで書かれたブラウザアプリケーションを使って、アイデアを開発し、例を挙げてパターンを説明します。 TypeScriptは、コードに型情報を追加するJavaScriptの拡張機能です。 このアプリケーションは、MacOS/iOSで人気のあるNotesアプリケーションを模倣します。 AngularはMVVMパターンを適用します。 掘り下げて、MVCパターンとMVVMパターンの主な違いを見てみましょう。

AngularCLIを使用してアプリケーションをセットアップする

開始するには、AngularCLIをインストールする必要があります。 ノードと npm 最初にインストールされます。 まだ行っていない場合は、 node.js.org にアクセスし、指示に従ってNodeをダウンロードしてインストールします。 次に、コンピュータでターミナルを開き、 npm AngularCLIをインストールするコマンド。

npm install -g @angular/[email protected]

システム構成によっては、システム管理者がを使用してこのコマンドを実行する必要がある場合があります。 sudo. これにより、 ng システム上でグローバルにコマンドを実行します。 ng Angularアプリケーションの作成、操作、テスト、および構築に使用されます。 を実行して、新しいAngularアプリケーションを作成できます。 ng new 選択したディレクトリにあります。

ng new AngularNotes

これにより、新しいアプリケーションに関するいくつかの質問に答えるウィザードが起動し、ディレクトリレイアウトとスケルトンコードを含むいくつかのファイルが作成されます。 最初の質問は、ルーティングモジュールを含めることに関するものです。 ルーティングを使用すると、ブラウザのパスを変更することで、アプリケーション内のさまざまなコンポーネントに移動できます。 この質問にははいと答える必要があります。 2番目の質問では、使用するCSSテクノロジーを選択できます。 非常に単純なスタイルシートのみを含めるので、プレーンなCSS形式で十分です。 質問に答えると、ウィザードは必要なすべてのコンポーネントのダウンロードとインストールを開始します。

マテリアルデザインとそのコンポーネントを使用して、アプリケーションの見栄えを良くすることができます。 これらは、を使用してインストールできます npm アプリケーションディレクトリ内のコマンド。 The ng new コマンドは、というディレクトリを作成する必要があります AngularNotes. その中に移動して、次のコマンドを実行します。

npm install --save @angular/[email protected] @angular/[email protected] @angular/[email protected] @angular/[email protected]

The src ディレクトリには、アプリケーションのソースコードが含まれています。 ここ、 src/index.html ブラウザのメインエントリポイントです。 選択したテキストエディタでこのファイルを開き、次の行をに貼り付けます <head> セクション。 これにより、マテリアルアイコンに必要なフォントが読み込まれます。

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

The src/style.css スタイルシートにはグローバルスタイルが含まれています。 このファイルを開き、次のスタイルを貼り付けます。

@import "~@angular/material/prebuilt-themes/deeppurple-amber.css";

body {
  margin: 0;
  font-family: sans-serif;
}

h1, h2 {
  text-align: center;
}

次に、開く src/app/app.module.ts. このファイルには、グローバルに利用できるようにするすべてのモジュールのインポートが含まれています。 このファイルの内容を次のコードに置き換えます。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from "@angular/flex-layout";

import { MatToolbarModule,
         MatMenuModule,
         MatIconModule,
         MatInputModule,
         MatFormFieldModule,
         MatButtonModule,
         MatListModule,
         MatDividerModule } from '@angular/material';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FlexLayoutModule,
    FormsModule,
    ReactiveFormsModule,
    MatToolbarModule,
    MatMenuModule,
    MatIconModule,
    MatInputModule,
    MatFormFieldModule,
    MatButtonModule,
    MatListModule,
    MatDividerModule,
    AppRoutingModule,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

この時点で、ファイルにアプリケーションレイアウトを作成する方法を紹介し始めることができます src/app/app.component.html. しかし、これですでにアプリケーションアーキテクチャの議論に飛び込むことができます。 代わりに、次のセクションでは、最初にモデルの実装について説明します。 次のセクションでは、ビューとそのViewModelとの関係について説明します。

モデル

モデルには、アプリケーションのビジネスエンドが含まれています。 単純なCRUD(Create Read Update Delete)アプリケーションの場合、モデルは通常、単純なデータモデルです。 より複雑なアプリケーションの場合、モデルは当然、その複雑さの増加を反映します。 ここに表示されているアプリケーションでは、モデルはテキストノートの単純な配列を保持します。 各ノートには、 ID タイトル、およびテキストがあります。 Angularでは、モデルはいわゆるservicesにコード化されています。 The ng コマンドを使用すると、新しいサービスを作成できます。

ng generate service Notes

これにより、2つの新しいファイルが作成されます。 src/app/notes.service.tssrc/app/notes.service.spec.ts. このチュートリアルでは、他のファイルと同様に、これらのファイルの2番目を無視できます。 .spec.ts ファイル。 これらのファイルは、コードの単体テストに使用されます。 本番環境にリリースするアプリケーションでは、そこにテストを記述します。 開ける src/app/notes.service.ts その内容を次のコードに置き換えます。

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observer } from 'rxjs';

export class NoteInfo {
  id: number;
  title: string;
}

export class Note {
  id: number;
  title: string;
  text: string;
}

@Injectable({
  providedIn: 'root'
})
export class NotesService {
  private notes: Note[];
  private nextId = 0;
  private notesSubject = new BehaviorSubject<NoteInfo[]>([]);

  constructor() {
    this.notes = JSON.parse(localStorage.getItem('notes')) || [];
    for (const note of this.notes) {
      if (note.id >= this.nextId) this.nextId = note.id+1;
    }
    this.update();
  }

  subscribe(observer: Observer<NoteInfo[]>) {
    this.notesSubject.subscribe(observer);
  }

  addNote(title: string, text: string): Note {
    const note = {id: this.nextId++, title, text};
    this.notes.push(note);
    this.update();
    return note;
  }

  getNote(id: number): Note {
    const index = this.findIndex(id);
    return this.notes[index];
  }

  updateNote(id: number, title: string, text: string) {
    const index = this.findIndex(id);
    this.notes[index] = {id, title, text};
    this.update();
  }

  deleteNote(id: number) {
    const index = this.findIndex(id);
    this.notes.splice(index, 1);
    this.update();
  }

  private update() {
    localStorage.setItem('notes', JSON.stringify(this.notes));
    this.notesSubject.next(this.notes.map(
      note => ({id: note.id, title: note.title})
    ));
  }

  private findIndex(id: number): number {
    for (let i=0; i<this.notes.length; i++) {
      if (this.notes[i].id === id) return i;
    }
    throw new Error(`Note with id ${id} was not found!`);
  }
}

ファイルの上部近くに、2つのクラス定義があります。 NoteInfoNote. The Note クラスにはメモに関する完全な情報が含まれていますが、 NoteInfo のみが含まれています id そしてその title. アイデアは NoteInfo はるかに軽量で、すべてのノートタイトルを表示するリストで使用できます。 両方 NoteNoteInfo は単純なデータクラスであり、ビジネスロジックは含まれていません。 ロジックはに含まれています NotesService、アプリケーションのモデルとして機能します。 いくつかのプロパティが含まれています。 The notes プロパティはの配列です Notes オブジェクト。 この配列は、モデルの信頼できる情報源として機能します。 機能 addNote, getNote, updateNote、 と deleteNote モデルでCRUD操作を定義します。 それらはすべて直接作用します notes 配列、配列内の要素の作成、読み取り、更新、および削除。 The nextId プロパティは、メモを参照できる一意のIDとして使用されます。

あなたはいつでも、 notes 配列が変更され、プライベート update メソッドが呼び出されます。 このメソッドは2つのことを行います。 まず、メモをローカルストレージに保存します。 ブラウザのローカルストレージが削除されていない限り、これによりデータがローカルに保持されます。 これにより、ユーザーはアプリケーションを閉じて後で開いても、メモにアクセスできます。 実際のアプリケーションでは、CRUD操作は、データをローカルに保存する代わりに、別のサーバー上のRESTAPIにアクセスします。

によって実行される2番目のアクション update に新しい値を出すことです notesSubject 財産。 notesSubjectBehaviorSubject 凝縮された配列を含むRxJSから NoteInfo オブジェクト。 The BehaviorSubject すべてのオブザーバーがサブスクライブできるオブザーバブルとして機能します。 このサブスクリプションは、 subscribe の方法 NotesService. サブスクライブしたオブザーバーは、いつでも通知されます update と呼ばれます。

モデルの実装から取り除く主なことは、モデルがビューやコントローラーの知識を持たないスタンドアロンサービスであるということです。 これは、MVCアーキテクチャとMVVMアーキテクチャの両方で重要です。 モデルは他のコンポーネントに依存してはなりません。

景色

次に、ビューに注目したいと思います。 Angularアプリケーションでは、ビューは .html テンプレートと .css スタイルシート。 これらのテンプレートの1つについてはファイルですでに説明しました src/app/app.component.html. ファイルを開き、次のコンテンツを貼り付けます。

<mat-toolbar color="primary" class="expanded-toolbar">
    <span>
      <button mat-button routerLink="/">{{title}}</button>
      <button mat-button routerLink="/"><mat-icon>home</mat-icon></button>
    </span>
    <button mat-button routerLink="/notes"><mat-icon>note</mat-icon></button>
</mat-toolbar>
<router-outlet></router-outlet>

ちょっとしたスタイリングも加えてみませんか? 開ける src/app/app.component.css 次のスタイルを追加します。

.expanded-toolbar {
  justify-content: space-between;
  align-items: center;
}

The app.component メインページのレイアウトが含まれていますが、意味のあるコンテンツは含まれていません。 コンテンツをレンダリングするコンポーネントをいくつか追加する必要があります。 使用 ng generate このようにもう一度コマンドします。

ng generate component Home
ng generate component Notes

これにより、2つのコンポーネントが生成されます。 各コンポーネントは、 .html, .css、および .ts ファイル。 今のところ、心配しないでください .ts ファイル。 これについては、次のセクションで説明します。 (覚えておいてください、 .spec.ts このチュートリアルで完全に無視しているファイル。)

開ける src/app/home/home.component.html 内容を以下に変更します。

<h1>Angular Notes</h1>
<h2>A simple app showcasing the MVVM pattern.</h2>

次に、開く src/app/notes/notes.component.html コンテンツを以下のコードに置き換えます。

<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" class="notes">
  <mat-list fxFlex="100%" fxFlex.gt-sm="20%">
    <mat-list-item *ngFor='let note of notes'>
      <a>
        {{note.title}}
      </a>
    </mat-list-item>
  </mat-list>
  <mat-divider fxShow="false" fxShow.gt-sm [vertical]="true"></mat-divider>
  <mat-divider fxShow="true" fxShow.gt-sm="false" [vertical]="false"></mat-divider>
  <div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="!editNote" class="note-container">
    <h3>{{currentNote.title}}</h3>
    <p>
      {{currentNote.text}}
    </p>
    <div fxLayout="row" fxLayoutAlign="space-between center" >
      <button mat-raised-button color="primary">Edit</button>
      <button mat-raised-button color="warn">Delete</button>
      <button mat-raised-button color="primary">New Note</button>
    </div>
  </div>
  <div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="editNote" class="form-container">
    <form [formGroup]="editNoteForm">
      <mat-form-field class="full-width">
        <input matInput placeholder="Title" formControlName="title">
      </mat-form-field>

      <mat-form-field class="full-width">
        <textarea matInput placeholder="Note text" formControlName="text"></textarea>
      </mat-form-field>
      <button mat-raised-button color="primary">Update</button>
    </form>
  </div>
</div>

付随する src/app/notes/notes.component.css このように見えるはずです。

.notes {
  padding: 1rem;
}

.notes a {
  cursor: pointer;
}

.form-container, .note-container {
  padding-left: 2rem;
  padding-right: 2rem;
}

.full-width {
  width: 80%;
  display: block;
}

ここまでは順調ですね!

見て src/app/notes/notes.component.html これは、アプリケーションのメインビューを表します。 次のようなプレースホルダーに気付くでしょう {{note.title}} 値で埋めることができるように見えます。 上記のバージョンでは、ビューはアプリケーション内のコードを参照していないようです。

MVCパターンに従う場合、ビューはデータを挿入できるスロットを定義します。 また、ボタンがクリックされるたびにコールバックを登録するためのメソッドも提供します。 この点で、ビューはコントローラーを完全に知らないままになります。 コントローラはアクティブに値を入力し、コールバックメソッドをビューに登録します。 コントローラーだけがビューとモデルの両方を認識し、2つをリンクします。

以下に示すように、AngularはMVVMパターンと呼ばれる別のアプローチを採用しています。 ここで、コントローラーはViewModelに置き換えられています。 これは次のセクションのトピックになります。

ViewModel

ViewModelはに住んでいます .ts コンポーネントのファイル。 開ける src/app/notes/notes.component.ts 以下のコードを入力してください。

import { Component, OnInit } from '@angular/core';
import { Note, NoteInfo, NotesService } from '../notes.service';
import { BehaviorSubject } from 'rxjs';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-notes',
  templateUrl: './notes.component.html',
  styleUrls: ['./notes.component.css']
})
export class NotesComponent implements OnInit {
  notes = new BehaviorSubject<NoteInfo[]>([]);
  currentNote: Note = {id:-1, title: '', text:''};
  createNote = false;
  editNote = false;
  editNoteForm: FormGroup;

  constructor(private formBuilder: FormBuilder,
              private notesModel: NotesService) { }

  ngOnInit() {
    this.notesModel.subscribe(this.notes);
    this.editNoteForm = this.formBuilder.group({
      title: ['', Validators.required],
      text: ['', Validators.required]
    });
  }

  onSelectNote(id: number) {
    this.currentNote = this.notesModel.getNote(id);
  }

  noteSelected(): boolean {
    return this.currentNote.id >= 0;
  }

  onNewNote() {
    this.editNoteForm.reset();
    this.createNote = true;
    this.editNote = true;
  }

  onEditNote() {
    if (this.currentNote.id < 0) return;
    this.editNoteForm.get('title').setValue(this.currentNote.title);
    this.editNoteForm.get('text').setValue(this.currentNote.text);
    this.createNote = false;
    this.editNote = true;
  }

  onDeleteNote() {
    if (this.currentNote.id < 0) return;
    this.notesModel.deleteNote(this.currentNote.id);
    this.currentNote = {id:-1, title: '', text:''};
    this.editNote = false;
  }

  updateNote() {
    if (!this.editNoteForm.valid) return;
    const title = this.editNoteForm.get('title').value;
    const text = this.editNoteForm.get('text').value;
    if (this.createNote) {
      this.currentNote = this.notesModel.addNote(title, text);
    } else {
      const id = this.currentNote.id;
      this.notesModel.updateNote(id, title, text);
      this.currentNote = {id, title, text};
    }
    this.editNote = false;
  }
}

の中に @Component クラスのデコレータ、ビューへの参照を見ることができます .html.css ファイル。 一方、クラスの残りの部分では、ビューへの参照はまったくありません。 代わりに、に含まれるViewModel NotesComponent クラスは、ビューからアクセスできるプロパティとメソッドを公開します。 これは、MVCアーキテクチャと比較して、依存関係が逆転していることを意味します。 ViewModelにはビューの知識はありませんが、ビューで使用できるモデルのようなAPIを提供します。 もう一度見てみると src/app/notes/notes.component.html 次のようなテンプレート補間がわかります。 {{currentNote.text}} のプロパティに直接アクセスします NotesComponent.

アプリケーションを機能させるための最後のステップは、どのコンポーネントがさまざまなルートを担当しているかをルーターに通知することです。 開ける src/app/app-routing.module.ts 以下のコードに一致するようにコンテンツを編集します。

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { NotesComponent } from './notes/notes.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'notes', component: NotesComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

これはリンクします HomeComponent デフォルトルートと NotesComponentnotes ルート。

メインのアプリケーションコンポーネントについては、後で実装するいくつかのメソッドを定義します。 開ける src/app/app.component.ts コンテンツを次のように更新します。

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  public title = 'Angular Notes';
  public isAuthenticated: boolean;

  ngOnInit() {
    this.isAuthenticated = false;
  }

  login() {
  }

  logout() {
  }
}

コンポーネントには2つのプロパティが含まれています titleisAuthenticated. これらの2つ目は、ユーザーがアプリケーションにログインしたかどうかを示すフラグです。 今のところ、それは単にに設定されています false. 2つの空のメソッドは、ログインまたはログアウトをトリガーするコールバックとして機能します。 今のところ、空のままにしておきますが、後で記入します。

ビューを完了する

依存関係の方向に関するこの知識があれば、ボタンとフォームがViewModelでアクションを実行するようにViewを更新できます。 開ける src/app/notes/notes.component.html もう一度、コードを次のように変更します。

<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" class="notes">
  <mat-list fxFlex="100%" fxFlex.gt-sm="20%">
    <mat-list-item *ngFor='let note of notes | async'>
      <a (click)="onSelectNote(note.id)">
        {{note.title}}
      </a>
    </mat-list-item>
  </mat-list>
  <mat-divider fxShow="false" fxShow.gt-sm [vertical]="true"></mat-divider>
  <mat-divider fxShow="true" fxShow.gt-sm="false" [vertical]="false"></mat-divider>
  <div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="!editNote" class="note-container">
    <h3>{{currentNote.title}}</h3>
    <p>
      {{currentNote.text}}
    </p>
    <div fxLayout="row" fxLayoutAlign="space-between center" >
      <button mat-raised-button color="primary" (click)="onEditNote()" *ngIf="noteSelected()">Edit</button>
      <button mat-raised-button color="warn" (click)="onDeleteNote()" *ngIf="noteSelected()">Delete</button>
      <button mat-raised-button color="primary" (click)="onNewNote()">New Note</button>
    </div>
  </div>
  <div fxFlex="100%" fxFlex.gt-sm="70%" *ngIf="editNote" class="form-container">
    <form [formGroup]="editNoteForm" (ngSubmit)="updateNote()">
      <mat-form-field class="full-width">
        <input matInput placeholder="Title" formControlName="title">
      </mat-form-field>

      <mat-form-field class="full-width">
        <textarea matInput placeholder="Note text" formControlName="text"></textarea>
      </mat-form-field>
      <button mat-raised-button color="primary">Update</button>
    </form>
  </div>
</div>

あなたが見ることができます (click) のメソッドを直接参照するさまざまな場所のハンドラー NotesComponent クラス。 これは、ビューがViewModelとそのメソッドについて知る必要があることを意味します。 依存関係を逆転させる理由は、ボイラープレートコードの削減です。 ViewとViewModelの間には双方向のデータバインディングがあります。 ビューのデータは、ViewModelのデータと常に同期しています。

Angularアプリに認証を追加する

適切なユーザー認証がなければ、優れたアプリケーションは完成しません。 このセクションでは、既存のAngularアプリケーションに認証をすばやく追加する方法を学習します。 Oktaは、わずか数行のコードでアプリにプラグインできるシングルサインオン認証を提供します。

Oktaの無料の開発者アカウントが必要になります。 表示されるフォームに詳細を入力し、利用規約に同意して、[開始]を押して送信するだけです。 登録が完了すると、Oktaダッシュボードに移動します。 ここでは、Oktaサービスに登録されているすべてのアプリケーションの概要を確認できます。

アプリケーションの追加をクリックして、新しいアプリケーションを登録します。 表示される次の画面で、アプリケーションのタイプを選択できます。 シングルページアプリケーションは、Angularアプリに最適です。 次のページに、アプリケーションの設定が表示されます。 アプリケーションをテストするときは、ポート番号を4200に変更する必要があります。 ng serve.

それでおしまい。 これで、後で必要になるクライアントIDが表示されるはずです。 これで、認証サービスをコードに含める準備ができました。 OktaはAngularに便利なライブラリを提供します。 アプリケーションのルートディレクトリで次のコマンドを実行することにより、インストールできます。

npm install @okta/[email protected] --save

開ける app.module.ts インポートします OktaAuthModule.

import { OktaAuthModule } from '@okta/okta-angular';

さらに下に、同じファイルで、のリストに以下を追加します imports.

    OktaAuthModule.initAuth({
      issuer: 'https://{yourOktaDomain}/oauth2/default',
      redirectUri: 'http://localhost:4200/implicit/callback',
      clientId: '{clientId}'
    })

このスニペットでは、 {clientId} Okta開発者ダッシュボードで取得したクライアントIDに置き換える必要があります。

パスワードなしで特定のルートにアクセスできないようにするには、変更する必要があります src/app/app-routing.module.ts. のインポートを追加します OktaCallbackComponentOktaAuthGuard.

import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';

次に、ルートの配列に別のルートを追加します。

{ path: 'implicit/callback', component: OktaCallbackComponent }

The implicit/callback ユーザーがログインプロセスを完了すると、ルートがOktaによって呼び出されます。 The OktaCallbackComponent 結果を処理し、認証プロセスを要求したページにユーザーをリダイレクトします。 個々のルートを保護するために、簡単に追加できるようになりました OktaAuthGuard このようにそのルートに。

{ path: 'notes', component: NotesComponent, canActivate: [OktaAuthGuard] }

メインアプリケーションのViewModelを実装せずに残したことを忘れないでください。 開ける src/app/app.component.ts もう一度、次のインポートをファイルの先頭に追加します。

import { OktaAuthService } from '@okta/okta-angular';

次に、のすべてのメソッドを実装します AppComponent クラス。

constructor(public oktaAuth: OktaAuthService) {}

async ngOnInit() {
  this.isAuthenticated = await this.oktaAuth.isAuthenticated();
}

login() {
  this.oktaAuth.loginRedirect();
}

logout() {
  this.oktaAuth.logout('/');
}

やるべきことは1つだけです。 これで、ログインボタンとログアウトボタンをトップバーに追加できます。 開ける src/app/app.component.html これらの2行を <mat-toolbar> 要素、クロージング後 </span>.

<button mat-button *ngIf="!isAuthenticated" (click)="login()"> Login </button>
<button mat-button *ngIf="isAuthenticated" (click)="logout()"> Logout </button>

ログインボタンとログアウトボタンは、 login()logout() のメソッド app.component.ts ViewModel。 これらの2つのボタンの可視性は、 isAuthenticated ViewModelのフラグ。

これですべてです。 これで、認証を備えたMVVMアーキテクチャに基づく完全なアプリケーションができました。 アプリケーションのルートディレクトリでAngularテストサーバーを起動することで、テストできます。

ng serve

ブラウザを開き、に移動します http://localhost:4200. このようなものが表示されるはずです。

Angularで安全なアプリケーション開発の詳細

このチュートリアルでは、AngularがMVVMデザインパターンに基づいていることと、このパターンがよく知られているMVCパターンとどのように異なるかを示しました。 MVCパターンでは、コントローラーは、他の2つのコンポーネントによって提供されるオブザーバブルとオブザーバブルを使用して、ビューをモデルにリンクするだけです。 コントローラが接続を設定すると、ビューとモデルは直接通信しますが、誰と通信しているかはわかりません。 具体的には、コントローラーはそれ自体のアプリケーション状態を保持しません。 ビューとモデルを接続するためのファシリテーターにすぎません。 MVVMパターンでは、コントローラーはViewModelに置き換えられます。 ViewとViewModelは、双方向のデータバインディングを介してリンクされます。 それらは同じ状態を共有します。

MVCおよびMVVMのデザインパターンの詳細については、次のリンクを参照してください。

このチュートリアルのコードは、 oktadeveloper /okta-angular-notes-app-exampleから入手できます。

この投稿が気に入った場合は、私たちが公開している他の投稿も気に入っていただける可能性があります。 Twitterで@oktadevをフォローし、 YouTubeチャンネルに登録して、より優れたチュートリアルをご覧ください。