Angular でモデル駆動型フォームを実装してみた

Overview

Angular でフォームを実装してみた では, より手軽に実装可能な, テンプレート駆動型のフォームについて記載しました. テンプレート駆動型のフォームでは, 検証ルールをテンプレートに記述していました.

検証ルールはテンプレートに記述するだけではなく, コンポーネント側に記述することも可能です. これが, モデル駆動型 (Reactive 駆動型) のフォームです. テンプレート駆動型のフォームと比較すると, 冗長なコードになってしまいますが, より柔軟に, 複雑な検証ルールを実装できます.

ReactiveFormsModule

モデル駆動型のフォームを実装するには, FormsModule の代わりに, ReactiveFormsModule をインポートする必要があります.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

そして, テンプレートとコンポーネントは以下のようになります.

<form [formGroup]="myForm" (ngSubmit)="show()">
  <div>
    <label>メールアドレス: <input type="email" name="mail" [formControl]="mail" /></label>
    <span *ngIf="mail.errors?.required">メールアドレスは必須です.</span>
    <span *ngIf="mail.errors?.email">メールアドレスを正しい形式で入力してください.</span>
  </div>
  <div>
    <label>パスワード: <input type="password" name="password" [formControl]="password" /></label>
    <span *ngIf="password.errors?.required">パスワードは必須です.</span>
    <span *ngIf="password.errors?.minlength">パスワードは 6 文字以上で入力してください.</span>
  </div>
  <div>
    <label>名前: <input type="text" name="name" [formControl]="name" /></label>
    <span *ngIf="name.errors?.required">名前は必須です.</span>
    <span *ngIf="name.errors?.minlength">名前は 3 文字以上で入力してください.</span>
    <span *ngIf="name.errors?.maxlength">名前は 10 文字以内で入力してください.</span>
  </div>
  <div>
    <label>備考: <textarea name="memo" [formControl]="memo"></textarea></label>
    <span *ngIf="memo.errors?.maxlength">備考は 10 文字以内で入力してください.</span>
  </div>
  <div>
    <button type="submit" [disabled]="myForm.invalid">送信</button> 
  </div>
</form>
import { Component } from '@angular/core';
import {
  FormGroup,
  FormControl,
  FormBuilder,
  Validators
} from '@angular/forms';

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

  private mail     = new FormControl('', [Validators.required, Validators.email]);
  private password = new FormControl('', [Validators.required, Validators.minLength(6)]);
  private name     = new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(10)]);
  private memo     = new FormControl('', [Validators.required, Validators.maxLength(6)]);

  private myForm = this.builder.group({
    mail     : this.mail,
    password : this.password,
    name     : this.name,
    memo     : this.memo
  });

  constructor(private builder: FormBuilder) {
  }

  show() {
    console.log(this.myForm.value);
  }
}

モデル駆動型のフォームにおいて, 重要となる, FromControl, FormGroup について解説します.

FormControl

FormControl は, コンポーネント内で, そのインスタンスを生成して利用します.

new FormControl(value [, validators])

第 1 引数は, フォームの初期値, 第 2 引数は, (必要であれば) 検証ルールを配列で指定します. 検証ルールには, Validators クラスを利用することができます.

  private mail     = new FormControl('', [Validators.required, Validators.email]);
  private password = new FormControl('', [Validators.required, Validators.minLength(6)]);
  private name     = new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(10)]);
  private memo     = new FormControl('', [Validators.required, Validators.maxLength(6)]);

FormGroup

FormGroup は, FormControl インスタンスを束ねる役割をもっています. これによって, フォーム全体でまとめて, エラーが発生していないかを検証することが可能になります.

FormGroup を利用するには, コンポーネントのコンストラクタで, FormBuilder 型の引数をうけとるようにします (ちなみに, これは依存性注入 (DI) と呼ばれるデザインパターンの 1 種です).

  constructor(private builder: FormBuilder) {
  }

