1

I am new to Vue and with the help of the docs and the Vue community on S.O. I have been making progress in learning how to make more complicated components.

This question is in regards to a custom select component, where the data for the selected option is correct, but the displayed HTML is not. I am providing a M.W.E.; however, this only occurs for me when the custom select component is nested through several other components.

I know this is a lot of code, so the M.W.E. is provided via Code Sandbox, where each component is in it's own file (making it easier to read).

Description of component

enter image description here

The component is meant to be an advanced data filter, that accepts an object that simulates a database, e.g. each key is a the "id" of its corresponding value ("record"). It then allows one to filter on the keys (properties / fields) of the record.

Thus we have the top-most component AdvancedFilterTable. This table allows the user to dynamically add and remove filters AdvancedFilterRows.

A filter row houses 5 simple components:

  • logic: is the filter and / or (AdvancedFilterSelectLogic)
  • function: what to be applied to the field (AdvancedFilterSelectFunction)
  • property: which field to filter on (AdvancedFilterSelectProperty)
  • conditional: what test to apply to each records afore set field (AdvancedFilterSelectConditional)
  • value: the value to test in the conditional (AdvancedFilterInput)

e.g.

{logic: 'and', function: 'identity', property: 'x', conditional: 'gt', value: 5}

The feature that I added which introduced the bug is dynamic options for AdvancedFilterSelectFunction and AdvancedFilterSelectConditional based on the type of AdvancedFilterSelectProperty.

e.g. if the selected property is x, where the corresponding value thereof is an array of numbers, then an option inAdvancedFilterSelectFunction should include "mean", "max", etc and AdvancedFilterSelectConditional should include "includes", "not includes", etc. However, if "mean" is selected (applying the function to the property changes its type) then the conditionals should change again (this time dropping "includes" and "not includes").

In the MWE provided, I have the following dummy data:

let records = {
  a: { x: 1, y: "a string", z: [1, 2, 3, 4] },
  b: { x: 2, y: "strange?", z: [1, 2, 4] },
  c: { x: 3, y: "starts w", z: [1, 2, 3, 4] },
  d: { x: 10, y: "some let", z: [1, 2, 4, 5, 6, 7, 8] },
  e: { x: 2, y: "? qwerty", z: [1, 40] }
};

Where the type of x is number, y is string, and z in array number.

How to produce bug

  1. open the mwe Code Sandbox enter image description here

  2. click the green plus icon to add a new filter

enter image description here

  1. click the property select and change x to z

enter image description here

Checking the components, all the corresponding data is set correctly. This is further emphasized as the function select and the conditional select have dynamically changes to include options specific to array type data.

Nonetheless, the select still shows 'x'. Trying to then select 'x' to reset the selection does not work as it is still selected to x...

The custom select's all pass their data through the different encapsulating components as outlined in Vue2: handling multi-child prop synchronization with models with the answer's fiddle: https://jsfiddle.net/SumNeuron/ejq86c73/9/

solution: https://61qky5y6mr.codesandbox.io/

2 Answers 2

2

In AdvancedFilterSelectProperty.vue, you have a prop selected. That changes as the user changes the selection. But, the selected property of the <select /> element itself is checking the expression key == value

The value comes from data of the component. The data does not update when the user changes the selection, but selected prop certainly does.

So, instead of checking against value in data, check against selected prop.

<select @change='updateSelected($event)'>
    <option
        v-for='(val, key) in options'
        :value='key'
        :key="key"
        :selected='key == selected'
    >
        {{val.text}}
    </option>
</select>

That solves your issue.

Check working example at https://codesandbox.io/s/38vlwq4yx6

Sign up to request clarification or add additional context in comments.

10 Comments

This correct, so +1, but a perhaps better solution is updated the updateSelect function to include this.value = event.target.value;
Not sure, why have two properties that indicate the same thing. value and selected. Just keep one source of truth, easier to reason about.
Also, check vuejs.org/v2/guide/components.html#Using-v-model-on-Components. I did not go through your entire setup. So not sure if this is applicable for you. But whenever I am making components like AdvancedFilterSelectProperty.vue, I also use v-model on such components.
I would prefer to have just one but in a previous question of mine stackoverflow.com/questions/50853417/… I learned that Vue does not like prop mutated by the component
AS for the v-model on components, I did read that and hence the custom @input function updateSelect as suggested in the documentation.
|
1

Your select inputs don't actually have any model associated with them, so the value is never shown, it's broadcast though. However since you're using props to delegate the value juggling from the parent, you actually need a computed property to handle this, something like this:

computed: {
  selectedVal: {
    get() {
      return this.selected
    },
    set(value) {
      this.$emit('updateSelected', value)
    }
  }
}

Then apply that to your select (at least the AdvancedFilterSelectProperty component):

<select v-model="selectedVal">

There's no need for the @input function here as our setter is $emitting to our parent when we select an <option> as the value is changing

2 Comments

If it isnt too much work, may I ask for you to fork the example and put this in? I am just a bit confused as to exactly where it goes and how to use. In the Vue docs they suggest specifying a custom model via model: {prop: <prop>, event:<event>} and I am not sure why it must be so much work to have top level access to nested component values. I would think it would be feasible with the .sync modifier, but I can not get it to work and their example with event name 'update:title' does not work for me :/
Is there a better way to do this? (both the data syncing and the organization of components). I am new to Vue and I figured this was the right delegation of components, but perhaps I am mistaken?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.