B4J Tutorial [ABMaterial] Star Rating ABMCustomComponent

Discussion in 'B4J Tutorials' started by alwaysbusy, Sep 14, 2017.

  1. alwaysbusy

    alwaysbusy Expert Licensed User

    Michał asked me in the feedback app to implement some kind of Star Rating component. As this is not so commonly needed, I thought this could be another good example of an ABMCustomComponent.

    This is what we are going to build (we can 'hover' over the stars to set it):

    [​IMG]

    I show you how to do it in ABMaterial 3.75 first with the new features (released soon), and will then explain what needs to be changed for previous versions.

    1. We create a new standard class CompStarRating:
    Code:
    'Class module
    Sub Class_Globals
       
    Public ABMComp As ABMCustomComponent
       
    Dim myInternalName As String   
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize(InternalPage As ABMPage, ID As String, radioName As String)
       myInternalName = radioName
       
    Dim CSS As String = $"
       #half-stars-example .rating-group {
           display: inline-flex;
       }
       #half-stars-example .rating__icon {
           pointer-events: none;
       }
       /* This is an important part: we MUST override the Materialize CSS implementation of the the radio button! */
       #half-stars-example .rating__input + label:before,
       #half-stars-example .rating__input + label:after {
           position: absolute !important;
           left: -9999px !important;           
       }   
       #half-stars-example .rating__label {
           cursor: pointer;
           padding: 0 0.1em;
           font-size: 2rem;
         margin-top: 1rem;
       }
       #half-stars-example .rating__label--half {
           padding-right: 0;
           margin-right: -0.6em;
           z-index: 2;
       }
       #half-stars-example .rating__icon--star {
           color: orange;
       }
       #half-stars-example .rating__icon--none {
           color: #eee;
       }
       #half-stars-example .rating__input--none:checked + .rating__label .rating__icon--none {
           color: red;
       }
       #half-stars-example .rating__input:checked ~ .rating__label .rating__icon--star {
           color: #ddd;
       }
       #half-stars-example .rating-group:hover .rating__label .rating__icon--star,
       #half-stars-example .rating-group:hover .rating__label--half .rating__icon--star {
           color: orange;
       }
       #half-stars-example .rating__input:hover ~ .rating__label .rating__icon--star,
       #half-stars-example .rating__input:hover ~ .rating__label--half .rating__icon--star {
           color: #ddd;
       }
       #half-stars-example .rating-group:hover .rating__input--none:not(:hover) + .rating__label .rating__icon--none {
           color: #eee;
       }
       #half-stars-example .rating__input--none:hover + .rating__label .rating__icon--none {
           color: red;
       }"$

       ABMComp.Initialize(
    "ABMComp", Me, InternalPage, ID, CSS)
    End Sub

    Sub ABMComp_Build(InternalPage As ABMPage, internalID As StringAs String
       
    Return $"<div id="half-stars-example">
      <div class="rating-group" id="$
    {internalID}">
      <input class="rating__input rating__input--none" checked name="$
    {myInternalName}" id="rating-0" value="0" type="radio">
      <label title="0 stars" class="rating__label" for="rating-0">&nbsp;</label>
         <input class="rating__input" id="rating-00" value="-1" type="radio">
      <label title="0.5 stars" class="rating__label rating__label--half" for="rating-05"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-05" value="0.5" type="radio">
      <label title="1 star" class="rating__label" for="rating-10"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-10" value="1" type="radio">
      <label title="1.5 stars" class="rating__label rating__label--half" for="rating-15"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-15" value="1.5" type="radio">
      <label title="2 stars" class="rating__label" for="rating-20"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-20" value="2" type="radio">
      <label title="2.5 stars" class="rating__label rating__label--half" for="rating-25"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-25" value="2.5" type="radio">
      <label title="3 stars" class="rating__label" for="rating-30"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-30" value="3" type="radio">
      <label title="3.5 stars" class="rating__label rating__label--half" for="rating-35"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-35" value="3.5" type="radio">
      <label title="4 stars" class="rating__label" for="rating-40"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-40" value="4" type="radio">
      <label title="4.5 stars" class="rating__label rating__label--half" for="rating-45"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-45" value="4.5" type="radio">
      <label title="5 stars" class="rating__label" for="rating-50"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
      <input class="rating__input" name="$
    {myInternalName}" id="rating-50" value="5" type="radio">
      </div>  
    </div>"$

    End Sub

    public Sub GetCurrentRating(InternalPage As ABMPage) As Double
       
    Dim script As String = $"return $('input[name=${myInternalName}]:checked').val();"$
       
    Dim ret As Future = InternalPage.ws.EvalWithResult(script, Null)
       InternalPage.ws.Flush
     
       
    Return ret.Value
    End Sub

    public Sub SetCurrentRating(InternalPage As ABMPage, value As Double) {
       
    Dim script As String = $" $('input[name=${myInternalName}][value="${value}"]').prop('checked', 'checked');"$
       InternalPage.ws.Eval(script, 
    Null)
       InternalPage.ws.Flush
    End Sub

    ' Is useful to run some initalisation script.
    Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
       
    Dim script As String = $""$
       InternalPage.ws.Eval(script, 
    Array As Object(ABMComp.ID))
       
    ' flush not needed, it's done in the refresh method in the lib
    End Sub
    ' runs when a refresh is called
    Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
       
    Dim script As String = $""$
       InternalPage.ws.Eval(script, 
    Array As Object(ABMComp.ID))
    End Sub
    ' do the stuff needed when the object is removed
    Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)
    End Sub
    ABMaterial users will notice I have added the CSS directly into the component. By doing so, we do not need to use the page.AddExtraCSSFile method in the Page_Build() method. (you can still do it this way, and in some cases it may be better as this part can be big. And in the final version, compress it!). In ABMaterial before 3.75, using AddExtraCSSFile is the only way possible.

    The only difficult part in the CSS was I had to override Materialize CSS's implementation of a radio button (the 'circle'). So we had to get rid of it.

    2. Now we can make a variable for our StarComponent in the page Class_Globals, as we want to be able to Set and Get the value:
    Code:
    Dim myRating As CompStarRating
    3. In Page_Connect() we add our component and a couple of buttons:
    Code:
    ...
    myRating.Initialize(
    page"myRating""rating")
    page.Cell(1,1).AddComponent(myRating.ABMComp)
     
    Dim btn As ABMButton
    btn.InitializeFlat(
    page"btn""""""Get value""")
    page.Cell(2,1).AddComponent(btn)
       
    Dim btn2 As ABMButton
    btn2.InitializeFlat(
    page"btn2""""""Set 2.5 stars value""")
    page.Cell(3,1).AddComponent(btn2)
    ...
    page.Refresh
    ...
    4. And our Get and Set code in the buttons:
    Code:
    Sub btn_Clicked(Target As String)
       
    Dim value As Double = myRating.GetCurrentRating(page)
       
    Log(value)
    End Sub

    Sub btn2_Clicked(Target As String)
       myRating.SetCurrentRating(
    page2.5)   
    End Sub
    That is all! :)

    Changes for versions of ABMaterial before 3.75

    1.
    Save the CSS string into a text file (e.g. star.rating.css) and copy it to the /css/custom folder.
    2. Remove the CSS param from ABMComp.Initialize("ABMComp", Me, InternalPage, ID, CSS)
    Code:
    ABMComp.Initialize("ABMComp", Me, InternalPage, ID'<-- CSS param deleted
    3. In Sub ABMComp_Build(InternalPage As ABMPage, internalID As String) As String remove the InteranlPage param:
    Code:
    Sub ABMComp_Build(internalID As StringAs String
    4. In Page_Build(), load the css file you created:
    Code:
    page.AddExtraCSSFile("custom/star.rating.css")
    Alwaysbusy
     
    MichalK73, mindful, Erel and 3 others like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice