- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我对 Aurelia 还是个新手,我发现它具有约束力,我现在遇到了一个有点奇怪的问题。
主要前提是我正在开发一个从 API 获取数据的更新提要 Web 应用程序。数据被分成可供查看的 block (卡片)。还有搜索/排序组件供用户过滤卡片。这就是我遇到问题的地方......
当用户对数据进行搜索或排序时,卡片会被过滤并正确显示。但是,我也在尝试添加一个分页组件,但出于某种原因,模型数据绑定(bind)并未在此组件中完全更新。
这是代码(包括几乎所有需要的代码):
update-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>
更新提要.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();
});
}
}
search-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>
search-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);
}
}
feed-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">«</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">»</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
仍然可以在此导航中访问。此外,即使是这样,我也确实需要能够在 View 模型中处理所有这些,因为在将其显示在 View 中之前仍然需要进行更多处理。
最佳答案
这是预期的结果。当您这样做时:
this.feedLength = this.feed.cardList.length;
您正在为 feedLength
分配原始值,而不是可观察对象。
要解决您的问题,您必须使用计算属性:
@computedFrom('feed.cardList') //imported from aurelia-framework
get feedLength() {
return this.feed.cardList.length
}
然后,您可以在您的 View 中将其用作普通属性:
<li repeat.for="page of feedLength" class="page-item">
<a class="page-link" href="#">${page + 1}</a>
</li>
无论如何,repeat.for="page of feedData.length"
是更好的方法。因此,仅在必要时才使用计算属性。
希望这对您有所帮助!
关于typescript - Aurelia repeat.for 绑定(bind)分页问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44545114/
我有一个 foo 类,它有一个 bar 方法,它接受可调用的东西(函数指针/仿函数)。这个可调用的东西应该作为绑定(bind)元素传递给另一个方法 doit 和第三个方法 bar_cb 方法。 #in
我正在尝试在我的 WPF 4.0 应用程序(使用 VS 2010 Pro RTM)中创建自定义 TabItem 模板/样式,但尽管一切似乎都正常工作,但我注意到跟踪窗口中存在绑定(bind)错误。 我
作为一名刚接触 Android 的开发人员,我想我可能误解了绑定(bind)服务。 我创建了一项服务来结束对服务器的访问。作为此服务的一部分,该服务正在监听多播地址,以识别本地网络上的设备何时出现和消
这个问题在这里已经有了答案: What is the use of the JavaScript 'bind' method? (23 个回答) 关闭 7 年前。 所以我一直在尝试了解一些 JS 上
我不明白这三种语法之间的区别: where a = f (b) do a <- f (b) do let a = f (b) 我确实明白了a <- f(b)与其他两个不同,在大多数情况下,我尝试了所有
我在将 Cocoa 项目从手动同步接口(interface)模型转换为绑定(bind)模型时遇到问题,这样我就不必担心接口(interface)粘合代码。 我关注了 CocoaDevCentral C
我正在尝试找出一种好的方法来对处理大数据集的代码进行并行化,然后将结果数据导入 RavenDb。 数据处理受 CPU 限制和数据库导入 IO 限制。 我正在寻找一种解决方案,以对 Environmen
我正在 foreach 循环中生成单选按钮。我试图将选中的属性绑定(bind)到父级中的基本可观察值。不幸的是,当单击单选按钮时,父级的属性似乎没有在单击处理程序中更新。 基于一些previous w
在我的 Windows Phone 应用程序中,我有两个 LongListSelectors并排在页面上。我想做到这一点,以便当用户滚动其中一个时,另一个滚动相同的量。 两个 LongListSele
我在网上看到这个问题准备面试: Given a non-preemptive kernel which type of process will get affected morein terms o
我有一个 foreach 绑定(bind),如下所示: Summary Permitting 原因是有两个选项卡始终存在,并且我根据是否添加了其他选项卡来添加其他选项
任何人都有绑定(bind)相同的情况DataContext到 TextBlock 中的 Text 属性(例如)。 我必须分配 DataContext以我的风格反射(reflect)基于 Datacon
给定以下代码: Login 和下面的javascript $(function () { $('#btnLogin').click(function () { co
我使用 boost::asio 创建了一个服务器。我在绑定(bind)到端点时遇到问题。所以,如果我在构造函数中初始化一个接受器: Server::Server(QWidget *parent) :
我正在将现有项目从 MySQL 转换为 Postgres。代码中有相当多的原始 SQL 文字使用 ? 作为占位符,例如 SELECT id FROM users WHERE
似乎在绑定(bind)某些数据时出错了,有人可以帮我解决我哪里出错了,尽管我无法弄清楚。 真的不需要在这里显示太多,这是 Binding,我已经通过移除背景并在其中放置颜色来测试背景,效果很好。 编辑
我正在尝试使用 wcf 构建一个 http 监听器(web 服务)。这个监听器是一个更大的桌面应用程序的一部分。此桌面应用程序还会调用 http 监听器。 当监听器接收到数据时,它应该被传递到桌面应用
嘿嘿。 我正在使用 Node.JS 和 child_process 来生成 bash 进程。我试图了解我是否正在执行 I/O 绑定(bind)、CPU 绑定(bind)或两者兼而有之。 我正在使用 p
尝试执行以下操作并出现“Got interpolation ({{}}) where expression was expected”错误。 {{item.name}} 谢谢!
我有一个导入的 Java 库,它是我解决方案中的“绑定(bind)库”项目。 我正在尝试从解决方案中的另一个项目绑定(bind)到第 3 方库中的服务。 第 3 方库文档 [在 java 中] 非常简
我是一名优秀的程序员,十分优秀!