1

I have been working on a FAQ page that has categories and once you have selected a category, it shows questions users have asked. You can then select a question to display the answer. There are 7 categories and each has an array named "questionList" that has various amounts of questions. I am creating a search bar that will filter through ALL questions in each category and display the question and answer that matches the search. Right now the filtered list only displays the first categories questions....

Here is an example of how my data is setup

categoryList : [
           {
             category: 'Category Title',
             questionList: [
                      {
                         question: 'Question',
                         answer: '<p>copy goes here</p>'
                       }, ... 
                     ]
            },
            {                 
               category: 'Next Title',
               questionList: [
                       { 
                         question: 'Question',
                         answer: '<p>copy goes here</p>'
                       },
                     ]
             }, ...

I was looking at another stackoverflow solution and I really could not figure out how to have it function with my multiple nested arrays. Here is the html:

<div class="filtered" v-for="qa in filteredList">
     <div class="filter-container">
          <h3>{{ qa.question }}</h3>
          <div v-html="qa.answer"></div>
     </div>
</div>

and my computed function:

filteredList() {
      for (i = 0; i < this.categoryList.length; i++) {

            var list =  this.categoryList[i];

            for (j = 0; j < list.questionList.length; j++ ) {

                    return list.questionList.filter(qa => {
                                 return qa.question.toLowerCase().includes(this.search.toLowerCase());
                        })
                   }
              }
          }

I'm not sure if I am even close to the correct solution or waaaay off here... please help!

3 Answers 3

1

You're returning abit too early, use filter then loop over the sub-items, though your HTML part is only looping over the main categories, you want two loops for that too.

const component = {
  template: `
  <div>
    <input v-model="search"/>

    <ul v-for="qa in filteredList">
      <li>
        {{qa.category}}
          <ul v-for="qa in qa.questionList">
            <li>
              {{qa.question}}:<br>
              <span v-html="qa.answer"></span>
             </li>
          </ul>
       </li>
    </ul>
  </div>
  `,
  computed: {
    filteredList() {
      return this.categoryList.filter(item => {
        for (const {question, answer} of item.questionList) {
          if (
            question.indexOf(this.search) > -1 ||
            answer.indexOf(this.search) > -1
          ) {
            return item
          }
        }
      })
    }
  },
  data() {
    return {
      search: '',
      categoryList: [
        {
          category: 'Category Title',
          questionList: [
            {
              question: 'Question',
              answer: '<p>copy goes here</p>',
            },
          ],
        },
        {
          category: 'Next Title',
          questionList: [
            {
              question: 'Question',
              answer: '<p>copy goes here</p>'
            }
          ]
        }
      ]
    }
  }
}

Edit (filter questions):

Using bog standard for loops, create a new copy of the array, loop over and only push the new item if questionList contains matches.

filteredList() {
  const ret = []
  for (const item of this.categoryList) {
    const t = {
      ...item,
      questionList: []
    }
    for (const q of item.questionList) {
      if (
        q.question.indexOf(this.search) > -1 ||
        q.answer.indexOf(this.search) > -1
      ) {
        t.questionList.push(q)
      }
    }
    if (t.questionList.length) ret.push(t)
  }
  return ret
},
Sign up to request clarification or add additional context in comments.

2 Comments

when i run this and type in "clean" ( my data has two questions under two separate categories that have the word "clean" in it), my console states there are 2, but it displays ALL the questions and answers for those two categories. Do I need to add an extra line of code telling it to only display the ones that have that word in it?
i merged this with my code that allows you to open and close the categories and questions. It is working beautifully!
0

Right now the filtered list only displays the first categories questions....

That's because you return the filtered array of first category in the for-loop.

Note that computed properties are functions. They are done when they return something.

What you should do is to iterate all QAs and create a new array whose elements have question text including keywords.

JSFiddle DEMO

Additional Info: Array.prototype.forEach() is a kind of functional way of for-loop without break (cf. Array.prototype.some()).

1 Comment

ahh! okay, this worked! thank you. I knew i had to run through both lists, but could not wrap my brain around how to display all. I didn't even think of putting them all into a new array.
0

This is the code:

data()
{
  return {
    searchTerm: '',
  }
},
computed:
{
  allQuestions()
  {
    // un-nest all questions as a flat array
    return this.categoryList
      .map(category => category.questionList)
      .reduce((acc, item) => acc.concat(item), []);
  }
  filteredList()
  {
    const term = this.searchTerm.toLowerCase();
    if(!term) return this.allQuestions;
    // only questions or answers which contain the term will be returned
    return this.allQuestions.filter(qa => qa.question.toLowerCase().indexOf(term) >= 0 
      || qa.answer.toLowerCase().indexOf(term) >= 0);
  }
}

1 Comment

When I add this to my code, it is getting the error "questionList is not defined". Would i have to type in the direct path where you have .map(category => questionList)?

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.