Aurelia repeat.for связать вопросы для нумерации страниц

Я все еще новичок в Аурелии и выясняю, что это связывает, и сейчас я сталкиваюсь с несколько любопытной проблемой.

Основная предпосылка заключается в том, что я работаю над веб-приложением фида обновлений, которое извлекает данные из API. Данные разбиты на куски (карточки), которые доступны для просмотра. Есть также компоненты поиска / сортировки для пользователей, чтобы фильтровать карты. Это где я сталкиваюсь с проблемой...

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

Вот код (включая почти все, если это необходимо):

Обновление-feed.html

<!--Main view component for the update feed. Will house all cards generated from update-feed.ts-->
<template>

  <!--Custom loader-->
  <load-screen if.bind="router.isNavigating || loading"></load-screen>

  <!--Search component-->
  <search-feed></search-feed>

  <!--Loader while fetching results-->
  <h1 if.bind="!results" class="default-msg" textContent.bind="defaultMessage"></h1>

  <!--If tasks found bind the pager-->
  <div if.bind="!loading && results" id="feed-main" role="main" class="container-fluid clear-top">

    <!--Pass data to pager to handle pagination-->
    <pager>
      <!--<card-commit></card-commit> //pager.html-->
    </pager>

  </div>

</template>

ДОПОЛНЕНО feed.ts

import BuildDash from '../../../../services/build-dash';
import {FeedData} from '../../../../services/feed-data';
import {bindable, inject} from 'aurelia-framework';

@inject(BuildDash, FeedData)
export class UpdateFeed {

  /**
   * Class will handle retrieving and displaying all card data
   */
  public buildDash;
  public loading: boolean;
  public results: boolean;
  public defaultMessage: string;
  public feedData: FeedData;

  // Prop for unit-test overriding of cardData with mockData
  public taskList: Array<any>;

  constructor(BuildDash, FeedData) {
    this.buildDash = BuildDash;
    this.feedData = FeedData;

    // Start loader
    this.loading = true;
    this.results = false;
    this.getDefaultMsg();
  }

  public activate() {

    /**
     * Using a setTimeout until a way to tap into the activate() method is solved.
     * This throws an error in unit tests 'Unhandled rejection Error: Not Found'
     * This error can be ignored since it calls a stubbed version of this in testing for the actual unit tests.
     */
    return setTimeout(() => this.getData(), 0);
  }

  public getDefaultMsg() {
    // Default message if there are no cards to display
    this.defaultMessage = this.loading ? 'Searching for tasks...' : 'You currently have no projects needing updated';
  }

  public getData() {

    // TODO: buildDash.build(portfolioId), portfolioId needs to be grabbed from current embedded portfolio page
    return this.buildDash.build(portfolioId).then(res => {

      // Primary data assignment
      return this.feedData.load(res.data);
    })
      .then(() => {
        console.log('Fetch complete');

        // Stop loader
        this.loading = false;

        // Cast FeedData to bool for results
        this.results = !!this.feedData.cardList;

        return this.getDefaultMsg();
      });

  }

}

поиск-feed.html

<template>

  <div class="container-fluid">
    <div class="search-bar">
      <form class="form-inline projector-forgive">

        <div class="refresh-btn">
          <button role="button" class="btn btn-primary form-inline projector-forgive" click.delegate="refreshFeed()">
            <span class="glyphicon glyphicon-refresh" aria-hidden="true"></span>
            Refresh Feed
          </button>
        </div>

        <div class="search-input">
          <select class="form-control" value.bind="selectedSort" change.delegate="sortDropDown()">
            <option repeat.for="option of sortOptions" model.bind="option.val">${option.text}</option>
          </select>

          <input class="form-control" type="search" placeholder="Search" value.bind="searchQuery" input.delegate="searchInput()"/>
        </div>

      </form>
    </div>
  </div>

</template>

Поиск-feed.ts

