Attribute lists must be familiar to everyone. Normally, the attribute list made using HTML5 is probably a drop-down menu. And in many cases, the drop-down list is not pretty enough. What should I do? I tried to use HT for Web to realize the function of clicking a button in the property bar to pop up a multi-function selection box and select the incoming data. I feel that the overall practice is relatively simple and convenient, so I will share it with you here.
renderingshttp://www.hightopo.com/demo/propertyEditor/index.html
Code implementation Topology mapFrom the rendering above, we can see that the entire page is divided into 3 parts, the graphView topology part on the left, the tableView table part in the lower right corner, and the propertyView attribute part in the upper right corner. We first divide the entire scene, and then add specific content to each part:
gv = new ht.graph.GraphView();var tablePane = new ht.widget.TablePane(gv.dm());//Table panel component propertyView = new ht.widget.PropertyView(gv.dm());/ /formPane is in propertyView, so you must first define var rightView = new ht.widget.SplitView(propertyView, tablePane, 'v', 0.4);//Split the component, v is divided into upper and lower layers, the ratio is 0.4:0.6rightView.getView().style.borderLeft = '1px solid #000';var borderPane = new ht.widget.BorderPane( );//Border panel component borderPane.setRightView(rightView, 400);//Set borderPane The right component is rightView with a width of 400borderPane.setCenterView(gv);//Set the borderPane middle component to gv borderPane.addToDOM();//Add the borderPane component into the body
The new parts in the above code are all HT encapsulated components, which are equivalent to classes. Here is an explanation of the SplitView split component. The split component is used to split two sub-components left and right or up and down. The sub-components can be components provided by the HT framework, or they can It is a native component of HTML. The sub-component is absolutely positioned with position as absolute. The parameters in this component are (left component or upper component, right component or lower component, h means left and right division v Indicates upper and lower division. The default value of the division position is 0.5. If the setting value is 0~1, it will be divided by percentage. Greater than 1 represents the absolute width or height of the left component or the upper component. Less than 1 represents the absolute width or height of the right component or the lower component. ); and the BorderPane panel component is a layout container, which can place sub-components in five areas: top, bottom, left, right, and center. The sub-components can be components provided by the HT framework, or they can be HTML native components. The component is positioned Perform absolute positioning for absolute mode. Here I combine SplitView and BorderPane to divide the scene into three parts. Finally, remember to add the final layout container to the body or any HTML tag so that it can be displayed on the interface. The definition of addToDOM is as follows:
addToDOM = function(){ var self = this, view = self.getView(), //Get the underlying div of this component style = view.style; //Get the style attribute of the underlying div document.body.appendChild(view) ; //Add the underlying div into the body style.left = '0'; //HT by default defines the components to determine absolute positioning, so the position needs to be set style.right = '0'; style.top = '0' ; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); }
HT components are generally embedded in containers such as BorderPane, SplitView and TabView. The outermost HT component requires the user to manually add the underlying div element returned by getView() to the DOM element of the page. What needs to be noted here is that, When the size of the parent container changes, if the parent container is HT's predefined container components such as BorderPane and SplitView, the HT container will automatically call the invalidate function of the child component recursively to notify the update. But if the parent container is a native html element, the HT component cannot know that it needs to be updated. Therefore, the outermost HT component generally needs to listen to the window size change event of the window and call the invalidate function of the outermost component to update.
The scene is created. In order to show the differences in attributes corresponding to different nodes, we added seven nodes to the topology map:
function initModel(){ var name = device; var count = 0; var root = createNode(name + count++, name + (++count));//Parameter 1 is name, parameter 2 is tag root.setImage('. /symbols/computer room/server.json'); root.setName('server'); root.s('label.position', 3); gv.sm().ss(root);//The root node is selected by default for (var i = 0; i < 2; i++) { var iNode = createNode(name + count++, name + (++count));/ /Parameter 1 is name, parameter 2 is tag createEdge(root, iNode); for (var j = 0; j < 2; j++) { var jNode = createNode(name + count++, name + (++count)); createEdge(iNode, jNode); } }}
The declaration of the createNode function is as follows:
function createNode(name, tag){//Create Node node flag++; var node = new ht.Node(); node.setName(name); node.setTag(tag); node.setImage('./symbols/computer room/ XX subsystem.json'); node.a('hidden', false);//Custom attributes, you can control node.a('hidden') to control the visibility of nodes node.a('Interface type', 'SATA'); node.a('Graphics card', 'Nvidia') ; if(flag % 2 === 0){ node.a('interface type', 'IDE'); node.a('graphics card', 'ATI'); } node.s('label.position', 11); gv.dm().add(node);//Add nodes to the data container DataModel node.tablePane1 = createTableView(serviceType, dataModel1);//Create a table panel node.tablePane2 = createTableView(serviceSize, dataModel2); node. tablePane3 = createTableView(version, dataModel3); node.formPane1 = createFormPane(node.tablePane1);//Create a form panel node.formPane1.title = 'Type';//To prepare for the title of the subsequent dialog box node.formPane2 = createFormPane(node.tablePane2); node.formPane2.title = 'Memory'; node.formPane3 = createFormPane(node.tablePane3); node.formPane3.title = 'Model'; if(flag % 3 === 0){ node.formPane3.v('tag', 'Lenovo Server X3650M5 8871'); }else{ node.formPane3.v('tag', 'Lenovo IBM X3250 5458I21 '); } node.a('model', node.formPane3.v('tag')); return node;}
We control the visibility of the node by controlling the hidden attribute of this node and using the visual filter setVisibleFunc function in graphView:
gv.setVisibleFunc(function(data){ if(data.a('hidden')){ return false; } return true;});Properties panel
With nodes, it is natural to display attributes. Together with the values in the tablePane table panel below, a total of seven attributes are added:
function createProperty(){//Create properties propertyView.addProperties([ { name: 'name',//Get the name attribute, combined with the accessType attribute to finally achieve access to node attributes. The default value of accessType is null, such as name is age, Use the get/set or is/set method of getAge() and setAge(98) to access (name here is name, so get it through getName()) displayName: 'Name'//Set the display text value of the attribute name}, { name: 'hidden',//Get the hidden attribute displayName: 'Hide this node', accessType: 'attr',//If name is hidden, use getAttr( 'hidden') and setAttr('hidden', false) to access the icon: 'images/alert.gif', //Set the icon valueType displayed on the left side of the attribute name: 'boolean', //Used to prompt the component to provide a suitable renderer rendering Boolean type, displayed as a check box editable: true //Set whether the property is editable}, { name: 'grade', displayName: 'Type', accessType : 'attr', drawPropertyValue: function(g, property, value, rowIndex, x, y, w, h, data, view){//Custom attribute value rendering function var cb = function(v) { data.a('grade', v); } return fillFormPane(data.formPane1, w, h, data.tablePane1, serviceType, cb); } }, { name: 'number', displayName: 'memory', accessType: 'attr', drawPropertyValue: function(g, property, value, rowIndex, x, y, w, h, data, view){ var cb = function(v) { data.a('number', v); } return fillFormPane(data.formPane2, w, h, data.tablePane2, serviceSize, cb); } }, { name: 'Interface Type', accessType: 'attr', displayName : 'Interface type' }, { name: 'Graphics card', accessType: 'attr', displayName: 'Graphics card' }, { name: 'Model', accessType: 'attr', displayName: 'Model', } ]);}
The return value of the drawPropertyValue attribute in the third and fourth attributes is the fillFormPane function. The parameters of this function are (form component formP, form component width w, form component height h, click the button in the form component to generate the table component tableP in the pop-up box , the array content arr in the table component, the cb function assigns the value returned by double-clicking the row in the table component to the ht.widget.TextField text box in the form).
The first parameter formP is the creation of the form component. The creation of the form component is to create a form component and add a text box and a button to the form component. This step is also quite simple in HT:
function createFormPane(tPane) {//Create a form panel var formPane = new ht.widget.FormPane(); formPane.setPadding(0);//Set the spacing around the form and the component content var tField = new ht.widget.TextField();//Create a text box tField.setText('');//The content of the text box is empty tField.setDisabled(true);//The text box is inoperable formPane.addRow([// Add a row to the form { id: 'tag', //The unique identification attribute can be obtained through formPane.getItemById(id) and added to the corresponding item object element: tField//Attribute value can be HTML native elements, self-drawn text information inside FormPane, and HT built-in components such as Button, CheckBox and ComboBox, etc.}, { button:{//After setting this attribute, HT will be automatically constructed based on the attribute value ht.widget.Button object and saved in the element attribute label:'...',//The text content on the button onClicked: function(){//Button click event for(var i = 0; i < tPane.dm().size(); i++){//Set tablePane to select the value corresponding to formPane by default var data = tPane.dm().getDatas().get(i); if(data.a('value' ) === formPane.v('tag')){ tPane.sm().ss(data); } } return createDialog(tPane, formPane);//What is returned is to create a dialog box, the content of which is the table panel} } }], [0.5, 0.1]);//Set the display ratio of the first element and the second element in the table component . This table component has only two elements in total, a text box and a button, with proportions of 0.5 and 0.1 respectively return formPane;}
The process of creating the createDialog function is also simple and clear. The title, size, content, etc. of the dialog box are configured through the setConfig(config) method. I passed a parameter tPane table component to createDialog, which is used as the content displayed in the dialog box:
function createDialog(tPane){//Create a pop-up box dialog.setConfig({ title: gv.sm().ld().getName()++formPane.title,//The title of the dialog box content: tPane, // Directly set the content of the pop-up box to the table panel width: 400, //Specify the width of the dialog box height: 200, draggable: true, // Specify whether the dialog box can be dragged and adjusted. closable: true, // Indicates whether to display the close button. maximizable: true, // Indicates whether the dialog box can be maximized. resizeMode: wh, // Move the mouse to the right of the dialog box. The size of the dialog box can be changed in the lower corner, wh means that the width and height can be adjusted buttons: [//Add two buttons { label: 'Cancel', action: function(){ dialog.hide() } }, { label: 'OK ', } ] }); dialog.show();//Show dialog box}
The fourth parameter tableP table component is nothing special. It just creates a form component and then adds columns to the form component. The steps are simple and the code is also quite simple:
function createTableView(arr, dm){//Create table component var tableView = new ht.widget.TableView(dm); tableView.addColumns([//Add column information in batches using json array parameters{ displayName: 'ID', //Get the column name content of the table header drawCell: function(g, data, selected, column, x, y, w, h, tableView){//Customized cell rendering method var id = tableView.getRowIndex(data);//Return the row index where the data object is located ht.Default.drawText(g, 'row' + (id + 1), null, null, x, y, w, h, 'center');//Drawing text parameters (g brush object, value text content, font text font, color text color, x coordinate when drawing starts, y coordinate when drawing starts on y, width when drawing w, height when drawing h , align text horizontal alignment, vAlign text vertical alignment) } }, { displayName: 'Name', drawCell: function(g, data, selected, column, x, y, w, h, tableView){ var id = tableView.getRowIndex(data); var info = arr[id]; ht.Default.drawText(g, info, null, null, x, y, w, h, 'center'); } } ]); return tableView; }
After explaining the parameters in fillFormPane, let’s take a look at how this function is defined. Basically, the last step is to click on the element in the tablePane table component and return this element to the textField text box in the formPane form component:
function fillFormPane(formP, w, h, tableP, arr, cb){//formpane on the right if(formP === undefined){ return; } formP.setWidth(w); formP.setHeight(h); formP.setHGap (0); if(formP.v('tag') === 'undefined' || formP.v('tag') === '') { formP.v('tag', arr[0]); } tableP.onDataDoubleClicked = function(data){//Callback when the data row in the table component is double-clicked var v = arr[data.a('index') ]; formP.v('tag', v);//Set the corresponding item element value according to the id, which is the abbreviation of setValue. The element with the id of tag is the text box dialog.hide(); if (cb){cb(v);} //If the cb parameter is passed in, set the value of data.a('number')/data.a('helloName') to the value of the double-clicked row in the table , that is, assigned to the third and fourth attributes} tableP.onDataClicked = function(data){//Called when the data row in the table component is clicked, dialog.getConfig().buttons[1].action = function(){//Click OK to proceed with the following operations var v = arr[data.a('index')]; formP.v('tag', v); dialog.hide(); if (cb){ cb(v);} } }; return formP.getView();}
function fillFormPane(formP, w, h, tableP, arr, cb){//formpane on the right if(formP === undefined){ return; } formP.setWidth(w); formP.setHeight(h); formP.setHGap (0); if(formP.v('tag') === 'undefined' || formP.v('tag') === '') { formP.v('tag', arr[0]); } tableP.onDataDoubleClicked = function(data){//Callback when the data row in the table component is double-clicked var v = arr[data.a('index') ]; formP.v('tag', v);//Set the corresponding item element value according to the id, which is the abbreviation of setValue. The element with the id of tag is the text box dialog.hide(); if (cb){cb(v);} //If the cb parameter is passed in, set the value of data.a('number')/data.a('helloName') to the value of the double-clicked row in the table , that is, assigned to the third and fourth attributes} tableP.onDataClicked = function(data){//Callback dialog.getConfig().buttons[1].action = when the data row in the table component is clicked function(){//Click OK to proceed with the following operations var v = arr[data.a('index')]; formP.v('tag', v); dialog.hide(); if (cb){ cb(v);} } }; return formP.getView();}
The display of the property bar on the upper right ends here. The table panel on the lower right is created in the same way. You can read the code to understand it by yourself.
autolayoutFinally, let’s talk about the arrangement of nodes in the entire interface. The autolayout automatic layout component in HT provides multiple types of algorithms to automatically arrange node positions based on the relationship between nodes and connections. Automatic layout is often used in scenes where there are many graphics elements or complex connection relationships, making it difficult to manually drag and place them. I present each layout method through buttons. Click the corresponding button, and the layout method will be automatically laid out according to the layout method set by the pressed button:
First, create a new instance, pass in the object that needs automatic layout, which can be DataModel, graphView and graph3dView, and then set the default layout method:
autoLayout = new ht.layout.AutoLayout(gv);setTimeout(function(){ layout('towardsouth', true);//Because before the image is loaded, automatic layout is laid out according to the default size of the node} , 200);
Then create a formPane form panel, add it to the body, and place it in the upper left corner of the body. I won’t paste all the code out, just display the button of the first layout:
function createDirectionForm(){ var form = new ht.widget.FormPane(); form.setWidth(200);//Set the form width form.setHeight(80); document.body.appendChild(form.getView()); form .getView().style.background = '#fff'; form.getView().style.boxShadow = '4px 16px 16px rgba(0, 0, 0, 0.1)';//Set the shadow style form.addRow([//This line is taken out separately as the title { element: 'Auto Layout:',//Displayed text}], [0.1]);//There is only one object in the array, just set the width of one object form.addRow([ { button: { icon: 'Layout/South Layout.json', onClicked: function(){ layout('towardsouth', true); }, background: null, labelColor: '#fff', groupId: 'btn', toolTip: 'south layout', borderColor: null } }, //.. ..Next add the remaining 6 buttons], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]);//There are seven objects in the array, so the width of the seven objects must be set return form;}
These are the more interesting parts. Thank you all for reading. I hope it will be helpful to your study. I also hope that everyone will support VeVb Martial Arts Network.