12

I want to create my own checkbox in Vue. I want to use two icons from fontawesome (lock and unlock). When my checkbox is checked then icon should be locked otherwise unlocked.

Here is my code:

<template>
   <div>
      <i @click="statusChanged()" v-if="!checked" class="fas fa-lock-open lock"></i>
      <i @click="statusChanged()" v-if="checked" class="fas fa-lock lock"></i>
   </div>
</template>
<script lang="ts">
import Vue from 'vue';
import { Prop } from 'vue/types/options';
export default Vue.extend({
    props: {
        checked: {
        type: Boolean as Prop<boolean>,
    },
},
methods: {
    statusChanged() {
        this.checked = !this.checked;
    },
},
});

I received an error:

Cannot assign to 'checked' because it is a constant or a read-only property

Can you help resolve this issue?

zcoop98
  • 2,590
  • 1
  • 18
  • 31
Marek
  • 193
  • 1
  • 2
  • 11
  • I've never used Vue with TypeScript but that is probably the equivalent of [Vue 2 - Mutating props vue-warn](https://stackoverflow.com/questions/39868963/vue-2-mutating-props-vue-warn) – yuriy636 Oct 19 '18 at 21:57
  • 1
    Why not use a `data` attribute instead of a prop? – Dexygen Oct 19 '18 at 21:58
  • @GeorgeJempty I don't want to use data because this option checked is set from outside the component, from parent – Marek Oct 20 '18 at 07:48
  • Just specify a data attribute with a name other than checked, and add a `created` block to the component where you set that data attribute equal to the value of the prop -- did you visit the link in the first comment above? It suggests this solution. – Dexygen Oct 20 '18 at 14:23
  • @GeorgeJempty I did but I have some problems with that approach. I did not change value from parent passed to my child component. – Marek Oct 20 '18 at 19:12
  • Hi @Marek! Answers on SO belong in the answer box, rather than edited into the question. I've moved your answer into a [*community wiki* answer below](https://stackoverflow.com/a/67928827). If you instead want to post the answer yourself, just let me know and I'll delete it. – zcoop98 Jun 10 '21 at 21:53

2 Answers2

24

Take a look at the prop.sync modifier. It is made for exactly this kind of case where you want to update the parents value but pass it as a property to the child:
https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier

Basically you mark the prop as being sync'able:
<Checkbox :checked.sync="foo"></Checkbox>

And to update the parent you emit an update:prop event from the child:
this.$emit('update:checked', !this.checked)

This should get you started for your solution:
https://codesandbox.io/s/97rl86n64

tony19
  • 125,647
  • 18
  • 229
  • 307
TommyF
  • 6,660
  • 8
  • 37
  • 61
2

Answer originally edited into question by OP @Marek, copied here by the community.

I was able to resolve the problem. Solution was really similar to what @TommyF suggested.

Here is the solution:

<template>
<div>
    <i v-if="!checked" class="fas fa-lock-open lock" @click="statusChanged()"></i>
    <i v-if="checked" class="fas fa-lock lock" @click="statusChanged()"></i>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { Prop } from 'vue/types/options';
export default Vue.extend({
    props: {
        checked: {
            type: Boolean as Prop<boolean>,
        },
    },
    model: {
        prop: 'checked',
        event: 'click',
    },
    methods: {
        statusChanged() {
            this.$emit('click', !this.checked);
        },
    },
});
</script>
zcoop98
  • 2,590
  • 1
  • 18
  • 31