Index
Proper use of design patterns can greatly improve the maintainability and scalability of the framework, a good design interface can greatly reduce the amount of user code and improve readability, in favor of the team work of large-scale project development, even do not need to access backend data to complete the unit testing of the front-end component, facilitates data binding and data synchronization for components and models, this will help the developer to further provide WYSIWYG visual development tools, such as Adobe Flash Builder and Microsoft Visual Studio.
In the early GUI design field, Java/Swing, SWT/JFace,
Cocoa and Qt are model of the design pattern based on Model View Controller(MVC).
After MVC slowly develop derived Model View Presenter(MVP) design patterns are gradually being adopted by the new GUI framework, such as based on Flash Apache/Flex enterprise application component, and the JGoodies framework encapsulated on the Swing basis. Martin Fowler GUI Architectures analyzes and compares the two design patterns.
In recent years, on the basis of MVP to add the ViewModel layer derive from the Model View ViewModel(MVVM) design pattern, but also by Microsoft new generation of GUI development framework Silverlight/WPF is adopted, including a framework for simplifying HTML and JS data bindings, such as Knockout, also based on the MVVM design pattern.
MVC/MVP/MVVM and other design patterns to facilitate the understanding and classification of different frameworks, any GUI framework in the specific implementation of their own evolution and characteristics, HT overall framework is more like the MVP and MVVM design patterns, but we prefer to call the HT design model Object View Mapping(OVM), analogous to Object Relational Mapping(ORM), which masks the heterogeneity of various view component by facing encapsulation of object mode, employs a unified DataModel data model and SelectionModel selection model that can drive ListView, TreeView, TableView, TreeTableView, GraphView and Graph3dView all HT view components.
The
HTDesign architecture, users only need to master a unified data-driven interface, not due to the increasement of view component to bring additional learning costs, which isHTeasy to master the basic.
The Data type runs through the HT framework and is the most basic data type.
getId() and setId(id) Gets and sets the unique number, assigned by the system automatically, the settings need attention DataModel Description, use DataModel#getDataById(id) to find.getTag() and setTag(tag) Gets and sets the tag, use DataModel#getDataByTag(tag) to find.getName() and setName(name) Gets and sets the name of the data object.getIcon() and setIcon(icon) Gets and sets the icons, as node icons on components such as TreeView and ListView.getDisplayName() and setDisplayName(displayName) Gets and sets the display name, as Column and Property header and attribute names display.getToolTip() and setToolTip(tooltip) Gets and sets the text tip information for the node or data on the components.getParent() and setParent(parent) Gets and sets the father node, as the information of the tree hierarchical structure, automatically call addChild or removeChild inside.addChild(child, index) Appends child node, index append the index for children, if it's null then appended as the last child, automatically call setParent inside.removeChild(child) Deletes the specific child node, automatically call setParent inside.clearChildren() Deletes all of child node, automatically call setParent inside.onChildAdded(child, index) The callback function while append children, it can be overloaded for subsequent processing.onChildRemove(child, index) The callback function while delete children, it can be overloaded for subsequent processing.onParentChanged(oldParent, parent) The callback function while changing the father node, it can be overloaded for subsequent processing.size() Returns the total number of children.hasChildren() Judges if there are children, if yes, return true, otherwise return false. isEmpty() Judges if there are children, if yes, return false, otherwise return true.getChildren() Gets all of the children node, this function returns the internal ht.List type array object reference. toChildren(matchFunc, scope) According to matchFunc function logic to build new ht.List type array object of all matching datas. eachChild(func, scope) Traverses all children, and can specify the function scope field.getChildAt(index) Returns specified index location child.isParentOf(data) Judges this data whether or not the father of the specified data.isDescendantOf(data) Judges this data whether or not the child of the specified data.isRelatedTo(data) Judges this data and the specified data data whether have parent-child relationship or descendent relationship.layer Attribute is operate by getLayer() and setLayer(layer), corresponding to the position of data in the GraphView component, with the default value null. isAdjustChildrenToTop() and setAdjustChildrenToTop(true) The default value is false, ht.Node type with the default value true, GraphView click data will call sendToTop by default, this attribute decides whether operating sendToTop to children. firePropertyChange(property, oldValue, newValue) Distributes attribute change events and can use the shorthand method of fp.onPropertyChanged(event) Attributes changed callback function and can be overridden subsequent processing.invalidate() In this function users trigger on attribute change events to alarm update interface by force, internally implemented as this.fp('*', false, true) .getStyleMap() Returns data internal style mapping information, while use getStyle(name) if styleMap value is null, automatically return ht.Style defined information.getStyle('name') and setStyle('name', value) Gets and sets the entities style and can use the shorthand method of s(name/name, value/json).onStyleChanged(name, oldValue, newValue) While the attribute of style changing, it will callback this function and can be overridden subsequent processing. getAttrObject() and setAttrObject(obj) Gets and sets the attr attribute object, it's default as null and used on storage users' business information. getAttr(name) and setAttr(name, value) Gets and sets the attr attribute and can use the shorthand method of a(name/name, value/json).onAttrChanged(name, oldValue, newValue) It will called on changing attr and can be overridden subsequent processing.toLabel() Returns value is default as the data text label of the components such as TreeView and GraphView, default return displayName||name information. addStyleIcon(name, icons) and removeStyleIcon(name) Adds and deletes style and icons attribute, you can reference Icon Chapter. getSerializableProperties() Returns attribute name map which is need to be serialized, you can referece Serialization Tutorial.getSerializableStyles() Returns style attributes name map which is need to be serialized, you can reference Serialization Tutorial.getSerializableAttrs() Returns attr attribute name map which is need to be serialized, you can reference Serialization Tutorial.ht.Default.setImage('edit', 'res/edit.png');
ht.Default.setImage('mail', 'res/mail.png');
ht.Default.setImage('readmail', 'res/readmail.png');
ht.Default.setImage('search', 'res/search.png');
ht.Default.setImage('settings', 'res/settings.png');
function init(){
dataModel = new ht.DataModel();
treeView = new ht.widget.TreeView(dataModel);
view = treeView.getView();
view.className = 'main';
document.body.appendChild(view);
window.addEventListener('resize', function (e) {
treeView.invalidate();
}, false);
var inbox = addData('Inbox', 'mail');
addData('Read Mail', 'readmail', inbox);
addData('Drafts', 'edit');
var search = addData('Search Folders', 'search');
addData('Categorized Mail', 'search', search);
addData('Large mail', 'search', search);
addData('UnRead Mail', 'search', search);
addData('Settings', 'settings');
treeView.expandAll();
treeView.getSelectionModel().setSelection(search);
}
function addData(name, icon, parent){
var data = new ht.Data();
data.setName(name);
data.setIcon(icon);
data.setParent(parent); // or parent.addChild(data);
dataModel.add(data);
return data;
}
Data container ht.DataModel(following abbrivation DataModel) as the model of load Data type, managing the additions and deletions of Data and the distribution of change events, all components of HT framework to display in the users interface by binding DataModel, as the same time, components would also listening the changing events of DataModel, real-time synchronization between data and user interface, mastering the DataModel operation has mastered the model-driven approach of all the components.
The Data type object is automatically assigned an id attribute inside the construct and can use data.getId() and data.setId(id) gets and sets the, the data object is not allowed to modify the id value after it is added to DataModel and can be quickly found Data objects through data.getDataById(id).
Generally recommended id attribute is automatically assigned by HT, the only sign of user's business meaning can exist in tag attribute, through Data#setTag(tag) function allows any dynamic change tag value, through DataModel#getDataByTag(tag) can find the corresponding data object and support through DataModel#removeDataByTag(tag) delete Data object.
id and tag are all target the only assigned Data object, if search for the not uniqueness attribute can used ht.QuickFinder plug-in.
The use of DataModel requires more attention: Data, which is generally require a parent-child relationship, should be added to the container. Often encountered parent in the container, but children didn't, resulting in components can't see the problems of children, adding parent wouldn't automatically load all descendants, this must be noted.
The Data type has getDataModel() function, data.getDataModel() can obtain the current container information while Data attended in the container, a Data object to be added to multiple DataModel containers at the same time is not allowed.
add(data, index) Appends Data objects, index generally need not be specified, it works only when the data's parent is null, specifying the index position of inserting roots array.
remove(data) Deletes Data object, this operate has the following side-effect:
DataModeldata.setParent(null)Edge type disconnected data relationship through edge.setSource(null) and data.setTarget(null)Node type will remove its associated connection from DataModel Node type will disconnected from the host by data.setHost(null)clear() Deletes all the Data objects in the container, and the operation is emptied one at a time without the remove process and does not affect the data parent-child relationship.onAdded(data) Data adds a callback function that can be overloaded for subsequent processing.onRemoved(data) Data deletes a callback function that can be overloaded for subsequent processing.contains(data) Determines whether the container contains the data object.size() Returns the total number of data objects in the current container.isEmpty() Determines whether the container is null.getRoots() Returns all Data objects that their parent are null.getDataById(id) Returns the Data object for the specified id.removeDataById(id) Deletes the Data object for the specified id.getDataByTag(tag) Returns the Data object for the specified tag.removeDataByTag(tag) Deletes the Data object for the specified tag.each(func, scope) Traverses all the Data objects.eachByDepthFirst(func, data, scope) Takes data as the starting depth to traverse Data objects.eachByBreadthFirst(func, data, scope) Takes data as the starting breadth to traverse Data objects.getDatas() Returns all added to the container's Data ht.List array.toDatas(matchFunc, scope) Returns the filtered new ht.List object array, the first parameter is null equivalent to copying all object arrays.getSelectionModel() Gets the selection model of the container and referred to briefly as sm().addDataModelChangeListener(function (e){}, scope) Increase the data model add or delete event listener and referred to briefly as mm(func, scope).e.kind === 'add' Represents the added Data object, e.data is appended object.e.kind === 'remove' Represents the delete Data object, e.data is deleted object.e.kind === 'clear' Means the container is cleared.removeDataModelChangeListener(func, scope) Deletes data model add or delete changed events listener and referred to briefly as umm(func, scope).addDataPropertyChangeListener(function (e){}, scope) Adds the event listener for data attribute changes in the model and referred to briefly as md(func, scope).e.data Represents an object with a property change.e.property Represents the name of the change property.e.newValue Represents the new value of the property.e.oldValue Represents the old value of the property.Data Object calls the firePropertyChange(property, oldValue, newValue) trigger property change event within the set property value function: get/set Type property, such as setAge(98) trigger event e.property for age.style Type property name preceded by s: prefix to differentiate, such as setStyle('age', 98) trigger event e.property for s:age.attr Type property name preceded by a: prefix to differentiate, such as setAttr('age', 98) trigger event e.property for a:age. removeDataPropertyChangeListener(func, scope) Deletes Data property change event listener in the model and can be abbreviated to umd(func, scope).onDataPropertyChanged(data, e) It will be called on changing data, can be overloaded for subsequent processing.getSiblings(data) Gets the sibling array with data, if data parent is null, then return dataModel.getRoots().moveTo(data, newIndex) Should specified index if remove data to the same sibling array.moveUp(data) Removes data to the same sibling's previous position.moveDown(data) Removes data to the same sibling's next position.moveToTop(data) Removes data to the top of the same sibling.moveToBottom(data) Removes data to the bottom of the same sibling.moveSelectionUp(sm) Removes the current selection data to the previous position of the same sibling array, if sm is null, use DataModel's binding selection model.moveSelectionDown(sm) Removes the current selection data to the same sibling array's next position, if sm is null, use DataModel's binding selection model.moveSelectionToTop(sm) Removes the current selection data to the top of the same sibling, if sm is null, use DataModel's binding selection model.moveSelectionToBottom(sm) Removes the current selection data to the bottom of the same sibling, if sm is null, use DataModel's binding selection model.serialize(space) Serializes the data model into a JSON format string, space to intent space.toJSON Serializes the data model into a JSON format object.deserialize(json, rootParent, setId) Deserializes data to data modeljson Data object that resolves to generate the corresponding Data object and added into data container.rootParent Parent node object, if not null, the deserialized object if don't have father, set rootParent to be his father.setId Specifies whether to set the id value on JSON information when deserializing.Through the following
firePrppertyChangecode snippet can be known, whenoldValueequals tonewValue, property change event won't be distributed, property change events throughhandleDataPropertyChangesend toDataModelcontinue to do processing, the subsequent processing include continuing to distribute the event to the property change listener which throughaddDataPropertyChangeListeneradd toDataModel.
firePropertyChange: function (property, oldValue, newValue) {
if (oldValue === newValue) {
return false;
}
var e = {
property: property,
oldValue: oldValue,
newValue: newValue,
data: this
};
if (this._dataModel) {
this._dataModel.handleDataPropertyChange(e);
}
this.onPropertyChanged(e);
return true;
}
ht.SelectionModel manages the Data object in the DataModel model, each DataModel object has a SelectionModel, if you control this SelectionModel then you can control all binding components' object selection state in this DataModel, that means share the same DataModel components default has a selected linkage feature.
If you want some components to be selected without linkage with other components, you can invoke view.setSelectionModelShared(false) so that the view will create a exclusive SelectionModel instance.
In summary there are two ways to get SelectionModel:
dataModel.getSelectionModel() Gets selected model for the component share in the data container.view.getSelectionModel() Gets selected model for the use of the current component, if selectionModelShared is false, return a view specific selection model.The common functions of SelectionModel are as follows:
getSelectionMode() and setSelectionMode(selectionMode) Gets and sets the selection modelnone: Can not be selected.single: Optional only.multiple: Default value, allow multiple selections.getFilterFunc() and setFilterFunc(func) Sets filters to customize selectable object rules, refers to The Filter Chapter.appendSelection(datas) Appends selected object, parameter can be a single object, also can be ht.List or Array array and referred to briefly as as.setSelection(datas) Sets selected object, parameter can be a single object, also can be ht.List or Array array and referred to briefly as ss.removeSelection(datas) Cancels selected object, parameter can be a single object, also can be ht.List or Array array and referred to briefly as rs.clearSelection() Cancels all the selected object, referred to briefly as cs.selectAll() Selected all object of DataModel, referred to briefly as sa.size() Returns the number of selected object.isEmpty() Determines if no object is currently selected.contains(data) Determines if data object is selected, referred to briefly as co.getFirstData() Returns the first selected object, return null if there is no selected object, referred to briefly as fd.getLastData() Returns the last selected object, return null if there is no selected object, referred to briefly as ld.each(function (data){}, scope) Traverses all the selected object.getSelection() Gets all the selected object array, you can't add or delete to the return array!toSelection(matchFunc, scope) Returns filtered selected object, if matchFunc is null, it represents copy all selected object to new array.addSelectionChangeListener(function (e){}, scope) Adds listener, listen selected change event, referred to briefly as ms:e.datas Includes all objects which selection state are changed, if the object is selected currently then not being selected before, otherwise not selected currently but being selected before.e.kind === 'set' Represents this event caused by setSelection(datas).e.kind === 'remove' Represents this event caused by removeSelection(datas).e.kind === 'append' Represents this event caused by appendSelection(datas).e.kind === 'clear' Represents this event caused by clearSelection(datas).removeSelectionChangeListener(function (e){}, scope) Deletes selected change event listener and referred to briefly as ums:index = 0;
dataModel = new ht.DataModel();
selectionModel = dataModel.getSelectionModel();
// monitor data property change event
dataModel.addDataPropertyChangeListener(function (e){
document.getElementById('property').innerText = e.data + '\'s ' + e.property + ' changed';
});
// monitor data model change event
dataModel.addDataModelChangeListener(function (e){
var output;
if(e.kind === 'add'){
output = e.data + ' added, ';
}
else if(e.kind === 'remove'){
output = e.data + ' removed, ';
}
else if(e.kind === 'clear'){
output = 'data model cleared, ';
}
output += 'size:' + dataModel.size();
document.getElementById('model').innerText = output;
});
// monitor selection model change event
selectionModel.addSelectionChangeListener(function (e){
var output = '';
size = selectionModel.size();
if(size === 0){
output = 'nothing selected';
}
else if(size === 1){
output = selectionModel.getLastData() + ' selected';
}
else{
output = size + ' datas selected';
}
document.getElementById('selection').innerText = output;
});
graphPane.getGraphView().setEditable(true);
addData();
addData();
selectionModel.setSelection(addData());
function addData(){
var node = new ht.Node();
node.setPosition(50 + index % 12 * 50, 50);
node.setName('node' + index++);
dataModel.add(node);
return node;
}
function removeData(){
while(selectionModel.size() > 0){
dataModel.remove(selectionModel.getLastData());
}
}