import {bindable, inject} from 'aurelia-framework';
import {DashBoard} from '../../pages/pmdash/pmdash';
import {FeedData} from '../../services/feed-data';

@inject(DashBoard, FeedData)
export class SearchFeed {

  @bindable public searchQuery: string;
  @bindable public selectedSort: string;
  @bindable public sortOptions: Array<object>;
  public data: FeedData;
  public router: any;

  constructor(DashBoard, FeedData) {
    this.router = DashBoard.router;
    this.data = FeedData;

    this.sortOptions = [
      {'val': 'commLate',  'text': 'Commit Date Late'},
      {'val': 'commEarly', 'text': 'Commit Date Early'},
      {'val': 'taskAsc',   'text': 'Task Name (Ascending)'},
      {'val': 'taskDesc',  'text': 'Task Name (Descending)'},
      {'val': 'projAsc',   'text': 'Project Name (Ascending)'},
      {'val': 'projDesc',  'text': 'Project Name (Descending)'}
    ];

    this.searchQuery = sessionStorage.getItem('Query') ? sessionStorage.getItem('Query') : '';

    if (sessionStorage.getItem('Dropdown')) {
      this.selectedSort = sessionStorage.getItem('Dropdown');
    }

  }

  public refreshFeed() {
    // Full refresh of feed components
    this.router.navigateToRoute(
      this.router.currentInstruction.config.name,
      this.router.currentInstruction.params,
      { replace: true }
    );
  }

  public sortDropDown() {
    sessionStorage.setItem('Dropdown', this.selectedSort);
    return this.searchInput();
  }

  public searchInput() {
    // console.log(this.searchQuery);
    return this.data.getFeedData(this.searchQuery);
  }
}

Ходовые data.ts

import {observable, noView} from 'aurelia-framework';

@noView()
export class FeedData {

  /**
   * Class to represent all card data in the update-feed.
   * Used to filter and render search results without modifying original API data
   */
  public cardListMaster: Array<any>;
  public cardList: Array<any>;
  public loadFlag: boolean;
  @observable public temp: Array<any>;

  constructor () {
    console.log('FeedData constructor');
    this.cardList = [];
    this.loadFlag = true;
  }

  public tempChanged() {
    // console.log('tempChanged: length', this.temp);
  }

  public load(data) {
    /**
     * Method used to prepare data for views during API calls
     */
    this.cardListMaster = data;
    this.temp = this.cardListMaster.slice();
    let query = sessionStorage.getItem('Query');
    return this.getFeedData(query);
  }

  public getFeedData(query) {

    let sort = sessionStorage.getItem('Dropdown');

    switch (sort) {
      case 'commLate':
        return this.sortCommitLate(query);
      case 'commEarly':
        return this.sortCommitEarly(query);
      case 'taskAsc':
        return this.sortTaskAsc(query);
      case 'taskDesc':
        return this.sortTaskDesc(query);
      case 'projAsc':
        return this.sortProjAsc(query);
      case 'projDesc':
        return this.sortProjDesc(query);
      default:
        return this.sortCommitLate(query);
    }
  }

  public sortCommitLate(query) {
    return this.searchInput(query).sort((a, b) => b['DE:pln_to_commit_delta'] - a['DE:pln_to_commit_delta']);
  }

  public sortCommitEarly(query) {
    return this.searchInput(query).sort((a, b) => a['DE:pln_to_commit_delta'] - b['DE:pln_to_commit_delta']);
  }

  public sortTaskAsc(query) {
    return this.searchInput(query).sort((a, b) => {

      const taskNameA = a.name.toLowerCase();
      const taskNameB = b.name.toLowerCase();

      if (taskNameA < taskNameB) {
        return -1;
      }
      if (taskNameA > taskNameB) {
        return 1;
      }
      return 0;
    });
  }

