Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Vue to listen to DOM events outside of the app

I need to use Vue.js to progressively enhance an existing server-side rendered form. I need to alter the <select>'s <options> depending on the value of another field on the form.

As I see it, I cannot mount Vue on the parent <form> (or another parent element) as I lose all my server-side rendered content, which I need to maintain.

I have mounted on the <select> that I wish to make dynamic. This allows me to dynamically generate <option>s however I wish. However the other form input element (a sibling-ish of the mounted <select>) is now outside of my Vue scope. How can my Vue app be alerted of changes to the dependent field so it can update my <option>s accordingly?

As I understand it, Vue has its own events system so I'm going to have to hook up a DOM event listener myself - which is completely fine. That's about as far as I've gotten.

Aside: I understand that given infinite time, money, and resources, I should follow the trend of having my entire user experience be an SPA which would solve this issue in a rocket-launcher-to-hammer-a-nail sort of way. However this is not an option. I must progressively enhance the SSR output.

like image 450
Kye Russell Avatar asked Sep 05 '18 09:09

Kye Russell


1 Answers

You could still query the document for sibling element with document.querySelector(), and call addEventListener() from within a Vue instance:

const ALL_DATA = [
  {id: 1, value: 1, text: 'One'},
  {id: 2, value: 2, text: 'Two'},
  {id: 3, value: 3, text: 'Three'},
];

new Vue({
  el: '#app',
  data: () => ({
    options: [
      ...ALL_DATA,
    ]
  }),
  mounted() {
    document.querySelector('#option-type').addEventListener('change', e => {
      switch (e.target.value) {
        case 'even':
          this.options = ALL_DATA.filter(x => x.value % 2 === 0);
          break;

        case 'odd':
        default:
          this.options = ALL_DATA.filter(x => x.value % 2 !== 0);
          break;
      }
    })
  }
});

document.getElementById('myForm').addEventListener('submit', e => {
  e.stopPropagation();
  e.preventDefault();
});
<script src="https://unpkg.com/[email protected]"></script>

<form action="#" id="myForm">

  <fieldset id="option-type">
    <label>Even
      <input name="option-type" type="radio" value="even">
    </label>
    <label>Odd
      <input name="option-type" type="radio" value="odd">
    </label>
  </fieldset>

  <select name="option" id="app">
    <option v-for="o in options"
            value="o.value"
            :key="o.id">{{o.text}}</option>
  </select>
</form>
like image 75
tony19 Avatar answered Oct 19 '22 21:10

tony19