孤独プログラマー譚

孤独死が近い。

Angular バリデーションエラー表示

ngModel経由なら、input要素のvalue, error とも取得できる。

<input #foo="ngModel" [(ngModel)]="metas.foo" required>
<div>value: {{foo.value}}</div>
<div>error: {{foo.errors?.required}}</div>
export class AppComponent {
  metas = {
    foo: '',
    bar: '',
    baz: '',
  }
}

ngForm経由では、input要素のvalueは取得できても、errorは取得できないっぽい。
(Form全体のエラーは取得できる。)

<form #metaForm="ngForm">
<input name="foo" [(ngModel)]="metas.foo" required>
</form>
<div>value: {{metaForm.value.foo}}</div>
<div>error: {{metaForm.invalid}}</div>

Angular 全て入力後に送信可能になるボタン 2

f:id:halation-summer:20191222145353g:plain
formを使わないバージョン。応用は効かせやすいかも。

<input [(ngModel)]="metas.foo"><br>
<input [(ngModel)]="metas.bar"><br>
<input [(ngModel)]="metas.baz"><br>
<br>
<div>disabled: {{disabled}}</div>
<button [disabled]="disabled">Submit</button>
export class AppComponent {
  metas = {
    foo: '',
    bar: '',
    baz: '',
  }
  get disabled() {
    if (this.metas.foo !== ''
    && this.metas.bar !== ''
    && this.metas.baz !== ''
    ) { return false }
    return true
  }
}

Angular 全て入力後に送信可能になるボタン 1

f:id:halation-summer:20191222141419g:plain
Vue.jsに続いて、Angularでも。
テキストフィールドに全て入力完了後、disabledが解除されて送信可能になるボタン。

<form #metaForm="ngForm">
<input name="foo" [(ngModel)]="metas.foo" required><br>
<input name="bar" [(ngModel)]="metas.bar" required><br>
<input name="baz" [(ngModel)]="metas.baz" required><br>
<br>
<div>disabled: {{metaForm.invalid}}</div>
<button [disabled]="metaForm.invalid">Submit</button>
</form>
export class AppComponent {
  metas = {
    foo: '',
    bar: '',
    baz: '',
  }
}

name属性をつけないと、うまく動かないっぽい。

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回返すことはできないという…。