B4J Tutorial [ABMaterial]: Creating a Sign In Modal Dialog with Options: Part 1

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.

  • 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.
Reproduction

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

  • ModalLogin.gif
    ModalLogin.gif
    287.5 KB · Views: 453
  • frmLogin.bas
    19.8 KB · Views: 447
Last edited:

alwaysbusy

Expert
Licensed User
Longtime User

Mashiane

Expert
Licensed User
Longtime User
Part 1,2,2.1 and 3 of this tutorial is now complete. Hope you enjoyed it.

Coming Soon: Activating a user's account

When a user Signs Up, on success, their accounts are in-active and thus they will not be able to sign in until their accounts are active. When completed, a user is supposed to get an email with a link that they need to click on and activate their account. In my dream world this should execute a php script on the server, activating the account.

This in essence does two things, verifies their email address and also enables them to use TeenCoach. I think I saw an article about getting query string values somewhere here and instead of a php script, will follow that approach.

Any ideas??
 
Top