Mohsen Mirshahreza
Mohsen Mirshahreza

Reputation: 147

Vue indeterminate checkbox binding

I am using vue for data binding. I want to create a widget for access level control so I need allow, deny, and indeterminate states.

This markup is good but there is no indeterminate state:

<div class="row" v-for='a in context.This.Actions'>
    <div class="col-96">
        <input class="custom-control-input access-checkbox" v-bind:id="'chk_'+a.Name" v-bind:value="a.Id" v-model="context.This.RoleActions" indeterminate="true" type="checkbox" />
        <label class="pointer" v-bind:for="'chk_'+a.Name">{{ a.Name }}</label>
    </div>
</div>

The variables are :

context.This.Actions = [
    { "Id": "id_1",
      "Name": "AAA"
    },
    { "Id": "id_2",
      "Name": "BBB"
    },
    { "Id": "id_3",
      "Name": "CCC"
    }
]

context.This.RoleActions = [ "id_1", "id_2" ]

I want this change:

context.This.RoleActions = [ {"id_1":true}, {"id_2":false} ]

and I expect the below result:

Upvotes: 9

Views: 13148

Answers (3)

Emerson Bottero
Emerson Bottero

Reputation: 338

Simple example

<template>
  <input
    type="checkbox"
    :indeterminate="isIndeterminate"
    :checked="isChecked"
    :value="modelValue"
    @input="$emit('update:modelValue', isChecked)"
    @click="toggleCheckState"
  />
</template>

<script setup lang="ts">
const props = defineProps({
  modelValue: { type: Boolean, default: undefined }
})
defineEmits(['update:modelValue'])
import { onMounted, ref } from 'vue'

const isChecked = ref<boolean | undefined>(false)
const isIndeterminate = ref(false)

onMounted(() => {
  if (props.modelValue == undefined) isIndeterminate.value = true
  else isChecked.value = props.modelValue
})

function toggleCheckState() {
  if (isIndeterminate.value) {
    isChecked.value = true
    isIndeterminate.value = false
  } else if (isChecked.value == false) {
    isChecked.value = undefined
    isIndeterminate.value = true
  } else {
    isChecked.value = false
    isIndeterminate.value = false
  }
}
</script>

Usage

<SmartCheckbox v-model="anyRef" /> 

{{ anyRef}}

Upvotes: 0

Bassam Seydo
Bassam Seydo

Reputation: 596

I had a similar issue using props with checkbox to support 2 and 3 states . to handle this I used computed property with getter and setter using Vuetify checkbox

Here is my example

<template>
  <v-container class="checkbox-container">
    <v-checkbox
      type="checkbox"
      :indeterminate="indeterminate"
      :color="indeterminate ? '#767575' : 'success'"
      v-model="internalState"
      @click.stop="onCheckbox"
      @keyup.space.prevent="onCheckbox"
    ></v-checkbox>
  </v-container>
</template>

<script>
/**
 * Responsability: boolean field editor checkbox
 * When @threeState is true : following states (check, uncheck, indeterminate) otherwise (check, uncheck)
 * @checkboxState is an external state where it contains always the current state of checkbox
 **/
export default {
  model: {
    // v-model prop
    prop: 'checkboxState',
  },
  props: {
    threeState: Boolean,
    /**
     * Init state is the starting state Which the chekbox starts from.
     * by defining initstate it will ignore the default input @boolProperty
     **/
    initState: {
      type: String,
      default: 'false',
    },
    // Reperesent the value of checked state in model
    config: {
      type: Object,
      default: () => ({
        checked: 'true',
        unchecked: 'false',
        indeterminate: null,
      }),
    },
    checkboxState: {
      type: String,
    },
  },

  data() {
    return {
      internalState: this.checkboxState,
      indeterminate: false,
    }
  },

  computed: {
    state: {
      get() {
        return this.checkboxState
      },
      set(newState) {
        this.changeCheckboxState(newState)
      },
    },
  },

  // Change the state of checkbox after DOM is mounted
  mounted() {
    this.changeCheckboxState(this.initState)
  },

  methods: {
    changeCheckboxState(state) {
      this.$vnode.data.model.callback(state)
      this.internalState = state === this.config.checked
      this.indeterminate = state === this.config.indeterminate
    },

    onCheckbox() {
      if (this.threeState) {
        switch (this.state) {
          case this.config.unchecked:
            this.state = this.config.checked
            break
          case this.config.checked:
            this.state = this.config.indeterminate
            break
          case this.config.indeterminate:
            this.state = this.config.unchecked
            break
        }
      } else {
        this.state = (!(this.state === this.config.checked)).toString()
      }
    },
  },
}
</script>

<style lang="scss" scoped>
.checkbox-container {
  width: 50px;
}
</style>

Upvotes: 1

Tim D
Tim D

Reputation: 211

Indeterminate is a DOM property on a checkbox, which means putting it in the markup won't have an effect, it needs to be applied programmatically.

Even after doing that, keep in mind the state of a checkbox is still either checked or not checked. This is important to keep in mind when processing the form. The difference is visual only. (source)

With those caveats in mind, in Vue 2 you can add an indeterminate property to a checkbox like so:

<input type="checkbox" indeterminate.prop="true">

or bind to a dynamic value in your component:

<input type="checkbox" :indeterminate.prop="dataProperty">

I would recommend refactoring with this in mind.

Upvotes: 21

Related Questions