[Nuxt.js] tab 메뉴 및 검색 구현하기

babypig·2022년 11월 28일
1

Nuxt.js

목록 보기
8/10
post-thumbnail

🔎 검색 구현하기

1. ⚙️ HTML 구조

<template>
  <main class="container">
    <section>
      <h1>FAQ</h1>
      <p>서비스 이용에 불편함이 있으시면 빠르게 도와드리겠습니다.</p>
    </section>
    <section>
      <div>
        <USearch v-model="searchFilter" border-style @search="searchText = searchFilter" @reset="searchFilter = ''" />
        <div class="tabs">
          <button :class="data.find((item) => item.isActive) ? '' : 'is-active'" @click="resetActive">전체</button>
          <button v-for="item in data" :key="item.id" :class="{ 'is-active': item.isActive }" @click="activeTab(item)">
            {{ item.category }}
          </button>
        </div>
      </div>
    </section>
    <section>
      <UPagination
        :data="activeContents"
        :data-option="{ currentPage: faqData.tablePage, pageSize: faqData.pageSize }"
        :is-hide-pagination="activeContents.length === 0"
        @onPageChange="onPageChange"
      >
        <template #data="{ data }">
          <div v-if="!data || !data[0]">
            <p>리스트가 없습니다.</p>
          </div>
          <div v-for="(item, index) in data" :key="index">
            <details>
              <summary>
                <dl>
                  <dd>{{ item.category }}</dd>
                  <dt>{{ item.questions }}</dt>
                </dl>
              </summary>
              <div>{{ item.asked }}</div>
            </details>
          </div>
        </template>
      </UPagination>
    </section>
  </main>
</template>

2. ⚙️ Json 구조

[
  {
    "id": 1,
    "category": "카테고리1",
    "content": [
      {
        "category": "카테고리1",
        "questions": "카테고리1 FAQ입니다.",
        "asked": "답변"
      },
    ]
  },
  {
    "id": 2,
    "category": "카테고리2",
    "content": [
      {
        "category": "카테고리2",
        "questions": "카테고리2 FAQ입니다.",
        "asked": "답변"
      },
    ]
  },
  {
    "id": 3,
    "category": "카테고리3",
    "content": [
      {
        "category": "카테고리3",
        "questions": "카테고리3 FAQ입니다.",
        "asked": "답변"
      }
    ]
  },
  {
    "id": 4,
    "category": "카테고리4",
    "content": []
  },
  {
    "id": 5,
    "category": "카테고리5",
    "content": [
      {
        "category": "회원",
        "questions": "회원 FAQ입니다.",
        "asked": "답변"
      },
    ]
  }
]

3. ⚙️ script

  • asyncData로 data에 isActive를 추가
  asyncData() {
    const data = cloneDeep(FaqJson);
    data.forEach((item) => {
      item.isActive = false;
    });
    return { data };
  },
  • v-model 및 search, reset에 사용할 data 추가
  data() {
    return {
      data: null,
      searchText: '',
      searchFilter: '',
    };
  },
  • filter에 사용할 methods 및 tab active 효과를 위한 함수 작성

  methods: {
    searchFilters(item) {
      return (
        item.questions.toUpperCase().includes(this.searchText.toUpperCase()) ||
        item.asked.toUpperCase().includes(this.searchText.toUpperCase())
      );
    },
    resetActive() {
      this.data.forEach((item) => {
        item.isActive = false;
      });
    },
    activeTab(item) {
      this.resetActive();
      item.isActive = true;
    },
  }
  • data에 isActive가 true일때 searchFilters를 실행 하고 전체일경우에는 isActive가 null이기 때문에 Nullish coalescing operator로 reduce 함수를 활용하여 전체값을 넣어줌.
  computed: {
    activeContents() {
      return (
        this.data.find((item) => item.isActive)?.content.filter(this.searchFilters) ??
        this.data.reduce((pre, cur) => {
          pre.push(...cur.content.filter(this.searchFilters));
          return pre;
        }, [])
      );
    }
  },

추가 수정 💫

문제점 : 검색 시 tab 전체 메뉴로 이동을 해야하는데 이동하지않고 해당 active 된 텝에서
검색이 이루어지며, 페이지네이션 이동할 시 초기화해주는 page 값을 초기화하는걸 넣어주지 않아 제대로 된 출력이 되지 않고있었음.

✅ 검색 시 tab 전체 메뉴로 이동을 해야하는데 이동하지않고 해당 active 된 텝에서
검색이 이루어지지 않는 문제는 검색과 초기화를 함수로 분리하여 해결

✅ 페이지네이션 이동할 시 초기화해주는 page 값을 초기화하는걸 넣어주지 않아 제대로 된 출력이 되지 않고있던 문제는 activeTab에 tablePage 값을 0으로 만들어주는것을 추가

  <USearch v-model="searchFilter" border-style @search="searchText = searchFilter" @reset="searchFilter = ''" />
 <!--수정-->
  <USearch v-model="searchFilter" border-style @search="onSearch" @reset="onReset" />
  computed: {
    activeContents() {
      const isAll = !this.data.find((item) => item.isActive);

      return this.data
        .reduce((pre, cur) => {
          if (isAll) {
            pre.push(...cur.content);
          } else if (cur.isActive) {
            pre.push(...cur.content);
          }

          return pre;
        }, [])
        .filter(this.searchFilters);
    }
  },
 methods: {
    onSearch() {
      this.resetActive();
      this.searchText = this.searchFilter;
    },
    searchFilters(item) {
      return (
        item.questions.toUpperCase().includes(this.searchText.toUpperCase()) ||
        item.asked.toUpperCase().includes(this.searchText.toUpperCase())
      );
    },
    onReset() {
      this.searchFilter = '';
      this.searchText = '';
      this.resetActive();
    },
    resetActive() {
      this.data.forEach((item) => {
        item.isActive = false;
      });
    },
    activeTab(item) {
      this.resetActive();
      item.isActive = true;
      this.faqData.tablePage = 0;
    },
    onPageChange(page) {
      this.faqData.tablePage = page;
    }
  }
profile
babypig

0개의 댓글