FormControl インスタンスを束ねるには, FormBuilder インスタンスの group メソッドを利用します.

  private myForm = this.builder.group({
    mail     : this.mail,
    password : this.password,
    name     : this.name,
    memo     : this.memo
  });

テンプレートとのひもづけ

コンポーネント側で必要な実装ができれば, あとは, コンポーネントとテンプレートをひもづけるだけです. そのためには, formGroup ディレクティブと formControl ディレクティブを利用します.

<form [formGroup]="myForm" (ngSubmit)="show()">
  <div>
    <label>メールアドレス: <input type="email" name="mail" [formControl]="mail" /></label>
    <span *ngIf="mail.errors?.required">メールアドレスは必須です.</span>
    <span *ngIf="mail.errors?.email">メールアドレスを正しい形式で入力してください.</span>
  </div>
    <!-- ... -->
</form>

Angular でファイルアップローダーを実装してみた

1. Overview

前回に引き続き, Angular でのフォーム実装を学びます. 今回は, ファイルアップローダーの実装をとおして, Angular における, HTTP クラアントの実装と, 非同期処理の実装について学びます.

2. HttpClientModule / HttpClient

Angular で HTTP クライアントを実装するには, HttpClientModule / HttpClient を利用します.

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

src/app/app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private title = 'File Uploader by Angular';

  constructor(http: HttpClient) {
  }
}

HttpModule / Http を利用しているサンプルコードなどもありますが, それらは非推奨なので注意してください.

3. RxJS

RxJS (Reactive Extensions Library for JavaScript) とは, 非同期およびコールバックベースのコードを, 関数的, かつ, リアクティブなスタイルで作成するためのライブラリです. Angular では, 非同期処理にこの RxJS を内部的に利用しています.

3. Implement

それでは, 実際に実装をしてみます. まず, 必要となるファイルアップロードサーバーを実装する必要があります. 言語はなんでも構いません. 今回は, Go で実装してみました.

uploader.go

package main

import (
    "encoding/json"
    "io"
    "net/http"
)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    reader, err := r.MultipartReader()

    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    h := w.Header()

    h.Set("Access-Control-Allow-Origin", "*")
    h.Set("Content-Type", "application/json")

    for {
        part, err := reader.NextPart()

        if err == io.EOF {
            break
        }

        if part.FileName() == "" {
            continue
        }

        res, err := json.Marshal(part)

        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        w.Write(res)
    }
}

func main() {
    http.HandleFunc("/upload", uploadHandler)
    http.ListenAndServe(":8080", nil)
}

Angular の実装です. まずは, テンプレートの実装です.

src/app/app.component.html

<form>
  <input #uploader type="file" accept="image/*" (change)="upload(uploader.files)" />
<form>

#uploader はテンプレート参照変数です. change は, イベントバインディングで, ファイルダイアログでファイルが選択されたときに発火します.

次に, コンポーネントの実装です.

src/app/app.component.html

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private title = 'File Uploader by Angular';

  constructor(private http: HttpClient) {
  }

  upload(files: Array) {
    if (files.length <= 0) {
      return;
    }

    const file = files[0];
    const formData = new FormData();

    formData.append('file', file, file.name);

    this.http.post('http://localhost:8080/upload', formData)
      .subscribe(
        data => console.log(data),
        error => console.log(error)
      );
  }
}

Angular の処理が関連しているのは, HttpClient#post メソッドの部分です. HttpClient#post メソッドは, 第 1 引数にリクエスト先の URL を, 第 2 引数に POST するデータを指定します. Http#post メソッドは, RxJS の Observable を返します. Observable からデータを取得するには, Observable#subscribe メソッドを利用します. Observable#subscribe メソッドは, Observer インスタンスを引数にとります. Observer は onNext / onError / onCompleted をもっており, Observable によって呼びだされ, それぞれ, ストリームの値, エラー, 完了を通知します (上記の, サンプルでは, onCompleted は利用していませんが …).

