孤独プログラマー譚

孤独死が近い。

Angular 複数チェックボックスを全て選択で切り替え

f:id:halation-summer:20191222110033g:plain

Vue.jsに続いて、Angularで全て選択を切り替え。

<input type="checkbox" [(ngModel)]="metas.foo">foo<br>
<input type="checkbox" [(ngModel)]="metas.bar">bar<br>
<input type="checkbox" [(ngModel)]="metas.baz">baz<br>
<br>
<input type="checkbox" [(ngModel)]="all_checked">all<br>
export class AppComponent {
  metas = {
    foo: false,
    bar: false,
    baz: false,
  }
  get all_checked() {
    return !Object.values(this.metas).includes(false)
  }
  set all_checked(checked) {
    for (const key of Object.keys(this.metas)) {
      this.metas[key] = checked
    }
  }
}

Vue.jsの場合、複数チェックボックスの選択値を配列で双方向バインドできるんだけど、Angularはできないのかなぁ…。
AngularのngModelはgetter, setterと相性がいいように感じました。

JavaScript オブジェクトの結合

オブジェクトの結合について、情報整理。
 
 
以下、既存のオブジェクトに、別のオブジェクトを結合する場合。
ES6で書けない時はjQueryを使う。

var nogizaka1 = {
  shiraishi: 'mai',
  akimoto: 'manatsu',
}
var nogizaka2 = {
  hori: 'miona',
  suzuki: 'ayane',
}

// jQueryのメソッド
$.extend(nogizaka1, nogizaka2)
console.log(nogizaka1)
// {shiraishi: "mai", akimoto: "manatsu", hori: "miona", suzuki: "ayane"}

// ES6
Object.assign(nogizaka1, nogizaka2)
console.log(nogizaka1)
// {shiraishi: "mai", akimoto: "manatsu", hori: "miona", suzuki: "ayane"}

 
 
以下、結合して新しいオブジェクトを生成する場合。

// jQueryのメソッド
var nogizaka3 = $.extend({}, nogizaka1, nogizaka2)
console.log(nogizaka3)
// {shiraishi: "mai", akimoto: "manatsu", hori: "miona", suzuki: "ayane"}

// ES6
var nogizaka3 = Object.assign({}, nogizaka1, nogizaka2)
console.log(nogizaka3)
// {shiraishi: "mai", akimoto: "manatsu", hori: "miona", suzuki: "ayane"}

// ES6 スプレッド演算子
var nogizaka3 = { ...nogizaka1, ...nogizaka2 }
console.log(nogizaka3)
// {shiraishi: "mai", akimoto: "manatsu", hori: "miona", suzuki: "ayane"}

スプレッド演算子を積極的に使いたい。

JavaScript プロパティの初期化

JavaScriptの小ネタ。

以下、よくあるインスタンス生成時のプロパティセット。

var personal_info = {
  name: 'Tom',
  age: '20',
}

function Person(personal_info) {
  this.name = personal_info.name
  this.age = personal_info.age
  this.indroduce_myself = function () {
    console.log(`私は${this.name}、${this.age}歳です。`)
  }
}

var person = new Person(personal_info)
person.indroduce_myself() // 私はTom、20歳です。

 
 
プロパティセット部分を Object.assign に置き換える。

function Person(personal_info) {
  Object.assign(this, personal_info)
  this.indroduce_myself = function () {
    console.log(`私は${this.name}、${this.age}歳です。`)
    // 私はTom、20歳です。
  }
}

だらだらと複数個のプロパティをセットしていく必要が無くなる。
玄人っぽくて良い!

Vue.js Ajaxを使った連動するセレクトボックス

1つ目のセレクトボックスの項目を非同期通信で取得。
そこで選択した項目を元に、2つ目のセレクトボックスの項目も非同期で取得する。

f:id:halation-summer:20190113232507g:plain

<div id="app">
  <select v-model="selected_category_id">
    <option v-for="item in category_list" :value="item.id">{{ item.name }}</option>
  </select>
  <div>{{ selected_category_id }}</div>
  <select v-model="selected_animal_id">
    <option v-for="item in animal_list" :value="item.id">{{ item.name }}</option>
  </select>
  <div>{{ selected_animal_id }}</div>
</div>


1つ目のセレクトボックス項目は created 内で取得。
2つ目のセレクトボックス項目は watch 内で取得。

