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
HT
Design 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 isHT
easy 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:
DataModel
data.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
firePrppertyChange
code snippet can be known, whenoldValue
equals tonewValue
, property change event won't be distributed, property change events throughhandleDataPropertyChange
send toDataModel
continue to do processing, the subsequent processing include continuing to distribute the event to the property change listener which throughaddDataPropertyChangeListener
add 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());
}
}