以上で実装は完了です.

動作確認するには, まず, ファイルアップロードサーバーをバックグランドで起動しておきます.

$ go run uploader.go &

あとは, npm start を実行して, ローカルサーバーを起動します.

$ npm start

ファイルアップロードをして, ストリームの値が取得できれば成功です.

Angular でフォーム実装してみた

1. Overview

フォームの実装をとおして, Angular (Angular 2 〜) の基礎 (の一部) を学んでみました. その備忘録です.

2. Syntax highlighting of TypeScript by Vim

と … その前に, 私はエディタに Vim を利用しています. Angular では, アプリケーションコードを TypeScript で記述する (Angular 自体も TypeScript で実装されています) ので, TypeScript のシンタックスハイライトを導入します.

プラグインマネージャーは Vundle を利用します.

まずは, 以下のリポジトリを clone します.

$ git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim

.vimrc に以下の記述を追加します.

filetype off

set rtp+=~/.vim/bundle/Vundle.vim

call vundle#begin()

Plugin 'gmarik/Vundle.vim'
Plugin 'leafgarland/typescript-vim'

call vundle#end()

filetype plugin on

 

そして, 以下のコマンドを実行します.

vim +PluginInstall +qall

3. Angular CLI

Angular は, クライアントサイドを (あえて) MVC に分類すると, すべてを担う, フルスタックフレームワークです (ちなみに, React は View のみとシンプル). そのため, (個人的主観ではありますが) ファイル構成が複雑で, 環境構築にもコストを要します. アプリケーションを実装するたびにこれらの作業をこなすのは現実的ではありません.

Angular CLI はこの問題を簡単に解決してくれます. Angular CLI を利用することで, コマンド 1 つで, 最低限必要なファイルの作成や環境構, つまり, アプリケーションの雛形生成を自動でおこなってくれます. Amngular CLI を利用するには, npm でインストールします.

$ npm install -g @angular/cli

アプリケーションの雛形を生成するには, ng new を実行します. 今回は, アプリケーション名を form とします.

$ ng new form
$ cd form

 

アプリケーション名でディレクトリが生成されるので, そのディレクトリに移動し, ls でファイルを確認すると, 多くのファイルやディレクトリが生成されていることがわかります.

% ls -A
.editorconfig     README.md         e2e               package-lock.json src               tslint.json
.gitignore        angular.json      node_modules      package.json      tsconfig.json

これは便利ですね.

3. Implement Form

準備ができたのでフォームを実装します. まずは, src/app/app.module.ts に FormsModule を追加します.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

次に, src/app/app.component.ts の AppComponent に, プロパティとメソッドを定義します.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private title = 'Form by Angular';
  private user = {
    mail     : 'xxx@gmail.com',
    password : '',
    name     : 'rilakkuma',
    memo     : ''
  };

  show() {
    Object.values(this.user).forEach(value => alert(value));
  }
}

そして, テンプレートである, app.component.html を以下のように記述します.

<form #myForm="ngForm" (ngSubmit)="show()" novalidate>
   <div>
     <label>メールアドレス : <input type="email" name="mail" [(ngModel)]="user.mail" #mail="ngModel" required email /></label>
     <span *ngIf="mail.errors?.required">メールアドレスは必須です</span>
     <span *ngIf="mail.errors?.email">メールアドレスを正しい形式で入力してください</span>
   </div>
   <div>
     <label>パスワード : <input type="password" name="password" [(ngModel)]="user.password" #password="ngModel" required minlength="6"></label>
     <span *ngIf="password.errors?.required">パスワードは必須です</span>
     <span *ngIf="password.errors?.minlength">パスワードは 6 文字以上で入力してください</span>
   </div>
   <div>
     <label>名前 : <input type="text" name="name" [(ngModel)]="user.name" #name="ngModel" required minlength="3" maxlength="10" /></label>
     <span *ngIf="name.errors?.required">名前は必須です</span>
     <span *ngIf="name.errors?.minlength">名前は 3 文字以上で入力してください</span>
     <span *ngIf="name.errors?.maxlength">名前は 18 文字以上で入力してください</span>
   </div>
   <div>
     <label>備考 : <textarea name="memo" [(ngModel)]="user.memo" #memo="ngModel" maxlength="10"></textarea></label>
     <span *ngIf="name.errors?.maxlength">備考は 10 文字以内で入力してください</span>
   </div>
   <div>
     <button type="submit" [disabled]="myForm.invalid">送信</button>
   </div>
