Well
I wanted to create a modal dialog (ABMModalSheet), that will have options for 1. Forgot Password and 2. Not a Member Yet to 1. enable moving to another screen if user forgot the password and 2. enable a user to register by moving to another screen that will prompt the user to register.
In this part of the tutorial we will work with the Sign In screen shown in the gif above. As you have noted, we have a logo, a small description of what we need to do here, an email and a password prompt, a label for forgot password and another for "not a member yet" and then a "Next" button to do whatever is necessary after that.
ABMModalSheet Definition:
An ABMModalSheet component is like a mini ABMPage helper, but it pops up over the existing page. It has two ABMContainers, one that makes up the header and one for the footer section, so you can use everyting an ABMContainer can.
ABMModalSheets and its components have to be added to the page in the PageBuild() method. You can the later load the sheet and modify the content before you open it."
#source HelperModalSheetPage.bas file (Demo.b4j ABMaterial 4.03)
Another container actually exists, the .Content one which we use for this exercise as the .Footer and .Header are not used here.
Goal
A user is expected to enter their email and password, these are verified against a database table for existence, if they dont exist, a prompt telling the user is provided.
As seen above, this modal sheet has an option for Forgot Password and Not a Member yet besides the sign in functionality. The forgot password label is right aligned whilst the Not a Member yet label is centered on the row. This is achieved with the use of cell theming.
1. Create the modal sheet
The name of our modal sheet is msLogin and here we difine its structure and its contents.
Some things to note. I have used the AddRows and the AddCellsOSMPV method here to create my modal sheet rows, that is just my preference and you can use whatever methods you deem fit to achieve the same results. All my columns are 12 in size across all devices. Whilst there is no content on the .Header and .Footer, I have opted to include the .BuildGrid methods for those, these can be commented out. I forget stuff so for me this is a pre-caution.
The label for "Not a Member yet" is centered, the theme "centercontent" has been used, this is defined as
1.1. Centering the label
As noted above, I did NOT actually follow a sequential RC in adding the components. I started with 8,1 then up to 2,1. This might be confusing, but just to think of it, it does not matter I think because after all ABMaterial places your components where they are supposed to be. Cool stuff, but, going forward, its really better to follow a sequential approach as it helps in following your code, I think.
1.2. Right align the label
1.3. Call the msLoginBuild on BuildPage
This call above should be added to the BuildPage method.
1.4 Show the modal sheet
As my page has only this modal sheet, I need this modal sheet to appear as soon as the page is shown. I have decided to put that call on the ConnectPage method of my page.
Getting the Sign In details & Validating them
In this next step, we need a way to get the contents from the modal sheet, check if they are entered i.e. not blank. To do this I have created two methods, to get the details as a map and then run that map against a fixed validating method, these are msLoginGetContents and msLoginValidate respectively.
The validation method is such that when an email or password are not entered, the focus is brought back to the needed component. As noted above, this calls an ABMShared.Warn method. I have defined that as a simple toast message below.
The toast theme, defined as..
The email and password are read as soon as the "Next" button is clicked, thus to achieve that we need to call a method to do the checks. This method will 1. get the entered details, 2. validate them 3. check them against a database table and process the next sequence of events.
For this I have defined a login call.
From the code above, a lot of work happens. We get the contents, run a select where?? call to check the details against a database and if these dont exist, tell the user with a toast. if the details are obtained, we read the group permissions that this user belongs to and then save the details to a session variable.
As the focus of this tutorial is the UX, you can refer to this tutorial I wrote about groups permissions etc etc.
The next button is clicked, thus, its code should be simple as..
If the user is validated, move to the next page.
The user forgot the password (Part 2)
When a user clicks this label, the user is supposed to be prompted of the email address, this should be validated, checked against the database, the user details should be read and these should be emailed to the user. For now, we just go to that page..
The user is not a member yet (Part 3)
When a user clicks this label, the user is supposed to be sent to another page to register, these user details should be read, the compulsory items validated and then if the user does not already exist, a new record should be created in the database.
Disclaimer:
There are different ways to achieve the same thing and this is just my own method of doing it and its not cut in stone. I could be wrong and there might be stuff I have left out, I'm not an expert and learning as everybody else. #SharingTheGoodness
HappyCoding...
I wanted to create a modal dialog (ABMModalSheet), that will have options for 1. Forgot Password and 2. Not a Member Yet to 1. enable moving to another screen if user forgot the password and 2. enable a user to register by moving to another screen that will prompt the user to register.
In this part of the tutorial we will work with the Sign In screen shown in the gif above. As you have noted, we have a logo, a small description of what we need to do here, an email and a password prompt, a label for forgot password and another for "not a member yet" and then a "Next" button to do whatever is necessary after that.
ABMModalSheet Definition:
An ABMModalSheet component is like a mini ABMPage helper, but it pops up over the existing page. It has two ABMContainers, one that makes up the header and one for the footer section, so you can use everyting an ABMContainer can.
ABMModalSheets and its components have to be added to the page in the PageBuild() method. You can the later load the sheet and modify the content before you open it."
#source HelperModalSheetPage.bas file (Demo.b4j ABMaterial 4.03)
Another container actually exists, the .Content one which we use for this exercise as the .Footer and .Header are not used here.
Goal
A user is expected to enter their email and password, these are verified against a database table for existence, if they dont exist, a prompt telling the user is provided.
- The email and password are compulsory.
- A user can click the "Forgot Password" label, this will take the user to another screen to process that action. This we will discuss in Part 2
- A user can click the "Not a Member yet" label, this will take the user to another screen to process that action. This we will discuss in Part 3
- A notification is being used to indicate any errors on the page. This uses a red theme.
- The modal sheet is not dismissable i.e a user needs to perform an action to close it.
As seen above, this modal sheet has an option for Forgot Password and Not a Member yet besides the sign in functionality. The forgot password label is right aligned whilst the Not a Member yet label is centered on the row. This is achieved with the use of cell theming.
1. Create the modal sheet
The name of our modal sheet is msLogin and here we difine its structure and its contents.
B4X:
Private Sub msLoginBuild() As ABMModalSheet
Dim msLogin As ABMModalSheet
msLogin.Initialize(page, "msLogin", False, ABM.MODALSHEET_TYPE_NORMAL, "ms")
msLogin.Size = ABM.MODALSHEET_SIZE_NORMAL
msLogin.IsDismissible = False
msLogin.IsTextSelectable = True
msLogin.Content.AddRows(1,True,"").AddCells12(1, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 10, 0, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 10, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 10, 10, ABM.VISIBILITY_ALL, "")
msLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
msLogin.Header.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
msLogin.Content.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
msLogin.Footer.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
'Add components to ModalSheet
Dim lbllblCreateAccount As ABMLabel
lbllblCreateAccount.Initialize(page, "lbllblCreateAccount", $"{C:#CC9966}{I}{B}Not a Member yet?{/B}{/I}{/C}"$, ABM.SIZE_PARAGRAPH, False, "centercontent")
lbllblCreateAccount.Clickable = True
lbllblCreateAccount.SetCursor(ABM.CURSOR_POINTING)
lbllblCreateAccount.supportemoji = False
msLogin.Content.Cell(8,1).AddComponent(lbllblCreateAccount)
msLogin.Content.Cell(8,1).UseTheme("centercontent")
Dim txtemail As ABMInput
txtemail.Initialize(page, "txtemail", ABM.INPUT_EMAIL, "Email Address", False, "")
txtemail.Tag = "txtemail"
msLogin.Content.Cell(4,1).AddComponent(txtemail)
Dim txtpassword As ABMInput
txtpassword.Initialize(page, "txtpassword", ABM.INPUT_PASSWORD, "Password", False, "")
txtpassword.Tag = "txtpassword"
msLogin.Content.Cell(5,1).AddComponent(txtpassword)
Dim btnNext As ABMButton
btnNext.InitializeRaised(page, "btnNext", "", "", "Next", "")
btnNext.Tag = "btnNext"
btnNext.UseFullCellWidth = True
btnNext.Size = ABM.BUTTONSIZE_NORMAL
msLogin.Content.Cell(7,1).AddComponent(btnNext)
Dim lblheader As ABMLabel
lblheader.Initialize(page, "lblheader", $"{B}To continue, let's verify it's you{/B}"$, ABM.SIZE_PARAGRAPH, False, "")
lblheader.supportemoji = False
msLogin.Content.Cell(3,1).AddComponent(lblheader)
Dim lbllblForgotPassword As ABMLabel
lbllblForgotPassword.Initialize(page, "lbllblForgotPassword", $"{C:#CC9966}{I}Forgot Password?{/I}{/C}"$, ABM.SIZE_PARAGRAPH, False, "rightlabel")
lbllblForgotPassword.Clickable = True
lbllblForgotPassword.SetCursor(ABM.CURSOR_POINTING)
lbllblForgotPassword.istextselectable = True
lbllblForgotPassword.supportemoji = False
msLogin.Content.Cell(6,1).AddComponent(lbllblForgotPassword)
Dim imglogo As ABMImage
imglogo.Initialize(page, "imglogo", "../images/logo.png?"& DateTime.Now, 1)
imglogo.Tag = "imglogo"
imglogo.IsResponsive = False
imglogo.IsCircular = False
imglogo.IsClickable = False
imglogo.IsMaterialBoxed = False
msLogin.Content.Cell(2,1).AddComponent(imglogo)
Return msLogin
End Sub
Some things to note. I have used the AddRows and the AddCellsOSMPV method here to create my modal sheet rows, that is just my preference and you can use whatever methods you deem fit to achieve the same results. All my columns are 12 in size across all devices. Whilst there is no content on the .Header and .Footer, I have opted to include the .BuildGrid methods for those, these can be commented out. I forget stuff so for me this is a pre-caution.
The label for "Not a Member yet" is centered, the theme "centercontent" has been used, this is defined as
1.1. Centering the label
B4X:
MyTheme.AddCellTheme("centercontent")
MyTheme.Cell("centercontent").align = ABM.CELL_ALIGN_CENTER
As noted above, I did NOT actually follow a sequential RC in adding the components. I started with 8,1 then up to 2,1. This might be confusing, but just to think of it, it does not matter I think because after all ABMaterial places your components where they are supposed to be. Cool stuff, but, going forward, its really better to follow a sequential approach as it helps in following your code, I think.
1.2. Right align the label
B4X:
MyTheme.AddLabelTheme("rightlabel")
MyTheme.Label("rightlabel").align = ABM.INPUT_TEXTALIGN_RIGHT
1.3. Call the msLoginBuild on BuildPage
B4X:
page.AddModalSheetTemplate(msLoginBuild)
This call above should be added to the BuildPage method.
1.4 Show the modal sheet
As my page has only this modal sheet, I need this modal sheet to appear as soon as the page is shown. I have decided to put that call on the ConnectPage method of my page.
B4X:
page.ShowModalSheet("msLogin")
Getting the Sign In details & Validating them
In this next step, we need a way to get the contents from the modal sheet, check if they are entered i.e. not blank. To do this I have created two methods, to get the details as a map and then run that map against a fixed validating method, these are msLoginGetContents and msLoginValidate respectively.
B4X:
'Get the contents of the modal sheet input components and save to a map
Sub msLoginGetContents() As Map
Dim pMap As Map
pMap.Initialize
Dim msLogin As ABMModalSheet
msLogin = page.ModalSheet("msLogin")
Dim txtemail As ABMInput = msLogin.Content.Component("txtemail")
Dim txtpassword As ABMInput = msLogin.Content.Component("txtpassword")
pMap.put("email", txtemail.Text)
pMap.put("password", txtpassword.Text)
Return pMap
End Sub
'Get the contents of the modal sheet from GetContents and check validity
Sub msLoginValidate(gMap As Map) As Boolean
Dim msLogin As ABMModalSheet
msLogin = page.ModalSheet("msLogin")
Dim stxtemail As String
stxtemail = gMap.get("email")
If stxtemail = "null" Then stxtemail = ""
If stxtemail.Length = 0 Then
ABMShared.Warn(page,"Email Address cannot be blank. Please enter a value.")
Dim txtemail As ABMInput = msLogin.Content.Component("txtemail")
txtemail.SetFocus
Return False
End If
Dim stxtpassword As String
stxtpassword = gMap.get("password")
If stxtpassword = "null" Then stxtpassword = ""
If stxtpassword.Length = 0 Then
ABMShared.Warn(page,"Password cannot be blank. Please enter a value.")
Dim txtpassword As ABMInput = msLogin.Content.Component("txtpassword")
txtpassword.SetFocus
Return False
End If
Return True
End Sub
The validation method is such that when an email or password are not entered, the focus is brought back to the needed component. As noted above, this calls an ABMShared.Warn method. I have defined that as a simple toast message below.
B4X:
Sub Warn(page As ABMPage, msg As String)
toastCount = toastCount + 1
page.ShowToast("toast" & toastCount, "toastred", $"${msg}"$, 3000, False)
End Sub
The toast theme, defined as..
B4X:
MyTheme.AddToastTheme("toastred")
MyTheme.Toast("toastred").actionforecolorintensity = ABM.INTENSITY_NORMAL
MyTheme.Toast("toastred").backcolor = ABM.COLOR_RED
MyTheme.Toast("toastred").forecolorintensity = ABM.INTENSITY_NORMAL
MyTheme.Toast("toastred").actionforecolor = ABM.COLOR_BLACK
MyTheme.Toast("toastred").rounded = True
MyTheme.Toast("toastred").forecolor = ABM.COLOR_WHITE
MyTheme.Toast("toastred").backcolorintensity = ABM.INTENSITY_NORMAL
The email and password are read as soon as the "Next" button is clicked, thus to achieve that we need to call a method to do the checks. This method will 1. get the entered details, 2. validate them 3. check them against a database table and process the next sequence of events.
For this I have defined a login call.
B4X:
Private Sub msLoginSignIn() As Boolean
'define a map to hold the form contents
Dim m As Map
'read the file contents to a map
m = msLoginGetContents
'validate the form contents where required
If msLoginValidate(m) = False Then
Return False
End If
'the form contents are ok, continue with the sign in process
'define the search criteria, fields must equal
Dim w As Map
Dim nk As String
Dim nv As String
w.Initialize
For Each strKey As String In m.Keys
nv = m.Get(strKey)
nk = strKey & "="
w.put(nk, nv)
Next
'Get connection from current pool if MySQL/MSSQL
Dim jSQL As SQL = ABMShared.SQLGet
Dim UserMap As Map = ABMShared.SQLSelectRecordWhereMap(jSQL,"users", w)
If UserMap.IsInitialized = False Then
ABMShared.Warn(page,"The Sign In credentials you have provided are incorrect. Please check them and try to sign in again.")
ws.Session.SetAttribute("IsAuthorized", "")
Return False
End If
Dim uactive As String = UserMap.get("useractive")
ws.Session.SetAttribute("UserActive", uactive)
If uactive = 0 Then
ABMShared.Warn(page,"Your Profile is currently in-active, please contact the App Administrator.")
Return False
End If
Dim uemail As String = UserMap.get("email")
ws.Session.SetAttribute("UserEmail", uemail)
Dim uname As String = UserMap.get("username")
ws.Session.SetAttribute("authName", uname)
Dim uid As String = UserMap.get("id")
ws.Session.SetAttribute("UserID", uid)
Dim utype As String = UserMap.get("groupid")
ws.Session.SetAttribute("UserType", utype)
ws.Session.SetAttribute("IsAuthorized", "true")
ws.Session.SetAttribute("authType", "local")
'Build the permissions
ABMShared.PermissionsL.Initialize
ABMShared.Permissions.Initialize
ABMShared.PermissionsL = ABMShared.SQLExecuteMaps(jSQL,"select * from GroupPermissions where groupid = ?", Array As String(utype))
For Each permission As Map In ABMShared.PermissionsL
Dim spageName As String = permission.get("pagename")
spageName = spageName.tolowercase
ABMShared.Permissions.put(spageName,permission)
Next
'Close the connection to the database
ABMShared.SQLClose(jSQL)
Return True
End Sub
From the code above, a lot of work happens. We get the contents, run a select where?? call to check the details against a database and if these dont exist, tell the user with a toast. if the details are obtained, we read the group permissions that this user belongs to and then save the details to a session variable.
As the focus of this tutorial is the UX, you can refer to this tutorial I wrote about groups permissions etc etc.
The next button is clicked, thus, its code should be simple as..
B4X:
'An ABMButton has been clicked
Sub btnNext_Clicked(Target As String)
If msLoginSignIn = False Then Return
ABMShared.NavigateToPage(ws, ABMPageId, "../frmMainMenu/frmMainMenu.html")
End Sub
If the user is validated, move to the next page.
The user forgot the password (Part 2)
When a user clicks this label, the user is supposed to be prompted of the email address, this should be validated, checked against the database, the user details should be read and these should be emailed to the user. For now, we just go to that page..
B4X:
'The label has been clicked
Sub lbllblForgotPassword_Clicked(Target As String)
ABMShared.NavigateToPage(ws, ABMPageId,"../frmForgotPassword/frmForgotPassword.html")
End Sub
The user is not a member yet (Part 3)
When a user clicks this label, the user is supposed to be sent to another page to register, these user details should be read, the compulsory items validated and then if the user does not already exist, a new record should be created in the database.
B4X:
'The label has been clicked
Sub lbllblCreateAccount_Clicked(Target As String)
ABMShared.NavigateToPage(ws, ABMPageId,"../frmCreateAccount/frmCreateAccount.html")
End Sub
Disclaimer:
There are different ways to achieve the same thing and this is just my own method of doing it and its not cut in stone. I could be wrong and there might be stuff I have left out, I'm not an expert and learning as everybody else. #SharingTheGoodness
HappyCoding...
Attachments
Last edited: