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. One can ‘hover’ over the stars to set its value or it can be set and retrieved by code.
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.
So let’s get started!
1. We create a new standard class CompStarRating:
'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 String) As String Return $" <div id="half-stars-example"> <div id="${internalID}" class="rating-group"><input id="rating-0" class="rating__input rating__input--none" checked="checked" name="${myInternalName}" type="radio" value="0" /> <label class="rating__label" title="0 stars" for="rating-0"> </label> <input id="rating-00" class="rating__input" type="radio" value="-1" /> <label class="rating__label rating__label--half" title="0.5 stars" for="rating-05"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label> <input id="rating-05" class="rating__input" name="${myInternalName}" type="radio" value="0.5" /> <label class="rating__label" title="1 star" for="rating-10"><i class="rating__icon rating__icon--star fa fa-star"></i></label> <input id="rating-10" class="rating__input" name="${myInternalName}" type="radio" value="1" /> <label class="rating__label rating__label--half" title="1.5 stars" for="rating-15"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label> <input id="rating-15" class="rating__input" name="${myInternalName}" type="radio" value="1.5" /> <label class="rating__label" title="2 stars" for="rating-20"><i class="rating__icon rating__icon--star fa fa-star"></i></label> <input id="rating-20" class="rating__input" name="${myInternalName}" type="radio" value="2" /> <label class="rating__label rating__label--half" title="2.5 stars" for="rating-25"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label> <input id="rating-25" class="rating__input" name="${myInternalName}" type="radio" value="2.5" /> <label class="rating__label" title="3 stars" for="rating-30"><i class="rating__icon rating__icon--star fa fa-star"></i></label> <input id="rating-30" class="rating__input" name="${myInternalName}" type="radio" value="3" /> <label class="rating__label rating__label--half" title="3.5 stars" for="rating-35"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label> <input id="rating-35" class="rating__input" name="${myInternalName}" type="radio" value="3.5" /> <label class="rating__label" title="4 stars" for="rating-40"><i class="rating__icon rating__icon--star fa fa-star"></i></label> <input id="rating-40" class="rating__input" name="${myInternalName}" type="radio" value="4" /> <label class="rating__label rating__label--half" title="4.5 stars" for="rating-45"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label> <input id="rating-45" class="rating__input" name="${myInternalName}" type="radio" value="4.5" /> <label class="rating__label" title="5 stars" for="rating-50"><i class="rating__icon rating__icon--star fa fa-star"></i></label> <input id="rating-50" class="rating__input" name="${myInternalName}" type="radio" value="5" /></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 version 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:
Dim myRating As CompStarRating
3. In Page_Connect() we add our component and a couple of buttons:
... 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:
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(page, 2.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)
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:
Sub ABMComp_Build(internalID As String) As String
4. In Page_Build(), load the css file you created:
page.AddExtraCSSFile("custom/star.rating.css")
Alwaysbusy