Фильтр поиска клиентов Vue JS в режиме реального времени по результатам Laravel с разбивкой по страницам

#php #laravel #vue.js #nuxt.js

Вопрос:

У меня возникли некоторые проблемы с добавлением фильтрации поиска в реальном времени на стороне клиента в мой проект Nuxt JS, который извлекает данные из моего API Laravel 8. Мой API Laravel 8 использует paginate() метод для разбиения моих результатов на страницы из моей базы данных и возврата некоторого JSON, в котором data массив содержит все записи для текущей страницы.

Мой интерфейс содержит таблицу с v-for циклическим отображением некоторых отфильтрованных результатов, filteredDomains которые, по сути, получают данные после того, как запрос к моему API получает результаты.

Дело в том, что мой поиск в реальном времени на строительство, но только на первой странице, и я хотел бы, чтобы это фильтр и поиск по всем страницам, как если бы они были одной странице (что они и ранее были, и он работал раньше, теперь, когда он с разбиением на страницы, это не делает)

Что я упускаю?

 <b-row>
  <b-col>
    <b-form-input
      style="padding-right: 10rem;"
      v-model="search.domain"
      type="text"
      size="lg"
      maxlength="256"
      placeholder="Start typing a domain...">
    </b-form-input>
  </b-col>
</b-row>
<b-row>
  <b-col>
    <div class="table-responsive">
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th scope="col"></th>
            <th scope="col">Domain</th>
            <th scope="col">Registrar</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(domain, index) in filteredDomains" :key="index" class="domain-row">
            <td class="text-center" style="min-width: 60px;">
              ...
            </td>
            <td :style="domain.has_valid_ssl == 1 ? 'min-width: 260px;' : 'min-width: 150px;'">
              ...
            </td>
            <td style="min-width: 150px;">
              ...
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </b-col>
</b-row>
<b-row>
  <b-col>
    <b-pagination
      v-model="page"
      :total-rows="domains.total"
      :per-page="domains.per_page"
      first-number
      last-number
      pills
    ></b-pagination>
  </b-col>
</b-row>
 

И мой сценарий таков:

 export default {
  data () {
    return {
      page: this.$route.query.page ? this.$route.query.page : 1,
      hasErrors: false,
      isLoading: true,
      search: {
        domain: ''
      },
      domains: []
    }
  },
  created () {
    this.getDomains()
  },
  methods: {

    /*
    ** Get domains
    */
    getDomains () {
      this.hasErrors = false

      this.$axios.get(`${process.env.API_URL}/api/domains?page=${this.page}`, { timeout: 60000 }).then(res => {
        if (res.data.success) {
          this.isLoading = false
          this.hasErrors = false

          const domains = res.data.domains.data.length > 0 ? res.data.domains : []
          if (domains.data amp;amp; domains.data.length > 0) {
            for (const [index, domain] of domains.data.entries()) {
              if (typeof domain.registrar != 'string' || domain.registrar == null) continue
              domain.registrar = JSON.parse(domain.registrar)
            }
          }

          this.domains = domains
        }
      }).catch(err => {
        this.isLoading = false
        this.hasErrors = true
      })
    }

    /*
    ** Navigate to a page
    */
    navigateToPage (page) {
      try {
        const goTo = page

        this.page = parseInt(goTo)
        this.$router.push({
          query: {
            page: this.page
          }
        })

        this.getDomains()
      } catch (err) {}
    }

  },
  computed: {

    /*
    ** Filter domains for searchable domains
    */
    filteredDomains () {
      if (!this.domains.data) {
        return []
      }
      return this.domains.data.filter(domain => {
        return domain.domain.toLowerCase().includes(this.search.domain.toLowerCase())
      })
    }

  },
  watch: {
    page (val) {
      this.navigateToPage(this.page)
    }
  }
}
 

Может быть, мне нужно выполнить for цикл внутри моей filteredDomains функции, чтобы построить список всех доменов и отфильтровать этот список? Хотя это кажется излишним…

Ответ №1:

При том, как вы в настоящее время используете разбиение на страницы, в ваших данных есть только одна страница доменов, возвращаемая из API. По умолчанию это будет первая страница, поэтому ваш код ведет себя так, как ожидалось.

Чтобы исправить это, я вижу два варианта:

  1. Соберите все домены и отфильтруйте их (как вы сами предложили). Вы также можете реализовать разбиение на страницы на стороне клиента, если хотите сохранить эту функцию.
  2. Передайте свой запрос на поиск в свой API. Таким образом, фильтрацию необходимо будет выполнить на стороне сервера, и будут возвращены только отфильтрованные домены.

Какой из них лучше подходит для вашей ситуации, зависит от количества предметов. Если предметов относительно мало, я бы предложил первый вариант, так как он сохраняет всю логику в одном месте. Однако, если есть сотни или даже тысячи элементов, я бы предложил второй вариант, поскольку базы данных часто лучше оптимизированы для выполнения такого рода фильтрации.

В любом случае вам следует подумать о добавлении отмены, чтобы повысить производительность. Без отмены подписки ваши данные будут просматриваться снова и снова после каждой отдельной буквы, которую пользователь вводит в поле поиска. При отключении данные будут отфильтрованы только после того, как пользователь перестанет печатать в течение определенного промежутка времени (обычно около 300 мс).

Комментарии:

1. Спасибо за это, это помогает мне проверить мое направление!