ag-gridメモ的な

ag-grid使っていてやってみたこともろもろのメモです。

# 編集要素を持ったセルコンポーネントのエディタ切り替えを不可にする
と言うとわかりずらいですが、

1. 例えばセルの表示状態（RendererComponent）をチェックボックスにしているときなど、カラム定義（ColumnDefs）のeditable:trueにしていると、ダブルクリックなどでinput要素に切り替わりカッコ悪くなる。
2. それを回避しようと、該当するRendererを使うときは常にeditable:falseにして、そのうえでRendererParamsにreadonlyプロパティなどを勝手に渡して活性制御を行っていたのですが、そのうち、今度は行ごとに活性制御を行いたくなってきた。
3. そうなると、やはりカラム定義のeditableには本来の用途で柔軟性をもたせたい。

で、EditorConponentへの切り替え自体をキャンセルさせてしまおう。と思いました。

```vuejs:UneditableEditor.vue
<template>
    <div>dammy template</div>
</template>

<script>
import Vue from "vue";
export default Vue.extend({
    methods: {
        isCancelBeforeStart() {
            return true;
        },
    },
})
</script>
```
つまり、編集不可能なEditorを設定する。
チェックボックスセルでは以下のようにeditableプロパティを使用できる。

```vuejs:ComboBoxCell.vue
<template>
    <div>
        <input type="checkbox" 
        v-model="isChecked"
        @change="onChange"
        :disabled="isDisabled"
        >
    </div>
</template>

<script>
import Vue from "vue";

export default Vue.extend({
    data() {
        return {
            isChecked: false,
            isDisabled: false
        }
    },
    methods:{
        onChange(){
            this.params.setValue(this.isChecked);
            this.params.api.refreshHeader();
        },
    },
    mounted(){
        let isChecked = true;
        this.isChecked = this.params.value;
        if(typeof(this.params.colDef.editable) === 'function'){
            this.isDisabled = !this.params.colDef.editable(this.params)
        }else{
            this.isDisabled = !this.params.colDef.editable
        }
    }
})
</script>
```

ついでにヘッダーも。

```vuejs:CheckBoxColumn.vue
<template>
    <div>
        <input
        :id="'id_'+params.column.colDef.field" 
        type="checkbox" 
        v-model="isChecked"
        @change="onChange">
        <label :for="'id_'+params.column.colDef.field">
            {{ params.displayName }}
        </label>
    </div>
</template>

<script>
import Vue from "vue";

export default Vue.extend({
    data() {
        return {
            isChecked: false
        }
    },
    methods:{
        onChange(){
            this.params.api.forEachNode(node =>{
                if(this.isEditable(node)){
                    node.setDataValue(this.params.column.colId, this.isChecked);
                }
            });
            this.params.api.redrawRows();
        },
        updateCheckBoxCol(){
            let isChecked = true;
            this.params.api.forEachNode(node => {
                if(this.isEditable(node) && 
                !node.data[this.params.column.colId]) {
                    isChecked = false;
                }
            });
            this.isChecked = isChecked;
        },
        /***
         * checkboxセルの編集可否を判定
         * colDefのeditableプロパティが関数の場合、RowNodeを与えて呼び出し。
         * （引数のオブジェクトはdataプロパティを持っていることを前提）
         * Params
         * ------
         *     node: RowNode
         */
        isEditable(node){
            if(typeof(this.params.column.colDef.editable) === "function"){
                return this.params.column.colDef.editable(node)
            }
            return this.params.column.colDef.editable
        }
    },
    beforeUpdate(){
        this.updateCheckBoxCol();
    },
    mounted(){
        this.updateCheckBoxCol();
    },
})

</script>
```

# セルの入力状態（バリデーション）を管理する
以下、公式ドキュメントのソースをバリデーション用に修正。

```vuejs:
<template>
<div>
    <input 
    :ref="'input'" 
    v-model="value"
    :maxlength="maxlength"
    />
</div>
</template>


<script>
import Vue from "vue";

export default Vue.extend({
    data() {
        return {
            value: '',
            cancelBeforeStart: false,
            maxlength: 255
        }
    },
    methods: {
        getValue() {
            /* 多分セルの編集完了時に呼び出されるだろうgetValueメソッド内で
               バリデーションを実施。不正な入力が行われたセルのインデックスを
               paramsのcontextプロパティに設定。 */
            if(this.params.pattern){
                let isValid = new RegExp(this.params.pattern).test(this.value)
                // 当該セルをinvalidCellsリストから除外。
                this.params.context.invalidCells = this.params.context.invalidCells.filter(
                    c => c[this.params.column.colId] !== this.params.rowIndex)
                if(!isValid){
                    // 不正入力の場合は該当するセルのcolId:RowIndexのオブジェクトを設定。
                    const invalidCell = {}
                    invalidCell[this.params.column.colId] = this.params.rowIndex
                    this.params.context.invalidCells.push(invalidCell)
                }
            }
            return this.value;
        },
        getCharCodeFromEvent(event) {
            event = event || window.event;
            return (typeof event.which === "undefined") ? event.keyCode : event.which;
        },
    },
    created() {
        this.value = this.params.value;
        this.maxlength = this.params.maxlength
    },
    mounted() {
        Vue.nextTick(() => {
            if (this.$refs.input) {
                this.$refs.input.focus();
            }
        });
    }
})

</script>
```

例えば、上の情報をセルのスタイルなどに使用する。

```javascript

            this.gridOptions = {
                context:{
                    invalidCells:[]
                },
                defaultColDef:{
                    cellClassRules: {
                        'invalid-input-cell': function(params) {
                            return params.context.invalidCells.find(c => c[params.colDef.field] === params.rowIndex)
                        },
                    }
                },
```

**（ちなみに）**
javascript慣れてないです。
また、ag-gridのAPIにもっといい機能がすでにあるかもなので、ご指摘・情報あれば教えていただきたく。