0

I'm displaying 3 drop-down menus. Depending on what subject is chosen in the select menu one affects the subjects that should display in the other 2 select menus.

For example, if "Economics" is selected in drop-down one then "Marketing" and "Mathematics" should not display in the other menus. There are 7 different scenarios to cover in total, similar to this one. Can anyone help me get off the ground with this, please?

<template>
  <select v-model="one">
    <option v-for="subject in subjects">
      {{ subject }}
    </option>
  </select>
  <select v-model="two" :disabled="!one">
    <option v-for="subject in subjects.filter(item => item.split(' ')[0].indexOf(this.one.split(' ')[0]))">
      {{ subject }}
    </option>
  </select>
  <select v-model="three" :disabled="!two">
    <option v-for="subject in subjects.filter(item => item.split(' ')[0].indexOf(this.one.split(' ')[0]) && item.split(' ')[0].indexOf(this.two.split(' ')[0]))">
      {{ subject }}
    </option>
  </select>
  
  <div>
    Selected: {{one}} {{two}} {{three}}
  </div>
</template>

<script>
export default {
  data() {
    return {
      subjects: [
        "Education",
        "Economics",
        "English",
        "English & Creative Writing",
        "French",
        "History",  
        "Law",
        "Marketing",
        "Mathematics",  
        "Psychology", 
        "Spanish"
      ],
      one: "",
      two: "",
      three: "",
    }
  }
}
</script>
2
  • Do you have an exclusion list of which topics should be excluded based on what's selected? Commented Oct 14, 2021 at 13:13
  • Yes, i do but I was going to try and do at least some myself. make me feel a little useful :) I really appreciate the help though. Some of the examples of what I'm trying to do are as follows: 1. if you select "English" or "English & CW" in any menu, then neither should be in any other menu 2. If you select either "Economics" or "Marketing" or "Mathematics", none of these should be in any other menu 3. If you select "Psychology", then all subjects apart from "French" and "History" should be hidden as a second choice. 3rd choice can be "Economics", "Spanish", Marketing. Commented Oct 14, 2021 at 13:38

1 Answer 1

1

The general idea is to map up the relation between what I would call "top level subjects" or topics and the "sub level subjects" or sub-topics.

If you know you only ever will have three inputs (at max), then doing a mapping for a pre-populated array is ok. However, if you're looking at dynamically adding more levels, you would need to put down more work. That being said, I've looked at this briefly and added a subject <-> topic relation. Each entry has the parent attribute which defines if they have one or more. If they don't, they're a "top level subject" or topic. If they do, they're a sub-topic.

By handling the onChange event for each select, you can figure out what ID the subject you selected has and find the corresponding entry in the subjects object array. Once you found that, you filter all entries that have parents !== null and match every ID in their parent array. This is calculated using a property, which is ideal for such purposes.

HTML

<div id="app">
  <select
    v-model="selected[0].id"
    @change="selectTopic($event)"
   >
    <!-- List first only the top level topics -->
    <option 
      v-for="(subject, subjectKey) in topics"
      :key="`subject-${subjectKey}`"
      :value="subject.id"
     >
      {{ subject.name }}
    </option>
  </select>

  <select
    v-if="nextLevelSubjects"
    v-model="selected[1].id"
     @change="selectTopic($event)"
  >
    <!-- List first only the top level topics -->
    <option
      v-for="(subject, subjectKey) in nextLevelSubjects"
      :key="`next-level-subject-${subjectKey}`"
      :value="subject.id"
     >
      {{ subject.name }}
    </option>
  </select>

  <div v-if="someSelectionHasBeenMade">
    <pre>
      All topics selected: {{ selected }}
    </pre>
  </div>
</div>

Vue Code (Excerpt)

  data: {
      someSelectionHasBeenMade: false,
      nextLevelSubjects: null,
      selected: [
      {
        id: null
      },
      {
        id: null
      },
      {
        id: null
      }
      ],
      subjects: [
      {
        id: 1,
        name: "Education",
        parent: null
      },
      {
        id: 2,
        name: "English",
        parent: [1]
      },
      {
        id: 3,
        name: "English & Creative Writing",
        parent: [1]
      },
      {
        id: 4,
        name: "French",
        parent: [1]
      },
            {
        id: 5,
        name: "Economics",
        parent: null
      },
      {
        id: 6,
        name: "Law",
        parent: [5]
      },
            {
        id: 7,
        name: "Marketing",
        parent: [5]
      },      
      ]
  },
  computed: {
    topics () {
        return this.subjects.filter(e => e.parent === null)
    }
  },
  methods: {
    selectTopic (event) {
        this.someSelectionHasBeenMade = true
      const parentId = Number.parseFloat(event.target.value)
      this.nextLevelSubjects = this.subjects.filter(e => e.parent !== null).filter(i => i.parent.every(p => p === parentId))
    }
  }
})

I've made a JSFiddle here: https://jsfiddle.net/Coreus/md7ktj6e/2/

Making a sub-topic be parent to several topics

If you want a sub-topic to match either one of more subjects / topics, put in several IDs in the parent property for the sub-topic in question and change the filtering by parent to some. Like so:

  subjects: [
      {
        id: 1,
        name: "Education",
        parent: null
      },
      {
        id: 2,
        name: "Languages",
        parent: null
      },
      {
        id: 3,
        name: "English",
        parent: [1, 2]
      },
       ....
      {
        id: 4,
        name: "French",
        parent: [1, 2]
      },
      {
        id: 5,
        name: "History",
        parent: [1]
      },

By the example above, the relation tree looks like so:

Education

  • English
  • French
  • History

Languages

  • English
  • French

Then change the filtering to use some instead of every.

  this.nextLevelSubjects = this.subjects.filter(e => e.parent !== null).filter(i => i.parent.some(p => p === parentId))

Edit: Code example has been updated

Please note this just illustrates purpose. You obviously need to put down more work for it to work on additional tiers (sub-topics) down the line. It should, however, give you an idea on how to approach this further.

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

5 Comments

Thank you. This looks great I will check it out!
Is it possible to have the subject with multiple 'parent; id's? I have tried but cant seem to get this to work easily. For example most of the subjects need to display multiple time. E.G 'Education' needs to appear In menu 2 for all subjects, 'Economics' should only appear for some. I tried parent: [1 || 2] but it doesn't seem to like it. Any ideas? Thanks
Sure. If the parent contains i.e [5, 7] you could infer that those sub-topics refer to one of them and changing i.parent.every to i.parent.some.... etc. Also, if you found this answer helpful, please consider marking it as accepted to guide others who are looking for a similar approach and of course as a boon for those that put time into the answer.
The answer has been updated with expanded details, see above.
Great thnk you. I will give this a go.

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.