  public sortTaskDesc(query) {
    return this.searchInput(query).sort((a, b) => {

      const taskNameA = a.name.toLowerCase();
      const taskNameB = b.name.toLowerCase();

      if (taskNameA < taskNameB) {
        return 1;
      }
      if (taskNameA > taskNameB) {
        return -1;
      }
      return 0;
    });
  }

  public sortProjAsc(query) {
    return this.searchInput(query).sort((a, b) => {

      const projNameA = a.project.name.toLowerCase();
      const projNameB = b.project.name.toLowerCase();

      if (projNameA < projNameB) {
        return -1;
      }
      if (projNameA > projNameB) {
        return 1;
      }
      return 0;
    });
  }

  public sortProjDesc(query) {
    return this.searchInput(query).sort((a, b) => {

      const projNameA = a.project.name.toLowerCase();
      const projNameB = b.project.name.toLowerCase();

      if (projNameA < projNameB) {
        return 1;
      }
      if (projNameA > projNameB) {
        return -1;
      }
      return 0;
    });
  }

  public searchInput(query) {
    query = !query ? '' : query.toLowerCase();

    this.temp = this.cardListMaster.slice();
    let masterCopy = this.cardListMaster.slice();

    if (sessionStorage.getItem('Query') === query && !this.loadFlag) {

      return this.cardList;

    } else {

      sessionStorage.setItem('Query', query);

      let filteredList = masterCopy.filter(card => {
        for (const key in card) {
          if (String(card[key]).toLowerCase().includes(query)) {
            // console.log(card);
            return card;
          }
        }
      });

      this.loadFlag = false;
      Array.prototype.splice.apply(this.temp, [0, this.temp.length].concat(filteredList));
      return Array.prototype.splice.apply(this.cardList, [0, this.cardList.length].concat(this.temp));
    }

  }

}

ОСНОВНОЙ ВЫПУСК КОМПОНЕНТА pager.ts

import {inject, observable} from 'aurelia-framework';
import {FeedData} from '../../services/feed-data';

@inject(FeedData)
export class Pager {

  public feed: FeedData;
  public feedData: Array<any>;
  public feedLength: number;
  public pageList: number;
  public cardsPerPage;
  public currentPage;
  public load: boolean;

  constructor(FeedData) {
    this.feed = FeedData;
    // this.loadPager();

    console.log('loadPager called');

    this.feedData = this.feed.cardList;
    this.feedLength = FeedData.cardList.length;

    this.cardsPerPage = 20;
    this.currentPage = 1;
    this.pageList = Math.ceil(this.feedLength / this.cardsPerPage);
    // FIXME: pageList not updating!
    // I've tried referencing this property just about every way I know how to, but no matter what it does not update like 'feedData' does automatically in the view

    console.log(this.pageList);

  }
}

pager.html

<template>

  <!--THIS WORKS-->
  <!-- feedData updates automatically with the model-->
  <template repeat.for="task of feedData">

    <!--Bind each tasks data to a card as we loop-->
    <card-commit

      task-id.bind="task.ID"
      task-name.bind="task.name"
      project-name.bind="task.project.name"
      assigned-to.bind="task.assignedTo.name"
      successors.bind="task.successors"
      commit-delta.bind="task['DE:pln_to_commit_delta']"
      commit-status.bind="task.commitStatus"
      planned-start-date.bind="task.plannedStartDate"
      planned-comp-date.bind="task.plannedCompletionDate"
      duration.bind="task.duration"
      actual-start.bind="task.actualStart"
      commit-date.bind="task.commitDate"
      condition.bind="task.condition"
      note.bind="task.lastNote"
      note-text.bind="task.lastNote.noteText"
      note-entry-date.bind="task.lastNote.entryDate"
      note-avatar-download-url.bind="task.lastNote.owner.avatarDownloadURL"
      note-owner-name.bind="task.lastNote.owner.name"

    ></card-commit>

  </template>

  <!--Pager Nav-->
  <nav aria-label="Page navigation">
    <ul class="pagination">

      <!--Previous Link-->
      <li class="page-item">
        <a class="page-link" href="#" aria-label="Previous">
          <span aria-hidden="true">&laquo;</span>
          <span class="sr-only">Previous</span>
        </a>
      </li>


      <!-- THIS DOES NOT WORK-->
      <!-- I have to manually refresh the page to get 'feedLength' to update-->
      <!--Pages-->
      <li repeat.for="page of feedLength" class="page-item">
        <a class="page-link" href="#">${page + 1}</a>
      </li>

      <!--Next Link-->
      <li class="page-item">
        <a class="page-link" href="#" aria-label="Next">
          <span aria-hidden="true">&raquo;</span>
          <span class="sr-only">Next</span>
        </a>
      </li>
    </ul>
  </nav>

