YOSHINO日記

プログラミングに関すること

Ember.js入門18: モックサーバーを作成してCRUDを試してみる

モックサーバーのルーティング

mirage/config.js

export default function() {
    this.get('/books');
    this.get('/books/:id');
    this.post('/books');
    this.delete('/books/:id');
}

adapterはデフォルト。

app/adapters/application.js

import DS from 'ember-data';

export default DS.JSONAPIAdapter.extend({
});

モデルの定義

app/models/book.js

import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  author: DS.attr('string'),
  year: DS.attr('date')
});

ルーティング

app/router.js

import EmberRouter from '@ember/routing/router';
import config from './config/environment';

const Router = EmberRouter.extend({
  location: config.locationType,
  rootURL: config.rootURL
});

Router.map(function() {
  this.route('books', {path: '/books/:book_id'});
  this.route('new');
});

export default Router

テストデータの作成:(Fakerを使う)

mirage/factories/book.js

import Mirage, {faker} from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  title: faker.lorem.sentence,
  author() {return faker.name.findName();},
  year: faker.date.past
});

mirage/scenarios/default.js

export default function(server) {
  server.createList('book', 10);
}

Create: データを作成する

formをつくります。

app/templates/new.hbs

{{outlet}}

<b>title: {{input value=info.title size='15'}}</b><br>
<b>Author: {{input value=info.author size='15'}}</b><br>
<b>Year: {{input value=info.year size='35'}}</b><br>
<button {{action 'newText'}}>Create</button>
<button {{action 'cancel'}}>Cancel</button><br>

viewでうけとったデータを以下のjsで受け取ります。

app/routes/new.js

import Controller from '@ember/controller';

export default Controller.extend({
  info: {},
  actions:{
    newText(){
      let inf = this.get('info');
      let newBook = this.store.createRecord(
        'book',
        { // new.hbsで入力した値をstoreする
          title: inf.title,
          author: inf.author,
          year: new Date(inf.year)
        }
      );
      newBook.save().then(()=>{
        this.transitionToRoute('application');
        this.set('info',{});
      },()=>{
        console.log('failed');
      });
    },
    cancel(){
      return true;
    }
  }
});

cancel()でreturn trueにしているのは、呼び出し側に処理を集約させたいからです。

cancel()は、createが失敗した時に呼ばれるので、applicationのルーターで処理を書くことで、 コードの重複を減らします。

Update: データを更新する

newと処理は非常に似ています。

まず、vuew側からデータを受け取ります。

app/templates/books.hbs

{{outlet}}
<b>title: {{input value=model.title size='15'}}</b><br>
<b>Author: {{input value=model.author size='15'}}</b><br>
<b>Year: {{input value=model.year size='35'}}</b><br>
<button {{action 'updateText'}}>Submit Changes</button>
<button {{action 'cancel'}}>Cancel</button><br

モデルは、routeで定義します。

app/routes/books.js

import Route from '@ember/routing/route';

export default Route.extend({
  model(params){
    return this.store.findRecord('book', params.book_id) // Get Request
  },
  actions:{
    cancel(){
      return true;
    }
  }
})

return this.store.findRecord('book', params.book_id)

で、モックサーバーへのGETリクエストでデータを取得しています。

最後にコントローラーで更新のためのアクションを記述します。

app/controllers/books.js

import Controller from '@ember/controller';

export default Controller.extend({
  actions: {
    updateText(){
      let book = this.get('model');
      book.set('year',new Date(book.get('year')));
      book.save();
      this.transitionToRoute('application');
    },
    cancel(){
      return true;
    }
  }
});

Delete: データを削除する

今回は、app/routes/application.jsに記述します。

import Route from '@ember/routing/route';

export default Route.extend({
  model(){
    return this.store.findAll('book')
  },
  actions: {
    delete(book){
      book.deleteRecord();
      console.log(book.get('isDeleted')); // save()呼ばれるまでリクエストしない。(同時に行うなら、destroyRecord())
      book.save();
    },
    cancel(){
      this.transitionTo('application')
    }
  }
})

deleteのリクエストはsave()で呼ばれています。

なので、viewでは、とくにurlが指定されていません。

app/templates/application.hbs

{{#link-to 'index'}}<h2 id="title">Welcome to Ember</h2>{{/link-to}}
{{#link-to 'new'}}<h5>Add New Book</h5>{{/link-to}}

{{outlet}}

{{#each model as |book|}}
  <br>
  title: {{#link-to 'books' book.id}}{{book.title}}{{/link-to}}
  <br>
  <a href="" {{action 'delete' book}}>delete?</a>
{{/each}