new Vue({
  el: '#app',
  data: {
    category_list: [],
    selected_category_id: null,
    animal_list: [],
    selected_animal_id: null,
  },
  async created () {
    this.category_list = (await axios.get('http://localhost/animals/category/')).data
    this.selected_category_id = this.category_list[0].id
  },
  watch: {
    async selected_category_id () {
      this.animal_list = (await axios.get(`http://localhost/animals/animals/${this.selected_category_id}/`)).data
      this.selected_animal_id = this.animal_list[0].id
    },
  },
})


算出プロパティを使えるかと思ったけど、どうも非同期通信は使えないみたいで…。
そもそも算出プロパティは return で値を返すので、Promise では return を2回返すことはできないという…。

Vue.js 全て入力後に送信可能になるボタン

テキストフィールドに全て入力完了後、disabledが解除されて送信可能になるボタン。

f:id:halation-summer:20190112140948g:plain

<div id="app">
  <div><input v-model="field1"></div>
  <div><input v-model="field2"></div>
  <div><input v-model="field3"></div>
  <div>{{ entered_all }}</div>
  <button :disabled="!entered_all">submit</button>
</div>
new Vue({
  el: '#app',
  data: {
    field1: '',
    field2: '',
    field3: '',
  },
  computed: {
    entered_all () {
      const required_fields = [
        this.field1,
        this.field2,
        this.field3,
      ]
      return required_fields.indexOf('') === -1
    },
  },
})

jQueryはDOMからD0Mを直接触っていたが、
Vue.jsはViewModelを介す必要があるから、jQueryと考え方が違って面白い。

Vue.js 複数チェックボックスを全て選択で切り替え

よくある「全て選択」チェックボックス

f:id:halation-summer:20190112075758g:plain

<div id="app">
  <div v-for="(item, index) in items">
    <input type="checkbox" :value="item" v-model="checked_items">{{ labels[index] }}
  </div>
  <div>{{ checked_items }}</div>
  <input type="checkbox" v-model="checked_all">check_all<br>
  <div>{{ checked_all }}</div>
</div>
new Vue({
  el: '#app',
  data: {
    items: ['value1', 'value2', 'value3'], // 全てのvalue値
    labels: ['label1', 'label2', 'label3'],
    checked_items: ['value2'], // 初期値
  },
  computed: {
    checked_all: {
      get () {
        return this.checked_items.length === this.items.length
      },
      set (checked) {
        this.checked_items = checked ? this.items : []
      },
    },
  },
})


以下の部分は、サーバーサイドレンダリングで配列に値をつっこんでもいいし、
AJAXで取得してきてもいい。

data: {
  items: ['value1', 'value2', 'value3'], // 全てのvalue値
  labels: ['label1', 'label2', 'label3'],
  checked_items: ['value2'], // 初期値
},

Vue.js 単一ファイルコンポーネントと通常使用の併用

通常使用とは何なのかというと、HTMLのscriptタグ内で、new Vue() してやること…ということにします。

単一ファイルコンポーネントは使いたい。
でも、他のHTML要素の方も、Vue.jsで制御したい。
その場合、どうすればいいか、調べてもよく分からなった。

例えば、ボタンを押した時にアラートメッセージを出す…等の小さい制御も、Vue.jsで書きたい。
でもそれは、単一ファイルコンポーネントにする程ではない。
かと言って、大きめの動的な要素は、単一ファイルコンポーネントを使いたい。

そこで、以下の方法でやることにした。

main.js
Webpack等でバンドルする際のエントリーポイント。
Vue.extend()は、再利用可能なコンストラクタを生成するらしい。
components や data をVueコンストラクタに登録しつつも、新たなコンストラクタを作る。
それを、global.Vue …要はグローバル変数に入れてやる。
これで、各HTMLファイルから、new Vue() してやることが出来る。

global.Vue = Vue.extend({
  components: {
    menulist: Menu,
    detail: Detail,
  },
  data () {
    return {
      foo: 'foo'
    }
  },
  router,
})

以下、HTMLファイル。
Vueコンストラクタからインスタンスを生成している。

<div id="app">
  <div>{{ foo }}</div>
  <div>{{ bar }}</div>
</div>
<script src="bundle.js"></script>
<script>
new Vue({
  el: '#app',
  data: {
    bar: 'bar',
  },
})
</script>

Webアプリ共通の設定 → エントリーポイントで登録
各HTMLページ毎の設定 → scriptタグ内で登録

という感じで作ってやる。今のところこれが一番スッキリしてると思う。