[Vuejs]-How to trigger event in all sibling components except current component in vuejs?

4👍

Why not pass the current component as an event argument and use that to check if the event originated from this component or another one.

edit() {
  this.editing = true;
  EventBus.$emit('editing-another-field', this);
}

created() {
  EventBus.$on('editing-another-field', source => {
    if (source !== this) {
      this.editing = false;
    }
  });
}

Or you can do it like this (it is important to unregister the event listener when the component is destroyed to avoid a memory leak):

edit() {
  EventBus.$emit('editing-field', this);
}

created() {
  this.editingFieldHandler = vm => {
    this.editing = vm === this;
  };

  EventBus.$on('editing-field', this.editingFieldHandler);
}

destroyed() {
  EventBus.$off('editing-field', this.editingFieldHandler);
}

Otherwise you can emit the event first and then set this.editing to true.

0👍

Are you sure you want an event bus? This brings up bad memories of JQuery 😉 I think it would be cleaner to limit yourself to a tree of parents and children. Thinking MVVM, formLockedBy is a perfectly valid and sensible property to store on the parent and pass to the children.

The solution below, running here, shows a form with two fields. The fields are both instances of modal-component. The parent manages the formLockedBy property. The child fields look at this property to know to disable themselves. When the user starts typing in a field, the field emits an editing event and formLockedBy gets set. Similarly, when a field emits a save or cancel event, the parent clears formLockedBy and the other input(s) spring back to life.

Note the advantages…

  • Only the parent listens for events.
  • The identifier stored in formLockedBy is just the string name of the field. This is much safer than passing and storing a reference to the Vue component. If you don’t like this, you might consider adding a safe id to the object proto.
  • No surprises. The full list of events the parent will react to is declared in the tag instantiating the child. The child specifies in props everything it needs from the parent.

HTML

<div id="example">
  <modal-input name='First Name' 
    :form-locked-by='this.formLockedBy' 
    v-on:save='formLockedBy = null'
    v-on:cancel='formLockedBy = null'
    v-on:editing='fieldActive'
  ></modal-input>
  <modal-input name='Address' 
    :form-locked-by='this.formLockedBy' 
    v-on:save='formLockedBy = null'
    v-on:cancel='formLockedBy = null'
    v-on:editing='fieldActive'
  ></modal-input>
</div>

JS

Vue.component('modal-input', {
  template: `<div>
    {{name}} :
    <input :name='name' type="text" v-on:keydown="active" :disabled="formLockedBy && formLockedBy != name"/>
    <span v-if="editing && formLockedBy == name">
      <input type="button" value="Save" v-on:click="$emit('save');editing=false;"></input>
      <input type="button" value="Cancel" v-on:click="$emit('cancel');editing=false;"></input>
    </span>
  </div>`,
  data : function(){
    return {editing:false};
  },
  props: ['name','formLockedBy'],
  methods : { 
    active : function(event){
      if(!this.editing){
        this.editing = true;
        this.$emit('editing',{field:this.name})      
      }
      return true;
    }
  }
});
// create a root instance
new Vue({
  el: '#example',
  data: {
    formLockedBy : null
  },
  methods : {
    fieldActive : function(args){
      this.formLockedBy = args.field;
    }
  } 
})

Leave a comment