B4J: Custom component ABMGridTable (Team Project)

One of the most asked for components in ABMaterial is an editable Grid.  We already have ABMTable and ABMTableMutable, but they have their limitations when it comes to being editable. So, as this kind of object is huge, I suggest we make it a Team Effort to build one.

I did have a look around and found a nice one jsGrid which is feature rich, easy to use and fits rather well with the ABMaterial look and feel.

I already did the first part: modified the default CSS so it works with ABMaterial (checkboxes and combos did not work), created a basic class template and a sample on how to use it in a webapp.

Copy the CSS and JS files from the zip the css/custom/ and /js/custom/ folders of the ABMaterial www folder.
Start with localhost:51042/GridDemo

My current result looks like this (allows adding, deleting, changing, sorting and filtering):

upload_2017-2-6_12-34-40

Creating even such a complex component is very easy in B4J with ABMaterial.  Lets dive into some code:

The ABMTableGrid class

'Class module
Sub Class_Globals
	Public ABMComp As ABMCustomComponent
	Private cmbCountries As String
	Private DBdata As String
End Sub

'Initializes the object. Countries and data are Json Strings
Public Sub Initialize(InternalPage As ABMPage, ID As String, Countries As String, data As String)
	ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
	cmbCountries = Countries
	DBdata = data
End Sub

Sub ABMComp_Build(internalID As String) As String
	Return $"
<div id="${internalID}"></div>
<script>var _${internalID};</script>"$
End Sub

Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
	Dim script As String = $"_${internalID} = $("#${internalID}").jsGrid({
		height: "100%",
		width: "100%",
		filtering: true,
		inserting: true,
		editing: true,
		sorting: true,
		paging: true,
		autoload: true,
		pageSize: 10,
		pageButtonCount: 5,
		deleteConfirm: "Do you really want to delete the client?",
		controller: {
			loadData: function(filter) {
				return $.grep(${DBdata}, function(client) {
					var fName = filter.Name.toUpperCase();
					var fAddress = filter.Address.toUpperCase();
					return (!filter.Name || client.Name.toUpperCase().indexOf(fName) > -1)
						&& (!filter.Age || client.Age === filter.Age)
						&& (!filter.Address || client.Address.toUpperCase().indexOf(fAddress) > -1)
						&& (!filter.Country || client.Country === filter.Country)
						&& (filter.Married === undefined || client.Married === filter.Married);
					});
			},
			insertItem: function(insertingClient) {
				var json = JSON.stringify(insertingClient);
				b4j_raiseEvent('${ABMComp.ID}_inserted', {'value':json});
			},
			updateItem: function(updatingClient) {
				var json = JSON.stringify(updatingClient);
				b4j_raiseEvent('${ABMComp.ID}_updated', {'value':json});
			},
			deleteItem: function(deletingClient) {
				var json = JSON.stringify(deletingClient);
				b4j_raiseEvent('${ABMComp.ID}_deleted', {'value':json});
			}
		},
		fields: [
			{ name: "Name", type: "text", width: 150 },
			{ name: "Age", type: "number", width: 50 },
			{ name: "Address", type: "text", width: 200 },
			{ name: "Country", type: "select", items: ${cmbCountries}, valueField: "Id", textField: "Name" },
			{ name: "Married", type: "checkbox", title: "Is Married", sorting: false},
			{ type: "control" }
		]
	});"$

	InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
	' flush not needed, it's done in the refresh method in the lib
End Sub

public Sub SetDBData(data As String)
	DBdata = data
End Sub

Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
	Dim script As String = $"$("#${internalID}").jsGrid("refresh");"$
	InternalPage.ws.Eval(script, Null)
End Sub

Usage in a WebApp:

Sub Class_Globals
	...
	Dim myTableGrid As ABMTableGrid
	' fake database for demo purposes
	Dim database As Map
End Sub

' make sure you copied the files from the .zip in the right folders
public Sub BuildPage()
	...
	page.AddExtraCSSFile("custom/jsgrid.min.css")
	page.AddExtraCSSFile("custom/jsgrid-theme.min.css")
	page.AddExtraJavaScriptFile("custom/jsgrid.min.js")
	...
End Sub

public Sub ConnectPage()
	...
	' as for the demo, we fake a real database here
	LoadFakeData

	page.Cell(2,1).SetFixedHeight(500)

	' for our combo, we add a Json string counting the countries
	myTableGrid.Initialize(page, "myTableGrid", $"[
		{ Name: "", Id: 0 },
		{ Name: "United States", Id: 1 },
		{ Name: "Canada", Id: 2 },
		{ Name: "United Kingdom", Id: 3 },
		{ Name: "France", Id: 4 },
		{ Name: "Brazil", Id: 5 },
		{ Name: "China", Id: 6 },
		{ Name: "Russia", Id: 7 }
	]"$, LoadData)
    page.Cell(2,1).AddComponent(myTableGrid.ABMComp)
	...
End Sub

Sub LoadData() As String
	' we have to return a Json string
	Dim data As StringBuilder
	data.Initialize
	data.Append("[")
	For i = 0 To database.size - 1
		If i > 0 Then
			data.Append(",")
		End If
		data.Append(database.get(i))
	Next
	data.Append("]")
	Return data.ToString
End Sub

Sub myTableGrid_Inserted(value As Map)
	log(value.get("ID")
End Sub

Sub myTableGrid_Updated(value As Map)
	log(value.get("ID")
End Sub

Sub myTableGrid_Deleted(value As Map)
	log(value.get("ID")
End Sub

Sub LoadFakeData()
	database.Initialize
	database.Put(0,$"{"ID": 0,"Name": "Otto Clay","Age": 61,"Country": 6,"Address": "Ap #897-1459 Quam Avenue","Married": false}"$)
	database.Put(1,$"{"ID": 1,"Name": "Connor Johnston","Age": 73,"Country": 7,"Address": "Ap #370-4647 Dis Av.","Married": false}"$)
	...
End Sub

As you can see, adding this rather complex component took me about 10 minutes to make, literally the time to put on a pot of Java. (pun intended!)

So this is were you guys can come in!

Some suggestions:
1. Adapt my class so the fields are generic (maybe by passing a Map with field definitions in initialize and then ‘build’ the correct JavaScript string in FirstRun?).
2. I only have set some basic parameters (like sorting, filtering etc), but this component has a lot more to offer. They can become properties of the class. See the jsGrid link at the top of this article.

Download the full source code and share your changes in this B4J Forum topic, so everyone can benefit from your creation.

Alwaysbusy

Click here to Donation if you like my work

Advertisements

About Alwaysbusy

My name is Alain Bailleul and I'm the Senior Software Architect/Engineer at One-Two. I like to experiment with new technologies, Computer Vision and A.I. My projects are programmed in B4X , Xojo, C#, java, HTML, CSS and JavaScript. View all posts by Alwaysbusy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: