EmberJS - Table 带有多个复选框 - 如何使用分页保留 'checked' 状态
EmberJS - Table with multiple checkboxes - how to preserve 'checked' status with pagination
这是我的组件模板
sortable-table-checkbox.hbs
<table class="table table-checkbox" data-page-length="{{dataPageLength}}">
<thead>
<tr>
<th> </th>
{{#each-in columns as |prop value| }}
<th
class="{{if (or (eq (get sortingStatus prop) '')
(eq (get sortingStatus prop) 'asc')
(eq (get sortingStatus prop) 'desc')) 'sorting'}} w25p {{get sortingStatus prop}}"
{{action 'toggleSorting' prop}}>
{{#if value.title}}
{{value.title}}
{{else}}
{{value}}
{{/if}}
</th>
{{/each-in}}
</tr>
</thead>
<tbody>
{{#each data as |item|}}
<tr>
<td>
<div class="">
{{input
type='checkbox'
checked=item.checked
change=(action 'chooseItem' item)}}
</div>
</td>
{{#each-in columns as |prop value| }}
<td>
{{#collection- value=(get item prop) field=value.field as |col|}}
{{#number-format isNumber=(eq value.type 'number') value=col as |val|}}
{{#if value.linkable}}
{{#link-to value.linkable.route (get item value.linkable.paramField)}}
{{value.prefix}}
{{val}}
{{/link-to}}
{{else}}
{{value.prefix}}
{{val}}
{{/if}}
{{/number-format}}
{{/collection-}}
</td>
{{/each-in}}
</tr>
{{/each}}
</tbody>
</table>
<p>Your Selected Items:</p>
<ul>
{{#each selectedItems as |item|}}
<li>{{item.title}}</li>
{{else}}
<em>No items selected</em>
{{/each}}
</ul>
{{checked}}
{{yield}}
这是我的组件
sortable-table-checkbox.js
import Ember from 'ember';
/**
* Table component
*
* Allows to render dynamic tables with sorting where items are displayed as
* checkboxes
*
* Header titles of table should passed as ``columns`` arg to the component.
*
* columns: {
* title: {
* title: 'Game Title',
* sorting: 'title',
* linkable: {
* route: 'home.cabinet.developer.games.game',
* paramField: 'game_uid',
* },
* },
* genres: {
* title: 'Genre',
* },
* amount: {
* title: 'Ad Revenue Earned',
* prefix: '$',
* type: 'number',
* sorting: 'amount'
* },
* platforms_data: {
* title: 'Platform',
* collection: true,
* field: 'title',
* },
* ages_data: {
* title: 'Ages',
* collection: true,
* field: 'value',
* },
* },
*
* Example of using of this component
*
* {{sortable-table data=model.games.results columns=columns sorting=sorting}}
*
* @class SortingTableComponent
* @module components/sortable-table
* @extends Ember.Component
* @public
*/
export default Ember.Component.extend({
/**
* Sorting status
*
* @property sortingStatus
* @type {Object}
* @default null
* @public
*/
sortingStatus: null,
/**
* Avoid sharing state between components
*
* @method init
* @return {undefined}
* @public
*/
init() {
this._super(...arguments);
this.set('sortingStatus', {});
this.selectedItems = [];
this.checked=false;
},
/**
* Fill ``sortingStatus`` object with reset statuses
*
* @method willInsertElement
* @return {undefined}
* @public
*/
willInsertElement() {
const columns = this.get('columns');
for (const element in columns) {
if (columns[element].sorting) {
this.set(`sortingStatus.${element}`, '');
}
}
},
actions: {
/**
* Toggle sorting
*
* @method toggleSorting
* @param prop {String} name of property
* @return {boolean} false if this property doesn't exist in ``sorting`` object
* @public
*/
toggleSorting(prop) {
// If ``prop`` property doesn't exist in ``sorting`` object, return false
if (!(prop in this.get('sortingStatus'))) {
return false;
}
// Reset another properties sorting state
for (const element in this.get('sortingStatus')) {
if (element !== prop) {
this.set(`sortingStatus.${element}`, '');
}
}
// Set asc if starting state, desc if asc and starting state if desc
if (this.get(`sortingStatus.${prop}`) === '') {
this.set(`sortingStatus.${prop}`, 'asc');
} else if (this.get(`sortingStatus.${prop}`) === 'asc') {
this.set(`sortingStatus.${prop}`, 'desc');
} else {
this.set(`sortingStatus.${prop}`, '');
}
// Sending action
this.sendAction('action', this.get(`columns.${prop}.sorting`), this.get(`sortingStatus.${prop}`));
return true;
},
chooseItem(item, e) {
const added = e.target.checked;
this.set('checked', added);
if (added) {
item.checked=true;
this.get('selectedItems').pushObject(item);
}
else {
this.get('selectedItems').removeObject(item);
}
},
},
});
因此,当我浏览页面时,应用程序会 API 调用查询要显示的下 5 个项目,当我返回第 1 页时,我没有 item.checked 状态 - 因为项目是来自 API 调用的新对象,因此它没有我在 chooseItem 中设置的 .checked 属性动作。
如何解决这个问题?
也许有一个动态的方法来检查是否可以设置复选框的 checked 属性?
这些更改未保存,因为您没有在后端保存带有正确选中字段的模型。我建议两种动态计算复选框选中状态的方法:
- 快的那个。您可以 create a custom helper to check that element is in array. Or you can use any existing addon 提供这样的帮手。然后你可以在sortable-table-checkbox.hbs中这样写:
{{input type='checkbox'
checked=(array-contains selectedItems item.id property='id')
change=(action 'chooseItem' item)}}
- 您可以创建一个组件
table-row
并将您在 <tr></tr>
中的所有内容移动到 sortable-table-checkbox.hbs。该组件将具有 item
和 selectedItems
作为参数。然后你可以使用 Ember 计算属性:
//table-row.js
// ...
isChecked: Ember.computed('item.id', 'selectedItems.length', function() {
const itemId = this.get('item.id');
const selectedItems = this.get('selectedItems');
return selectedItems.some(el => el.get('id') === itemId);
})
// ...
//table-row.hbs
{{input type='checkbox'
checked=isChecked
change=(action 'chooseItem' item)}}
这是我的组件模板 sortable-table-checkbox.hbs
<table class="table table-checkbox" data-page-length="{{dataPageLength}}">
<thead>
<tr>
<th> </th>
{{#each-in columns as |prop value| }}
<th
class="{{if (or (eq (get sortingStatus prop) '')
(eq (get sortingStatus prop) 'asc')
(eq (get sortingStatus prop) 'desc')) 'sorting'}} w25p {{get sortingStatus prop}}"
{{action 'toggleSorting' prop}}>
{{#if value.title}}
{{value.title}}
{{else}}
{{value}}
{{/if}}
</th>
{{/each-in}}
</tr>
</thead>
<tbody>
{{#each data as |item|}}
<tr>
<td>
<div class="">
{{input
type='checkbox'
checked=item.checked
change=(action 'chooseItem' item)}}
</div>
</td>
{{#each-in columns as |prop value| }}
<td>
{{#collection- value=(get item prop) field=value.field as |col|}}
{{#number-format isNumber=(eq value.type 'number') value=col as |val|}}
{{#if value.linkable}}
{{#link-to value.linkable.route (get item value.linkable.paramField)}}
{{value.prefix}}
{{val}}
{{/link-to}}
{{else}}
{{value.prefix}}
{{val}}
{{/if}}
{{/number-format}}
{{/collection-}}
</td>
{{/each-in}}
</tr>
{{/each}}
</tbody>
</table>
<p>Your Selected Items:</p>
<ul>
{{#each selectedItems as |item|}}
<li>{{item.title}}</li>
{{else}}
<em>No items selected</em>
{{/each}}
</ul>
{{checked}}
{{yield}}
这是我的组件 sortable-table-checkbox.js
import Ember from 'ember';
/**
* Table component
*
* Allows to render dynamic tables with sorting where items are displayed as
* checkboxes
*
* Header titles of table should passed as ``columns`` arg to the component.
*
* columns: {
* title: {
* title: 'Game Title',
* sorting: 'title',
* linkable: {
* route: 'home.cabinet.developer.games.game',
* paramField: 'game_uid',
* },
* },
* genres: {
* title: 'Genre',
* },
* amount: {
* title: 'Ad Revenue Earned',
* prefix: '$',
* type: 'number',
* sorting: 'amount'
* },
* platforms_data: {
* title: 'Platform',
* collection: true,
* field: 'title',
* },
* ages_data: {
* title: 'Ages',
* collection: true,
* field: 'value',
* },
* },
*
* Example of using of this component
*
* {{sortable-table data=model.games.results columns=columns sorting=sorting}}
*
* @class SortingTableComponent
* @module components/sortable-table
* @extends Ember.Component
* @public
*/
export default Ember.Component.extend({
/**
* Sorting status
*
* @property sortingStatus
* @type {Object}
* @default null
* @public
*/
sortingStatus: null,
/**
* Avoid sharing state between components
*
* @method init
* @return {undefined}
* @public
*/
init() {
this._super(...arguments);
this.set('sortingStatus', {});
this.selectedItems = [];
this.checked=false;
},
/**
* Fill ``sortingStatus`` object with reset statuses
*
* @method willInsertElement
* @return {undefined}
* @public
*/
willInsertElement() {
const columns = this.get('columns');
for (const element in columns) {
if (columns[element].sorting) {
this.set(`sortingStatus.${element}`, '');
}
}
},
actions: {
/**
* Toggle sorting
*
* @method toggleSorting
* @param prop {String} name of property
* @return {boolean} false if this property doesn't exist in ``sorting`` object
* @public
*/
toggleSorting(prop) {
// If ``prop`` property doesn't exist in ``sorting`` object, return false
if (!(prop in this.get('sortingStatus'))) {
return false;
}
// Reset another properties sorting state
for (const element in this.get('sortingStatus')) {
if (element !== prop) {
this.set(`sortingStatus.${element}`, '');
}
}
// Set asc if starting state, desc if asc and starting state if desc
if (this.get(`sortingStatus.${prop}`) === '') {
this.set(`sortingStatus.${prop}`, 'asc');
} else if (this.get(`sortingStatus.${prop}`) === 'asc') {
this.set(`sortingStatus.${prop}`, 'desc');
} else {
this.set(`sortingStatus.${prop}`, '');
}
// Sending action
this.sendAction('action', this.get(`columns.${prop}.sorting`), this.get(`sortingStatus.${prop}`));
return true;
},
chooseItem(item, e) {
const added = e.target.checked;
this.set('checked', added);
if (added) {
item.checked=true;
this.get('selectedItems').pushObject(item);
}
else {
this.get('selectedItems').removeObject(item);
}
},
},
});
因此,当我浏览页面时,应用程序会 API 调用查询要显示的下 5 个项目,当我返回第 1 页时,我没有 item.checked 状态 - 因为项目是来自 API 调用的新对象,因此它没有我在 chooseItem 中设置的 .checked 属性动作。
如何解决这个问题?
也许有一个动态的方法来检查是否可以设置复选框的 checked 属性?
这些更改未保存,因为您没有在后端保存带有正确选中字段的模型。我建议两种动态计算复选框选中状态的方法:
- 快的那个。您可以 create a custom helper to check that element is in array. Or you can use any existing addon 提供这样的帮手。然后你可以在sortable-table-checkbox.hbs中这样写:
{{input type='checkbox' checked=(array-contains selectedItems item.id property='id') change=(action 'chooseItem' item)}}
- 您可以创建一个组件
table-row
并将您在<tr></tr>
中的所有内容移动到 sortable-table-checkbox.hbs。该组件将具有item
和selectedItems
作为参数。然后你可以使用 Ember 计算属性:
//table-row.js // ... isChecked: Ember.computed('item.id', 'selectedItems.length', function() { const itemId = this.get('item.id'); const selectedItems = this.get('selectedItems'); return selectedItems.some(el => el.get('id') === itemId); }) // ...
//table-row.hbs {{input type='checkbox' checked=isChecked change=(action 'chooseItem' item)}}