</template>

Итак, еще раз, основная проблема заключается в этом компоненте пейджера. Я не понимаю, почему feedData обновляется автоматически, в то время как другие свойства, ссылающиеся на него, также не обновляются. Как я могу это исправить?

Несколько интересным является то, что я могу сделать что-то подобное в навигации по пейджеру, и оно будет обновляться автоматически:

  <!--Pages-->
  <li repeat.for="page of feedData.length" class="page-item">
    <a class="page-link" href="#">${page + 1}</a>
  </li>

Но это, конечно, на самом деле не дает мне то, что мне нужно, все, что он делает, это показывает, что feedData все еще доступен в этой навигации. Кроме того, даже если бы это было так, мне действительно нужно было бы иметь возможность обрабатывать все это в модели представления, поскольку все еще потребуется дополнительная обработка, прежде чем отображать ее в представлении.

2 ответа

Это ожидаемый результат. Когда вы делаете это:

this.feedLength = this.feed.cardList.length;

Вы присваиваете примитивное значение feedLengthне наблюдаемый объект.

Чтобы решить вашу проблему, вы должны использовать вычисляемое свойство:

@computedFrom('feed.cardList') //imported from aurelia-framework
get feedLength() {
  return this.feed.cardList.length
}

Затем вы можете использовать его в своем представлении как обычное свойство:

<li repeat.for="page of feedLength" class="page-item">
  <a class="page-link" href="#">${page + 1}</a>
</li>

Во всяком случае, repeat.for="page of feedData.length" это лучший подход. Поэтому используйте вычисляемые свойства только при необходимости.

Надеюсь это поможет!

Исходя из того, что я понимаю сразу, вы пытаетесь обновить список нумерации страниц, который отражает количество элементов в вашем this.feed.cardList?

Если это так, вы можете сделать то же самое для нумерации страниц, как вы сделали выше в pager.html для тебя card-commit элементы и использовать массив (feedData) с repeat.for а также $index установить номера нумерации страниц следующим образом:

<li repeat.for="page of feedData" class="page-item">
    <a class="page-link" href="#">${$index + 1}</a>
</li>

Это должно создать элемент списка для каждого элемента в массиве feedData где page является элементом (полезно, если вам нужно установить элемент списка или ссылки, используя данные из вашего массива) и $index это page индекс элемента в пределах feedData, Приведенный выше код должен отражать любые изменения, внесенные в feedDataнапример, добавление или удаление элемента.

Я думаю, что есть больше как $index такие как $first, $last, и возможно $odd, $evenи т. д. Кроме того, вы могли бы сделать repeat.for="page of feedData.slice(0, 3)" или что-то подобное, если вы хотите ограничить количество элементов списка.

К вашему сведению, я относительно новичок в Аурелии (и в веб-разработке), поэтому я не эксперт. Из того, что я могу сказать, некоторые вещи не наблюдаются без дополнительной работы. Я заметил это - изменение значения ключа объекта в массиве объектов не отражается в repeat.for, Может быть похожая проблема здесь.

Другие вопросы по тегам