Beerwiz
Beerwiz

Reputation: 13

Passing key-event through to a component in Vue and Typescript

I'm kind of new to Vue, so please correct me if I'm formulating the question in a wrong way.

Use Case:

I have a very simple form it contains a TextField "Name" and a Button "Create". When the user have written a name then I wish that the user can press "Enter" to create the entity.

Code and findings:

I followed the instructions on how to pass events to a input field from the page below

https://v2.vuejs.org/v2/guide/events.html

When I use a input field it works as expected. Like this <input @keyup.enter="createCollection" />

But we are using TextField in a component for various reasons and I cannot get it to work. I have tried to get it working by trying a lot of different things, but for simplicity I just mention how I do now(or think it should work.).

      <FormValidatedText
        :label-text="$t('COLLECTION_CREATE')"
        :value="collection.name"
        :min-length="3"
        :max-length="30"
        :required="true"
        :input-id="'collection'"
        :invalid="!formIsValid"
        class="inline-form-field"
        @input="collectionFormNameChanged($event)"
        @validation="validCollectionName = $event"
        @keyup.enter="createCollection()"
      />

Other information and code:

The Component

<template>
  <div :class="{ invalid: touched && (valid === false || invalid === true) }">
    <label class="small-label" :for="inputId">{{ labelText }}</label>
    <input :id="inputId" type="text" class="field-value" :value="value" @input="onTextChange" @blur="isTouched" />
    <ul v-if="touched && !valid" class="field-value-errors">
      <li v-for="(error, index) in errors" :key="index">
        {{ $t(error.message, { variable: error.variable }) }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator';
import ValidateTextMixin from '@/components/Form/ValidateTextMixin.vue';
@Component
export default class FormValidatedText extends Mixins(ValidateTextMixin) {}
</script>

The Mixin that the Component uses

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
@Component
export default class ValidateTextMixin extends Vue {
  @Prop({ default: '' }) value!: string;
  @Prop({ required: true }) readonly inputId!: string;
  @Prop({ required: true }) readonly labelText!: string;
  @Prop({ default: false }) readonly required!: boolean;
  @Prop({ default: false }) readonly invalid!: boolean;
  @Prop() readonly minLength?: number;
  @Prop() readonly maxLength?: number;

  protected changed: boolean = false;
  protected touched: boolean = false;
  protected valid: boolean = false;
  protected errors: any[] = [];

  onTextChange($event: { target: HTMLInputElement }) {
    this.isValid($event.target.value);
    this.$emit('input', $event.target.value);
  }

  isValid(value: string) {
    this.valid = true;
    this.errors = [];
    if (this.required && !value) {
      this.valid = false;
      this.errors.push({ message: 'REQUIRED_FIELD_ERROR' });
    }
    if (this.minLength && value.length < this.minLength) {
      this.valid = false;
      this.errors.push({
        message: 'MINLENGTH_ERROR',
        variable: this.minLength
      });
    }
    if (this.maxLength && value.length > this.maxLength) {
      this.valid = false;
      this.errors.push({
        message: 'MAXLENGTH_ERROR',
        variable: this.maxLength
      });
    }
    this.$emit('validation', this.valid);
  }

  isTouched() {
    if (!this.touched) {
      this.isValid(this.value);
    }
    this.touched = true;
  }
}
</script>

Thanks for you help


Final implementation

Based on the answer from Ahmad Mobaraki. Please give him your vote if you think this is helpful =)

The component

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator';
import ValidateTextMixin from '@/components/Form/ValidateTextMixin.vue';
@Component
export default class FormValidatedText extends Mixins(ValidateTextMixin) {

  created() {
    window.addEventListener('keyup', (event) => {
      if (event.keyCode == 13) { // on enter
        this.$emit('enterpressed');
      }
    });
  }
}
</script>

And the usage of the Component

      <FormValidatedText
        :label-text="$t('COLLECTION_CREATE')"
        :value="collection.name"
        :min-length="3"
        :max-length="30"
        :required="true"
        :input-id="'collection'"
        :invalid="!formIsValid"
        class="inline-form-field"
        @input="collectionFormNameChanged($event)"
        @validation="validCollectionName = $event"
        @enterpressed="createCollection"
      />

Upvotes: 1

Views: 3822

Answers (1)

Ahmad Mobaraki
Ahmad Mobaraki

Reputation: 8160

I don't think Vue support key events on elements other than input tag. To do this you have to register a custom event handler for your component, like this:

{
    created: function () {
            window.addEventListener('keyup', this.myMethod)
    },
    methods: {
        myMethod: function () {
            // stuff
        }
    }
}

So in your case, you should do something like this:

1- Register event in your component:

export default class FormValidatedText extends Mixins(ValidateTextMixin) {
     created: function () {
          window.addEventListener('keyup', (event) =>{
               if (e.keyCode == 13) { // on enter
                 this.$emit('enterpressed')
                }
          })
     },
}

2- Listen to it in the parent component:

   <FormValidatedText
         .
         .
         .
        @enterpressed="createCollection()"
   />

Upvotes: 1

Related Questions