</form>

詳細を解説していきます.

まず, form タグには, それぞれ ngFrom ディレクティブ, ngSubmit イベントバインディング, novalidate 属性 (ブラウザによるネイティブの検証を無効にする属性です. Angular 4 〜 では, 自動で付与してくれるので不要です) を指定しています.

<form #myForm="ngForm" (ngSubmit)="show()" novalidate>

ngForm ディレクティブは, submit 時に, フォーム全体の検証をするために利用します.

<button type="submit" [disabled]="myForm.invalid">送信</button>

API が変わっているだけで, AngularJS での, ng-form, ng-submit と大差ないことに気づかれたでしょうか … ?

入力フォーム (email, password, text, textarea) です.

<label>メールアドレス : <input type="email" name="mail" [(ngModel)]="user.mail" #mail="ngModel" required email /></label>

Angular でのフォーム検証を有効にするためは, 双方向バインディング必要となるので, ngModel ディレクティブと要素を識別するための, name 属性が必須になります.

[(ngModel)]="user.mail"

では, AppComponent#user プロパティと双方向バインディングしています. また,

#mail="ngModel"

は, テンプレート参照変数で, ngModel ディレクティブを代入することで, フォーム要素の状態にアクセスできるようにします.

required, email, minlength, maxlength などは, 必要に応じて指定します.

そして, 不正な値であった場合のエラー表示に活躍するのが, *ngIf ディレクティブです (AngularJS での ng-if ディレクティブ).

<span *ngIf="mail.errors?.required">メールアドレスは必須です</span>

*ngIf ディレクティブは, その値が true であった場合のみ, 指定した要素を DOM に追加します. *ngIf ディレクティブと, フォームの検証の成否を組み合わせることで, 簡単にエラーメッセージの表示を実装することが可能です.

フォームの検証の成否の構文を一般化すると,

入力要素名 (テンプレート参照変数名).errors?.検証型

となります. ? は, Angular 2 〜 で利用可能な, Safe Navigation Operator で, errors が null や undefined であってもエラーを発生させることがありません (つまり, 安全にプロパティアクセス可能になります). 検証型には, フォームに指定した, required, email, minlength, maxlength などを指定します.

あとは, フォームに不正な値が入っていれば, submit ボタンが disabled となるように実装します.

<button type="submit" [disabled]="myForm.invalid">送信</button>

[disabled] は, プロパティバインティングです. そこに, (フォームを参照するテンプレート変数).invalid とすることで, それが実装できます. ここでは, myForm が, フォームを参照するテンプレート変数となるので, myForm.invalid と指定します.

最後に,

$ npm start

を実行することで, ng serve が実行され, 簡易サーバーが起動するので, localhost:4200 にアクセスして, フォームが機能していれば完成です !

React Context API

1. Overview

React v16.3 から Context API というのが導入されました. この API を利用することによって Redux と同じようなデータフローを簡単に実装可能になるようです.

2. Implement

重要になるコンポーネントが, React#createContext メソッドによって生成される, Provider と Consumer です. その名前のとおり, Provider の value props に渡した値が, Consumer に渡ってきます.

2 – 1. Provider

Redux での, Provider, createStore に相当する.

2 – 2. Consumer

react-redux の connect に相当する.

'use strict';

import React from 'react';

const { Provider, Consumer } = React.createContext();

