This is the first of a two part tutorial on some big changes I'm making to ABMaterial in 1.09.  It should be fully backwards compatible so no (or very, very little) changes will be needed from your side on your existing projects if you stay working the pre-1.09 way.
I've rewritten the demo app in a dynamic way using the 'static' version and it went swift. And it creates a lot of new potential!
PART 1: Flexibility
-----------------
When ABMaterial 0.x first came out, it was especially useful for static pages. Since then, I gradually made more and more components dynamic. In 1.09, every component gets the upgrade (even ModalSheets!).
In the next download you will find a completely 'rewritten' Demo app being 100% dynamic.
So the steps I had to do to make a Page dynamic:
For the demo app, first I did this in ABMShared for preparation (the steps look very intensive, but I've tried to make it as understandable as possible):
1. Create a new method: ConnectNavigationBar()
2. Move all lines where you build the topbar and sidebar to ConnectPage()
NOTES:
a. In BuildNavigationBar, we have to add a DUMMY item for the top and sidebar:
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
b. In ConnectNavigationBar, we have to remove those DUMMIES
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Full code:
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
3. Create a new Method: ConnectFooter() and ConnectFooterFixed()
4. Move all lines where you add components to ConnectFooter() & ConnectFooterFixed()
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Ok preparations are done, let's move to an example of making a Page dynamic now:
1. Create a new method: ConnectPage()
2. Move all lines where you add components, modal sheets etc to ConnectPage()
3. Add the following line to the BuildPage() method (I'll explain further why):
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
4. Add the following line to the start of ConnectPage()
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
5. Add the following lines to the end of ConnectPage()
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Full code:
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
6. In Page_ready() call our ConnectPage() method:
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
Done! We made our page dynamic.
Some explanation what is happening:
In a 'static' page, all the components are written in the .html file. In a 'dynamic' page they are not. Only when you connect as a user, the components are inserted in their browser.
This seems trivial, but this actually means we can 'write' components (html, css and javascript) at run-time, depending on e.g. the user that logged in. Some user is allowed to see one chart, another user another chart. Or one may be able to delete a record from your database, another is not. Or showing a certain modal sheet depending on the user without having to add all possibilities in the HTML. Or, like will be explained in tutorial part 2, show the page in different languages depending on who logged in!
Why did we need to add those extra lines in BuildPage() and ConnectPage()?
page.ShowLoaderType=ABM.LOADER_TYPE_MANUAL means: We are going to take control when the 'running circles' should hide. As the page is loaded dynamical, without this line the user actually sees the page being build. This can be confusing (e.g. the user sees a button, wants to click it, but suddenly a chart pops up).
So now we have to tell manualy when everything is in its place, using page.FinishedLoading in ConnectPage().
Note that we also here run our FooterConnect() and a page.refresh()! This MUST be done, as it gives the action from the server to the browser to actually perform your changes.
How come we don't have to add those page.Needs...() properties for the components we'll add outside BuildPage()?
When you run (build) you app, ABMaterial wil inspect the B4J source code and try to find out what components you'll planning to use. It generates a text file next to your .jar called: copymewithjar.needs. As the name suggests, when you 'install' your app to your real server, you MUST copy this file next to your .jar file. Why? Because at that time, the B4J source code is no longer available for expection so it uses this file to fill in the page.Needs...() when you start the .jar.
This is an example of a generated copymewithjar.needs file for one of my apps:
	
	
	
	
	
	
	
		
			
			
			
			
			
		
	
	
	
		
	
	
		
	
I've tried to do this analysis to a certain extend. (e.g. it can 'see' if you are needing a component you declared in a module and are using in your page class). But, if somehow it was unable to find it because your code structure is very complex and jumps all around, you will still need to set the page.Needs...() property yourself in BuildPage(). But this shouldn't happen very often. e.g. while converting all my apps, I never had to do it once.
Still a lot of testing to do, but it's looking good
This concludes Part 1. In Part 2 I'll show you how we can use localization in ABMaterial.
Alain
			
			I've rewritten the demo app in a dynamic way using the 'static' version and it went swift. And it creates a lot of new potential!
PART 1: Flexibility
-----------------
When ABMaterial 0.x first came out, it was especially useful for static pages. Since then, I gradually made more and more components dynamic. In 1.09, every component gets the upgrade (even ModalSheets!).
In the next download you will find a completely 'rewritten' Demo app being 100% dynamic.
So the steps I had to do to make a Page dynamic:
For the demo app, first I did this in ABMShared for preparation (the steps look very intensive, but I've tried to make it as understandable as possible):
1. Create a new method: ConnectNavigationBar()
2. Move all lines where you build the topbar and sidebar to ConnectPage()
NOTES:
a. In BuildNavigationBar, we have to add a DUMMY item for the top and sidebar:
			
				B4X:
			
		
		
		 ' you must add at least ONE dummy item if you want to add items to the topbar   in ConnectNaviagationBar
   page.NavigationBar.AddTopItem("DUMMY", "DUMMY", "", "", False)
   ' you must add at least ONE dummy item if you want to add items to the sidebar
   page.NavigationBar.AddSideBarItem("DUMMY", "DUMMY", "", "")
	
			
				B4X:
			
		
		
		  ' Clear the dummies we created in BuildNavigationBar
    page.NavigationBar.Clear
	Full code:
			
				B4X:
			
		
		
		Sub BuildNavigationBar(page As ABMPage, Title As String, logo As String, ActiveTopReturnName As String, ActiveSideReturnName As String, ActiveSideSubReturnName As String)
   page.SetFontStack("arial,sans-serif")
   ' we have to make an ABMImage from our logo url
    Dim sbtopimg As ABMImage
    sbtopimg.Initialize(page, "sbtopimg", logo, 1)
    sbtopimg.SetFixedSize(236, 49)
   page.NavigationBar.Initialize(page, "nav1", ABM.SIDEBAR_MANUAL_HIDEMEDIUMSMALL, Title, True, True, 330, 48, sbtopimg, ABM.COLLAPSE_ACCORDION, "nav1theme") 
   page.NavigationBar.TopBarDropDownConstrainWidth = False
   page.NavigationBar.ActiveTopReturnName = ActiveTopReturnName
   page.NavigationBar.ActiveSideReturnName = ActiveSideReturnName
   page.NavigationBar.ActiveSideSubReturnName = ActiveSideSubReturnName
 
   ' you must add at least ONE dummy item if you want to add items to the topbar   in ConnectNaviagationBar
   page.NavigationBar.AddTopItem("DUMMY", "DUMMY", "", "", False)
 
   ' you must add at least ONE dummy item if you want to add items to the sidebar 
   page.NavigationBar.AddSideBarItem("DUMMY", "DUMMY", "", "")
End Sub
Sub ConnectNavigationBar(page As ABMPage)
    ' Clear the dummies we created in BuildNavigationBar
    page.NavigationBar.Clear
 
    ' new behaviour: on each top item you can set if it should hide of not on a medium or small device.
    page.NavigationBar.AddTopItem("Contact", "", "mdi-action-account-circle", "", False)
    ' shortened for this tutorial, you move them all!
    ' --------------------------------------------
    page.NavigationBar.AddSideBarItem("Icons", "Icons", "mdi-action-account-circle", "../IconsPage/abmaterial-material-icons.html")  
    '... the rest
End Sub
	4. Move all lines where you add components to ConnectFooter() & ConnectFooterFixed()
			
				B4X:
			
		
		
		Sub BuildFooter(page As ABMPage)     
    page.Footer.AddRows(1, True, "").AddCellsOS(2,0,0,0,6,6,6, "")
    page.Footer.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components 
 
    page.Footer.UseTheme("footertheme")     
End Sub
Sub ConnectFooter(page As ABMPage) 
    Dim lbl1 As ABMLabel
    lbl1.Initialize(page, "footlbl1", "Blog: Alwaysbusy's Corner{BR}{BR}B4J by Anywhere Software{BR}Materialize CSS by students from Carnegie Mellon University",ABM.SIZE_PARAGRAPH, False, "whitefc")
    page.Footer.Cell(1,1).AddComponent(lbl1)
 
    Dim lbl2 As ABMLabel
    lbl2.Initialize(page, "footlbl2", "ABMaterial Copyright @2015-2016{BR}By Alain Bailleul{BR}{BR}Email: alain.bailleul@telenet.be",ABM.SIZE_PARAGRAPH, False, "whitefc")
    page.Footer.Cell(1,2).AddComponent(lbl2) 
End Sub
Sub BuildFooterFixed(page As ABMPage) 
    page.isFixedFooter= True
    ' because we have a fixed footer at the bottom, we have to adjust the padding of the body in pixels
    page.PaddingBottom = 200
 
    page.Footer.AddRows(1, True, "").AddCellsOS(2,0,0,0,6,6,6, "")
    page.Footer.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components 
 
    page.Footer.UseTheme("footertheme") 
End Sub
Sub ConnectFooterFixed(page As ABMPage)     
    Dim lbl1 As ABMLabel
    lbl1.Initialize(page, "footlbl1", "Blog: Alwaysbusy's Corner{BR}{BR}B4J by Anywhere Software{BR}Materialize CSS by students from Carnegie Mellon University",ABM.SIZE_PARAGRAPH, False, "whitefc")
    page.Footer.Cell(1,1).AddComponent(lbl1)
 
    Dim lbl2 As ABMLabel
    lbl2.Initialize(page, "footlbl2", "ABMaterial Copyright @2015-2016{BR}By Alain Bailleul{BR}{BR}Email: alain.bailleul@telenet.be",ABM.SIZE_PARAGRAPH, False, "whitefc")
    page.Footer.Cell(1,2).AddComponent(lbl2) 
End Sub
	Ok preparations are done, let's move to an example of making a Page dynamic now:
1. Create a new method: ConnectPage()
2. Move all lines where you add components, modal sheets etc to ConnectPage()
3. Add the following line to the BuildPage() method (I'll explain further why):
			
				B4X:
			
		
		
		page.ShowLoaderType=ABM.LOADER_TYPE_MANUAL ' NEW
	
			
				B4X:
			
		
		
		    'NEW
    ABMShared.ConnectNavigationBar(page)
	
			
				B4X:
			
		
		
		' also add the components to the footer
    ABMShared.ConnectFooter(page)
 
    page.Refresh ' IMPORTANT
 
    ' NEW, because we use ShowLoaderType=ABM.LOADER_TYPE_MANUAL
    page.FinishedLoading 'IMPORTANT
	Full code:
			
				B4X:
			
		
		
		public Sub BuildPage()
    ' initialize the theme
    BuildTheme
 
    ' initialize this page using our theme
    page.InitializeWithTheme(Name, "/ws/" & AppName & "/" & Name, False, theme)
    page.ShowLoader=True
    page.ShowLoaderType=ABM.LOADER_TYPE_MANUAL ' NEW
    page.PageTitle = "ABMCodeLabel"
    page.PageDescription = "The code label component " 
    page.PageHTMLName = "abmaterial-code-label.html"
    page.PageKeywords = "ABMaterial, material design, B4X, B4J, SEO, framework, search engine optimization"
    page.PageSiteMapPriority = "0.50"
    page.PageSiteMapFrequency = ABM.SITEMAP_FREQ_MONTHLY
    page.UseGoogleAnalytics(ABMShared.TrackingID, Null) ' IMPORTANT Change this to your own TrackingID !!!!!!!
     
    ABMShared.BuildNavigationBar(page, "ABMCodeLabel", "../images/logo.png", "", "Controls", "ABMCodeLabel")
     
    ' create the page grid
    page.AddRows(5,True, "").AddCells12(1,"")
    page.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
     
    ABMShared.BuildFooter(page)
End Sub
Sub ConnectPage()
    'NEW
    ABMShared.ConnectNavigationBar(page)
    ' add paragraph 
    page.Cell(1,1).AddComponent(ABMShared.BuildParagraph(page,"par1","ABMCodeLabels are blocks that represent source programming code within your page.  It can be used, like for this app, to make a help website or tutorial that explains your library.") ) 
    ' add paragraph 
    page.Cell(1,1).AddComponent(ABMShared.BuildParagraph(page,"par2","There are two methods to setup your source code string: Using a B4J List object or using the B4J Smart Strings.  An example of both, you can use the one you feel most comfortable with.  Note, Smart Strings cannon show everything: Using as code Sub Name() ... End Sub ... Sub Name2() ... End Sub will not work with Smart Strings!") ) 
 
    ' add codeblock 
    Dim code1 As StringBuilder
    code1.Initialize
    code1.Append("// add codeblock").Append(CRLF) 
    code1.Append("Dim code2 As StringBuilder").Append(CRLF)
    code1.Append("code2.Initialize").Append(CRLF)
    code1.Append("code2.Append(""Dim btn4 As ABMButton"").Append(CRLF)").Append(CRLF)
    code1.Append("code2.Append(""btn4.Initializefloating(page, ""btn4"", ""mdi-image-palette"", ""darkred"")"").Append(CRLF)").Append(CRLF)
    code1.Append("code2.Append(""btn4.Large = True"").Append(CRLF)").Append(CRLF)
    code1.Append("code2.Append(""page.Cell(5,1).AddComponent(btn4)"").Append(CRLF)").Append(CRLF)
    code1.Append("page.Cell(6,1).AddComponent(ABMShared.BuildCodeBlock(page, ""code2"", code2))").Append(CRLF)
     
    page.Cell(2,1).AddComponent(ABMShared.BuildCodeBlock(page, "code1", code1))
 
    ' add codeblock 
    Dim code4 As StringBuilder
    code4.Initialize
    code4.Append("// add codeblock").Append(CRLF) 
    code4.Append("Dim code3 As String = $""Dim btn4 As ABMButton").Append(CRLF)
    code4.Append("btn4.Initializefloating(page, ""btn4"", ""mdi-image-palette"", ""darkred"")").Append(CRLF)
    code4.Append("btn4.Large = True").Append(CRLF)
    code4.Append("page.Cell(5,1).AddComponent(btn4)""$").Append(CRLF)
    code4.Append("page.Cell(6,1).AddComponent(ABMShared.BuildCodeBlockFromSmartString(page, ""code3"", code3))").Append(CRLF)
         
    page.Cell(2,1).AddComponent(ABMShared.BuildCodeBlockFromSmartString(page, "code4", code4))
 
    ' also add the components to the footer
    ABMShared.ConnectFooter(page)
 
    page.Refresh ' IMPORTANT
 
    ' NEW, because we use ShowLoaderType=ABM.LOADER_TYPE_MANUAL
    page.FinishedLoading 'IMPORTANT
End Sub
	
			
				B4X:
			
		
		
		Sub Page_Ready()
    Log("ready!")
 
    ' NEW
    ConnectPage
 
    page.RestoreNavigationBarPosition
End Sub
	Done! We made our page dynamic.
Some explanation what is happening:
In a 'static' page, all the components are written in the .html file. In a 'dynamic' page they are not. Only when you connect as a user, the components are inserted in their browser.
This seems trivial, but this actually means we can 'write' components (html, css and javascript) at run-time, depending on e.g. the user that logged in. Some user is allowed to see one chart, another user another chart. Or one may be able to delete a record from your database, another is not. Or showing a certain modal sheet depending on the user without having to add all possibilities in the HTML. Or, like will be explained in tutorial part 2, show the page in different languages depending on who logged in!
Why did we need to add those extra lines in BuildPage() and ConnectPage()?
page.ShowLoaderType=ABM.LOADER_TYPE_MANUAL means: We are going to take control when the 'running circles' should hide. As the page is loaded dynamical, without this line the user actually sees the page being build. This can be confusing (e.g. the user sees a button, wants to click it, but suddenly a chart pops up).
So now we have to tell manualy when everything is in its place, using page.FinishedLoading in ConnectPage().
Note that we also here run our FooterConnect() and a page.refresh()! This MUST be done, as it gives the action from the server to the browser to actually perform your changes.
How come we don't have to add those page.Needs...() properties for the components we'll add outside BuildPage()?
When you run (build) you app, ABMaterial wil inspect the B4J source code and try to find out what components you'll planning to use. It generates a text file next to your .jar called: copymewithjar.needs. As the name suggests, when you 'install' your app to your real server, you MUST copy this file next to your .jar file. Why? Because at that time, the B4J source code is no longer available for expection so it uses this file to fill in the page.Needs...() when you start the .jar.
This is an example of a generated copymewithjar.needs file for one of my apps:
			
				B4X:
			
		
		
		ABMApplication:NeedsInput;NeedsTextArea;NeedsMask
AgendaPage:NeedsCalendar;NeedsSwitch
ProspectPage:NeedsRadio;NeedsInput;NeedsTextArea;NeedsMask;NeedsCombo;NeedsSlider;NeedsGoogleMap;NeedsLists;NeedsDateTimeScroller;NeedsSwitch
ReferentiesPage:NeedsRadio;NeedsInput;NeedsTextArea;NeedsMask;NeedsCombo;NeedsSlider;NeedsGoogleMap;NeedsLists
StartPage:NeedsInput;NeedsTextArea;NeedsMask;NeedsPagination;NeedsTable;NeedsSortingTable;NeedsTabs;NeedsCombo;NeedsDateTimeScroller;NeedsSwitch
	I've tried to do this analysis to a certain extend. (e.g. it can 'see' if you are needing a component you declared in a module and are using in your page class). But, if somehow it was unable to find it because your code structure is very complex and jumps all around, you will still need to set the page.Needs...() property yourself in BuildPage(). But this shouldn't happen very often. e.g. while converting all my apps, I never had to do it once.
Still a lot of testing to do, but it's looking good
This concludes Part 1. In Part 2 I'll show you how we can use localization in ABMaterial.
Alain
			
				Last edited: