src/view/list-view.js
'use strict'
import { makeLFID } from '../core/util.js'
import { List } from '../model/item.js'
import { ListBinder } from '../model/binder.js'
import View from './view.js'
/**
* View for the collection of items.
*
* @example
* class ListItemView extends View {
* html(data) {
* return `<li>${data.title}</li>`;
* }
* }
*
* let instanceView = new ListView(Node, ListItemView,
* {
* data: [
* { title: 'foo' },
* { ttile: 'bar' }
* ]
* });
*/
class ListView extends View {
/*
* Create a ListView.
*
* @param {object} [props] - Properties
* @param {string|Element} [props.rootEl] - root element ID or root node
* @param {Class} [itemView] - item view class
* @param {Class<View>} [props.parent] - parent view this belongs to
* @param {string|Element} [props.contentEl] - parent element of child views (specified by data-id or id value).
*/
constructor(itemView, props = {}) {
props._F_tmpl = itemView;
super(props);
}
/** @override */
_privates() {
const pa = super._privates();
pa._F_itemSet = new Map();
return pa;
}
/**
* If you dynamically change creating item view according to the item, override this method.
*
* @param {object} item - an item
* @return {Class<View>}
*/
itemViewClass(item) {
return this._F_tmpl
}
/**
* Add an item to list
*
* @param {object} item - an item
*/
addItem(item) {
const view = this._createViewByItem(item);
this.addItemEl(this.contentEl, view.el);
return view;
}
/**
* Insert an item to list at index
*
* @param {object} item - an item
* @param {number} index - target position
*/
insertItem(item, index) {
const view = this._createViewByItem(item);
this.insertItemEl(this.contentEl, view.el, this._childElAt(index));
return view;
}
/**
* Update an item at index
*
* @param {object} item - an item
* @param {number} index - target position
*/
updateItem(item, index) {
const LFID = this._childElAt(index).getAttribute('_lfid_');
this._F_itemSet.set(LFID, item);
this.views[LFID].data = item;
}
/**
* Remove item from list
*
* @param {object} item - an item
* @param {number} index - target position
*/
removeItem(item, index) {
const el = this._childElAt(index)
this._removeItemByEl(el);
}
/**
* Remove item with view
*
* @param {view} view - an view of removing item
*/
removeItemByView(view) {
this._removeItemByEl(view.el)
}
/**
* If you change adding item effect, override this method.
*
* @protected
* @param {Element} listEl parent element for List
* @param {Element} itemEl added element
*/
addItemEl(listEl, itemEl) {
listEl.appendChild(itemEl)
}
/**
* If you change inserting item effect, override this method.
*
* @protected
* @param {Element} listEl - parent element for List
* @param {Element} newEl - an element for new item for List
* @param {Element} nextEl - next element will be next one for newEl
*/
insertItemEl(listEl, newEl, nextEl) {
listEl.insertBefore(newEl, nextEl)
}
/**
* If you change removing item effect, override this method.
*
* @protected
* @param {Element} listEl - parent element for List
* @param {Element} itemEl - removed element
*/
removeItemEl(listEl, itemEl) {
listEl.removeChild(itemEl)
}
/**
* Return child element at position.
*
* @param {number} index - item position.
* @return {Element} target element
*/
_childElAt(index) {
return this.contentEl.children[index];
}
_createViewByItem(item) {
const LFID = makeLFID();
const cls = this.itemViewClass(item);
const view = new cls({ data: item });
this._F_itemSet.set(LFID, view);
this.views[LFID] = view;
view.el.setAttribute('_lfid_', LFID);
return view;
}
/**
* Remove item from list by element
*
* @param {element} el - removed element
*/
_removeItemByEl(el) {
const LFID = el.getAttribute('_lfid_');
this._F_itemSet.delete(LFID);
const vws = this.views
vws[LFID].unload();
delete vws[LFID];
this.removeItemEl(this.contentEl, el);
}
/** @override */
_bindData() {
const data = this._data;
if (data instanceof List) {
this._F_binders.push(new ListBinder(data, this))
}
}
/** @override */
_setDataToUI() {
if (this._data === undefined) return;
console.log('ListView#_setDataToUI', this);
for (let childView of Object.values(this.views)) {
this.removeItemByView(childView);
}
for (let item of this._data) {
this.addItem(item);
}
}
}
export default ListView