const Counter = () => (
    <Consumer>
        {({ state, actions }) => (
            <React.Fragment>
                <span>{state.count}</span>
                <button type="button" onClick={actions.increment}>+1</button>
                <button type="button" onClick={actions.decrement}>-1</button>
            </React.Fragment>
        )}
    </Consumer>
);

export default class App extends React.Component {
    state = {
        count : 0
    };

    constructor(props) {
        super(props);

        this.onClickIncrement = this.onClickIncrement.bind(this);
        this.onClickDecrement = this.onClickDecrement.bind(this);
    }

    onClickIncrement() {
        this.setState(prevState => {
            return { count : (prevState.count + 1) };
        });
    }

    onClickDecrement() {
        this.setState(prevState => {
            return { count : (prevState.count - 1) };
        });
    }

    render() {
        return (
            <Provider
                value={{
                    state   : this.state,
                    actions : {
                        increment : this.onClickIncrement,
                        decrement : this.onClickDecrement
                    }
                }}
            >
                <Counter />
            </Provider>
        );
    }
}

3. Conclusion

小規模なアプリであれば, わざわざ Redux (や MobX) を導入しなくても, Context API でいけるのでは ? と思いました.

Context API のサンプルリポジトリ

react-router v4 で onEnter ライクなことを実装する

1. Overview

react-routerにはあった onEnter というなにかしらの <Route /> に入ったときのフックポイントが用意されていました. しかしながら, v4 からはルーターの機能に特化したものとなり, onEnter などのフックポイントが廃止されました. しかしながら, onEnter ライクな処理をすることは可能なのでその実装方法をメモしておきます.

2. Implement routing by react-router v4

react-router v4 によるルーティングはおおよそ以下のような実装となります.

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './components/Home';
import PageA from './components/PageA';
import PageB from './components/PageB';
import PageC from './components/PageC';

export default () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/a" component={PageA} />
        <Route exact path="/b" component={PageB} />
        <Route exact path="/c" component={PageC} />
      </Switch>
    </Router>
  );
};

3. onEnter

onEnter ライクな処理となる関数を実装します.

const render = (Component) => ({ history, match }) => {
  // ページローディングなどを表示する: 例 YouTube のページ上部に表示されるプログレスバーなど

  return <Component history={history} match={match} />;
};

ちょっと見づらいかもしれませんが, 関数が関数を返す, いわゆるクロージャ (高階関数) となっています.
history は, ネイティブの history オブジェクトではなく react-router が定義する history オブジェクトで, match は パスからパラメータを取得するためのオブジェクトです.

そして, ルーティングを以下のように実装します.

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './components/app';
import PageA from './components/PageA';
import PageB from './components/PageB';
import PageC from './components/PageC';

const render = (Component) => ({ history, match }) => {
  // ページローディングなどを表示する: 例 YouTube のページ上部に表示されるプログレスバーなど

  return <Component history={history} match={match} />;
};

export default () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" render={render(Home)} />
        <Route exact path="/a" render={render(PageA)} />
        <Route exact path="/b" render={render(PageB)} />
        <Route exact path="/c" render={render(PageC)} />
      </Switch>
    </Router>
  );
};

注目すべきは, component props ではなく, render props になっていることです (ちなみに, component props と render props は共存できません).

これで onEnter ライクな処理が可能になります.

Node.js + React で SSR ~ SSR の実装 ~

Overview

前回の記事で SSR の環境を構築できたので, さっそく, React を利用した簡単な SSR の実装をしてみましょう.

1. Install packages

$ npm install --save react react-dom babel-cli babel-core babel-preset-es2015 babel-preset-react express

前回の記事でも述べましたが, 本来は –save-dev でインストールすべきパッケージも –save でインストールしていることに注意してください (Heroku のデプロイで dependencies のパッケージがインストールの対象とならないので).

2. Implement React Component

