wslでelixir その58

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題、やってみた。

# 練習問題
Phoenixで、vueでspaを実装せよ。

# 方針
api使う。
axios使う。
vuetify使う。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/51a1029d-2da2-b3a8-d44f-1cfd0b78c1da.png)


# 手順

- lib/hello2_web/templates/layout/root.html.heexを修正

```
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="csrf-token" content={csrf_token_value()}>
    <%= live_title_tag assigns[:page_title] || "Hello2", suffix: " 揃 Phoenix Framework" %>
	<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
	<link rel="stylesheet" href="//unpkg.com/vuetify@1.5.18/dist/vuetify.min.css">
	<link rel="stylesheet" href="//cdn.materialdesignicons.com/3.5.95/css/materialdesignicons.min.css">
	<script src="//cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
	<script src="//unpkg.com/vuetify@1.5.18/dist/vuetify.min.js"></script>
	<script src="//unpkg.com/axios/dist/axios.min.js"></script>
  </head>
```

- lib/hello2_web/templates/page/index.html.heexを修正

```
<section class="phx-hero">
  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>


<v-app id="app">
  <v-container>
    <v-row>
      <v-col cols="10">
        <v-toolbar flat color="white">
          <v-toolbar-title>Phoenix</v-toolbar-title>
          <v-divider class="mx-2" inset vertical ></v-divider>
          <v-spacer></v-spacer>
          <v-dialog v-model="dialog" max-width="500px">
            <template v-slot:activator="{ on }">
              <v-btn color="primary" dark class="mb-2" v-on="on">New Item</v-btn>
            </template>
            <v-card>
              <v-card-title>
                <span class="headline">{{ formTitle }}</span>
              </v-card-title>
              <v-card-text>
                <v-container grid-list-md>
                  <v-layout wrap>
                    <v-flex xs12 sm6 md4>
                      <v-text-field v-model="editedItem.title" label="title"></v-text-field>
                    </v-flex>
                    <v-flex xs12 sm6 md4>
                      <v-text-field v-model="editedItem.link" label="link"></v-text-field>
                    </v-flex>
                  </v-layout>
                </v-container>
              </v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="blue darken-1" flat @click="close">Cancel</v-btn>
                <v-btn color="blue darken-1" flat @click="save">Save</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-toolbar>
        <v-data-table :headers="headers" :items="serverDatas">
          <template slot="items" slot-scope="props">
            <td class="text-xs-left">{{ props.item.id }}</td>
            <td class="text-xs-left">{{ props.item.title }}</td>
            <td class="text-xs-left">{{ props.item.link }}</td>
            <td class="justify-center layout px-0">
              <v-btn small color="aqua" @click="editItem(props.item)">
                edit
              </v-btn>
              <v-btn small color="error" @click="deleteItem(props.item)">
                delete
              </v-btn>
            </td>
          </template>
        </v-data-table>
      </v-col>
    </v-row>
  </v-container>
</v-app>
<script>

new Vue({
  el: "#app",
  data() {
    return {
      dialog: false,
      isCreate: true,
      editedItem: {
        id: 0,
        title: '',
        link: ''
      },
      headers: [{
        text: 'ID',
        value: 'id'
      }, {
        text: 'title',
        value: 'title',
      }, {
        text: 'link',
        value: 'link'
      }, {
        text: 'edit/delete',
        value: 'delete',
        sortable: false
      }],
      serverDatas: [],
    }
  },
  computed: {
    formTitle() {
      return this.isCreate ? 'New Item' : 'Edit Item'
    }
  },
  watch: {
    dialog(val) {
      val || this.close()
    }
  },
  methods: {
    deleteItem(item) {
      confirm('delete ok') && this.deleteUser(item)
    },
    editItem(item) {
      this.isCreate = false
      this.editedItem = item
      this.dialog = true
    },
    close() {
      this.dialog = false
    },
    save() {
      if (!this.isCreate)
      {
        this.update()
      }
      else
      {
        this.create()
      }
      this.dialog = false
    },
    update() {
      alert("update")
      axios.patch('http://localhost:4000/api/urls/' + this.editedItem.id, {
        title: this.editedItem.title,
        link: this.editedItem.link
      }).then(response => this.users.unshift(response.data)).catch(error => conalert(error))
    },
    create() {
      alert("create")
      axios.post('http://localhost:4000/api/urls', {
        title: this.editedItem.title,
        link: this.editedItem.link
      }).then(response => this.serverDatas.unshift(response.data)).catch(error => alert(error))
    },
    deleteUser(item) {
      axios.delete('http://localhost:4000/api/urls/' + item.id).then(response => console.log(response)).catch(error => alert(error))
      const index = this.serverDatas.indexOf(item)
      this.serverDatas.splice(index, 1)
    },
  },
  created() {
    axios.get('http://localhost:4000/api/urls').then(res => {
      this.serverDatas = res.data.data
    }).catch(e => {
      alert(e)
    }).finally(() => {
      //alert("ok")
    })
  }
})


</script>
  <p>Peace of mind from prototype to production</p>
</section>



```

以上