2017-12-31 131 views
1

In meinem Szenario habe ich mouseover- und mouseout-Ereignisse, die ich bedingt binden möchte (z. B. nur, wenn sich der Benutzer auf einem Gerät mit Maus befindet).Bedingte Ereignisbindung - vuejs

Ich weiß, ich kann die Bedingung in der Ereignisbehandlungsroutine selbst haben, aber das würde noch den Speicher für die Event-Handler reservieren, was unnötig ist.

Gibt es eine Möglichkeit, das Ereignis selbst verbindlich zu machen bedingt?

(klar zu sein, was ich möchte das Ereignis Abonnement kurzzuschließen der Lage sein, so die zugrunde liegende addEventListener Betrieb nie passiert, wenn die Bedingung falsch ist)

+2

Sie 'v-wenn hasMouse' und einen Zweig mit den Ereignissen haben und die andere ohne benutzen können. Verwenden Sie wahrscheinlich [Bereichs-Slots] (https://vuejs.org/v2/guide/components.html#Scoped-Slots), um den gemeinsamen Code einzufügen. –

+0

Das ist OK für sehr einfache Komponenten, aber wenn Ihre Vorlage substanziell ist (ich arbeite in diesem Fall mit einem komplexen SVG) wird es ein Schmerz – asi

+0

Die berechnete Eigenschaft scheint eine gute Antwort zu sein. Gibt es einen Grund, warum du es nicht eintippen und als akzeptierte Antwort markieren möchtest? –

Antwort

1

Wenn Sie so etwas wie das tun wollen Sie könnten nur die Ereignis-Listener manuell anwenden, indem Sie ein ref auf dem Hinzufügen von element Sie wollen das Ereignis gelten, dann, dass der Ereignis-Listener im mounted Haken zu binden, zu verwenden, wenn die Bedingung erfüllt ist:

Markup

<button ref="button"> 
    Mouse Over Me 
</button> 

Vue Instanz

new Vue({ 
    el: '#app', 
    mounted() { 
    let hasMouse = true; 

    // If the user has a mouse, add the event listeners 
    if (hasMouse) { 
     let button = this.$refs.button 

     button.addEventListener('mouseover', e => { 
     this.mouseover = true 
     }) 

     button.addEventListener('mouseout', e => { 
     this.mouseover = false 
     }) 
    } 

    }, 
    data: { 
    mouseover: false 
    } 
}) 

Hier ist eine JSFiddle dafür: https://jsfiddle.net/0fderek6/

Wenn Sie nicht wie dieser Ansatz, können Sie auch eine directive und legen Sie die bedingte dort nutzen könnten, könnten Sie dann legen Sie das in eine mixin, um es wiederverwendbar zu machen:

Mixin

const mouseEvents = { 
    directives: { 
    mouseEvents: { 
     bind(el, binding, vnode) { 
     let hasMouse = true; 

     if (hasMouse) { 
      el.addEventListener('mouseover', e => { 
      vnode.context.mouseover = true 
      }) 

      el.addEventListener('mouseout', e => { 
      vnode.context.mouseover = false 
      }) 
     } 
     } 
    } 
    }, 
    data: { 
    mouseover: false 
    } 
} 

Vue Instanz

new Vue({ 
    el: '#app', 
    mixins: [mouseEvents] 
}) 

Markup

<button v-mouse-events> 
    Mouse Over Me 
</button> 

Hier ist die JSFiddle dafür: https://jsfiddle.net/nq6x5qeq/

EDIT

Wenn Sie den directive Ansatz mögen, alles, was Sie tun müssen, ist ein unbind Haken fügen Sie den Hörer zu entfernen, können Sie dann die binding arg der Ereignistyp sein und die binding value der Handler:

Vue.directive('mouse', { 
    bind(el, binding) { 
     if (hasMouse) { 
     console.log(binding.arg + ' added') 
      // bind the event listener to the element 
     el.addEventListener(binding.arg, binding.value) 
     } 
    }, 
    unbind(el, binding) { 
     if (hasMouse) { 
     console.log(binding.arg + ' removed') 
     el.removeEventListener(binding.arg, binding.value) 
     } 
    } 
}); 

alles was Sie jetzt tun müssen, ist, jeden Hörer hinzufügen genau wie würden Sie mit v-bind:

<div v-mouse:mouseover="mouseOverFunction"></div> 

Hier ist die JSFiddle Ihnen zu zeigen, wie das funktioniert: https://jsfiddle.net/59ym6hdb/

+0

Das hat einen Nachteil: Es führt keinen vollständigen 'removeEventListener' im' beforeDestroy'-Lebenszyklus-Ereignis aus. Auf vielen Un-Mounts -> mounts können sogar noch schlechter abschneiden, als die Bedingung in das Event selbst zu verschieben, es werden einfach Event-Listener hinzugefügt, ohne sie jemals zu entfernen! –

+0

@AndreiGlinangeu Ich denke, wir haben beide dies zu kompliziert gemacht, Sie müssten die Listener in der Direktive "lösen" (was bedeuten würde, den Status über Direktive-Hooks zu teilen) und Render-Funktionen sind nicht wirklich wartbar. Ich würde mich tatsächlich für die Lösung von @RoyJ entscheiden, die viel einfacher ist. –

+0

Wahr. Aber manchmal müssen Sie ein Ereignis wirklich auf demselben Knoten _really_skipen. Das kann passieren, wenn Sie viel Logik an einen einzelnen DOM-Knoten angehängt haben (wie eine Menge Maus * Listener, die ein einfaches Drag-and-Drop implementieren und Sie zum Beispiel nur das Click-Ereignis überspringen müssen). Natürlich können wir darüber für immer debattieren, ich gab einfach eine einfachere und eine weniger anspruchsvolle alternative Lösung. –

2

Noch einfacher wäre es, Render-Funktionen dafür zu verwenden.Sie müssen die Listener nicht manuell entfernen und sich um sie kümmern. Verwendet auch eine einfache JS-Syntax ohne Mixins.

new Vue({ 
    el: "#app", 
    data:() => ({ 
    counter: 0 
    }), 
    methods: { 
    handleClick() { 
     this.counter++; 
    } 
    }, 
    render(h) { 
    return h(
     "div", 
     IS_MOBILE_DEVICE 
     ? {} 
     : { 
      on: { click: this.handleClick } 
      }, 
     this.counter 
    ); 
    } 
}); 

Voll Beispiel: https://codesandbox.io/s/nw6vyo6knj

+0

Render-Funktionen funktionieren gut für eine sehr einfache Komponente, aber in einigen Fällen (ich arbeite mit einem komplexen SVG) sind nicht zu verwalten – asi

0

this discussion Nach scheint es der beste Weg, dies zu erreichen, ist v-on binden an eine Objektspezifikation, die Ereignisse enthält, die Sie in Ihre Anmeldung interessiert sind und Ihre conditionals dort zu platzieren, wie so:

<div v-on="{ mouseover: condition ? handler : null, click: ... }">

Einige Anmerkungen:

  • Passing null für einen Handler bedeutet, dass die zugrunde liegenden addEventLisetener wird nicht passieren - das ist, was wir wollen
  • Das bedeutet, in eine v-on Attribut alle Ereignisabonnements Gruppierung eher dann in separaten Spalten und explizite Bindungen (<div @mouseover='...' @click='...'/>)

  • Wenn dies eine lange Leben Komponente ist und die zugrunde liegenden Daten ändern häufig (was zu Rebinding) Sie Aufmerksamkeit zahlen sollte zur Verfügung über die Zeichnungen (d. h. die entsprechenden), da die Zeichnungen in einem Bind Pass werden nicht in den nachfolgenden entsorgt werden. Werten Sie nach Ihrem Use Case ...