SSR で利用する, React コンポーネントを実装します. といっても, とりあえず <h1 /> で Hello SSR と表示する単純なコンポーネントです.

components/App.js

import React, { Component } from 'react';

export default class App extends Component {
    render() {
        return <h1>Hello SSR</h1>:
    }
}

また, ビルド後の App.js は, ルート直下の App.js にデプロイすることにします.

3. Implement render function

前回の記事では, express のルーターに直接レンダリング処理を記述していましたが, それだとコードの見通しがよくないので, SSR をする専用の関数を実装します.

render.js

const React          = require('react');
const ReactDOMServer = require('react-dom/server');
const App            = require('./App').default;

function render(req, res) {
    const content = ReactDOMServer.renderToString(React.createElement(App));
    const html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Hello SSR</title>
</head>
<body>
    <section id="app">
        ${content}
    </section>
</body>
</html>`;

    res.status(200).send(html);
};

module.exports = render;

ポイントは, ReactDOMServer.renderToString でコンポーネントを HTML 文字列に変換している処理です. あとは, その文字列を HTML に埋め込むだけです.

render 関数が実装できたので, app-server.js を以下のように書き換えます.

app-server.js

'use strict';

const express = require('express');
const app     = express();
const path    = require('path');
const render  = require('./render');

const port = process.env.PORT || 8080;

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res, next) => {
    render(req, res);
});

app.listen(port, () => {
    console.log(`Listening on port ${port} ...`);
});

4. npm scripts

package.json に以下のスクリプトを追加します.

  // ...
  "scripts": {
    "build": "babel components/App.js --out-file App.js",
    "start": "node app.js"
  },
  // ...

5. Edit Procfile

Procfile を以下のように変更します.

web: npm run build && npm start

 

以上で, SSR の実装ができました. 変更したファイルをすべてコミットして,

$ git push heroku master

を実行し, https://nodejs-ssr-sample.herokuapp.com/ にアクセスして, 「Hello SSR」が表示されていれば OK です.

ちなみに, SSR された HTML のソースは以下のようになっています.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Hello SSR</title>
</head>
<body>
    <section id="app">
        <h1 data-reactroot="" data-reactid="1" data-react-checksum="-601091822">Hello SSR</h1>
    </section>
</body>
</html>

AngularJS キャッシュをクリアする

AngularJSでキャッシュが邪魔になる場合の対処法

(特に, IEでは無駄にキャッシュが残りやすい…)

var app = app.module('app', []);

app.controller('Controller', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
    $cacheFactory('cacheId').destroy();
}]);

AngularJS $httpサービスでのクロスオリジン通信

昨日の記事で, XMLHttpRequest Level 2を利用したクロスオリジン通信を記載しましたが, その方法はAngularJSでのクロスオリジン通信にも使えます.

ただし, Cookieを送信するために, 直接XMLHttpRequestインスタンスにアクセスすることはできないので, $httpサービスのオプションで指定します.

こんな感じです.

 $http({
     // ....,
     withCredentials : true,
     // ....
}).success(function(data, status, headers, config) {
     // do something ....
}).error(function(data, status, headers, config) {
     // do something ....
});

サーバーが返すレスポンスは昨日の記事で記載したままでOKです.

ただし, Ajaxを表すヘッダー, つまり, X-Requested-With: XMLHttpRequest を指定すると, クロスオリジン通信ができなくなるので注意してください.

$http({
     // ....,
     withCredentials : true,
     // ....,
     headers : {
          // いらない
          'X-Requested-With' : 'XMLHttpRequest'
     }
}).success(function(data, status, headers, config) {
     // do something ....
}).error(function(data, status, headers, config) {
     // do something ....
});

AngularJS $qサービス

AngularJSには, 非同期処理を同期処理のように記述し, 可読性を向上させるための$qサービスというのがあります.

jQueryのDeferredオブジェクトと多少APIは異なりますが, その使い方や目的は同じです.

こんな感じ.