The live clock cog

When we called the Start method on the time ago cog, the time was rendered on the web page using the virtual DOM, instead of a replace inner HTML operation taking place. Since the time ago cog, only updates the time once, upon calling the Start method of the cog, it's hard to appreciate the cog's virtual DOM in action.

In this example, we're going to build a live clock Cog, which has the ability to display the current time of any place in the world. Since we'll be displaying the time to the seconds, we'll be performing a SetProp operation every second to re-render the live clock Cog.

Figure 9.7 is an illustration of the live clock:

Figure 9.7: An illustration depicting the live clock cog

We'll be rendering the current time for four places: where you are presently located, Chennai, Singapore, and Hawaii. Inside the shared/templates/index_content.tmpl template source file, we declare four div containers that serve as the mounting points for the four live clock cogs we'll be instantiating:

 <div data-component="cog" id="myLiveClock" class="liveclockTime"></div>
<div data-component="cog" id="chennaiLiveClock" class="liveclockTime"></div>
<div data-component="cog" id="singaporeLiveClock" class="liveclockTime"></div>
<div data-component="cog" id="hawaiiLiveClock" class="liveclockTime"></div>

Notice again that we have defined the mount points for the live clocks by declaring div containers containing the attribute, "data-component", and having its value set to "cog". We assign unique IDs to all four cog containers. The class name, liveclockTime, that we have declared in the div container is for styling purposes.

Now that we've set up the mounting points for the four live clock cogs, let's take a look at how to implement the live clock cog.

The implementation for the live clock Cog can be found in the liveclock.go source file in the shared/cogs/liveclock folder.

We declare the name liveclock for the cog's package name:

package liveclock

Notice, that in our import grouping we have included the github.com/uxtoolkit/cog package:

import (
"errors"
"reflect"
"time"
"github.com/uxtoolkit/cog"
)

We define the cogType unexported package variable:

var cogType reflect.Type

Inside the init function, we assign the cogType variable with the value returned by calling the reflect.TypeOf function on a newly created LiveClock instance:

func init() {
cogType = reflect.TypeOf(LiveClock{})
}

This is a required step to implement a cog.

At this point, we've established that declaring and initializing the cogType of a cog are part of the baseline requirements that we must perform to implement a cog.

Here's what the struct for the LiveClock cog looks like:

type LiveClock struct {
cog.UXCog
ticker *time.Ticker
}

We embed the cog.UXCog type in the cog's struct definition. We introduce a ticker field which is a pointer to a time.Ticker. We'll be using this ticker to tick at every second for the live clock.

Here's the LiveClock cog's constructor function:

func NewLiveClock() *LiveClock {
liveClock := &LiveClock{}
liveClock.SetCogType(cogType)
liveClock.SetCleanupFunc(liveClock.Cleanup)
return liveClock
}

The NewLiveClock function serves as the constructor function for the live clock cog. We declare and initialize the liveClock variable to a new LiveClock instance. We call the SetCogType method of the liveClock object and pass the cogType. Recall that this is a required step (shown in bold) that must be present in a cog's constructor function.

We then call the SetCleanupFunc method of the liveClock object, and provide it a clean up function, liveClock.Cleanup. The SetCleanUp method is included in the cog.UXCog type. It allows us to specify a clean up function that should be called prior to the cog being removed from the DOM. Finally, we return the new instance of the LiveClock cog.

Let's examine the Cleanup function:

func (lc *LiveClock) Cleanup() {
lc.ticker.Stop()
}

This function is really simple. We simply call the Stop method on the cog's ticker object to stop the ticker.

Here's the cog's Start method where the ticker will be started:

func (lc *LiveClock) Start() error {

We start out by declaring the time layout constant, layout, and setting it to the RFC1123Z time format. We declare a location variable, a pointer to a time.Location type:

  const layout = time.RFC1123
var location *time.Location

Prior to starting up a LiveClock cog, the user of the cog must set two important props, the "timezoneName", and the "timezoneOffset":

  if lc.Props["timezoneName"] != nil && lc.Props["timezoneOffset"] != nil {
location = time.FixedZone(lc.Props["timezoneName"].(string), lc.Props["timezoneOffset"].(int))
} else {
return errors.New("The timezoneName and timezoneOffset props need to be set!")
}

These values are used to initialize the location variable. If either of these props were not provided, an error will be returned.

If both of the props are present, we proceed to assigning the ticker property of the live clock cog to a newly created time.Ticker instance, which will tick at every second:

lc.ticker = time.NewTicker(time.Millisecond * 1000)

We range on the ticker's channel to iterate at every one second, as a value arrives, and we set the currentTime prop, providing it a formatted time value (shown in bold):

  go func() {
for t := range lc.ticker.C {
lc.SetProp("currentTime", t.In(location).Format(layout))
}
}()

Note that we used both the location, and the time layout to format the time. Once the cog has been rendered, calls to SetProp that will occur every second will automatically call the Render method to re-render the cog.

We make a call to the cog's Render method to render the cog to the web page:

  err := lc.Render()
if err != nil {
return err
}

In the last line of the method, we return a nil value to indicate that no errors occurred:

 return nil

We've defined the template for the cog in the liveclock.tmpl source file:

<p>{{.timeLabel}}: {{.currentTime}}</p>

We print out the time label, along with the current time. The timeLabel prop is used to supply the time label to the cog and will be the name of the place for which we want to know the current time of.

Now that we've seen what goes into making the live clock cog, and how it displays the time, let's go ahead and sprinkle some live clock cogs on the home page.

Here's the section of code inside the InitializeIndexPage function of the index.go source file, where we instantiate the live clock cog for the local time zone:

  // Localtime Live Clock Cog
localZonename, localOffset := time.Now().In(time.Local).Zone()
lc := liveclock.NewLiveClock()
lc.CogInit(env.TemplateSet)
lc.SetID("myLiveClock")
lc.SetProp("timeLabel", "Local Time")
lc.SetProp("timezoneName", localZonename)
lc.SetProp("timezoneOffset", localOffset)
err = lc.Start()
if err != nil {
println("Encountered the following error when attempting to start the local liveclock cog: ", err)
}

In order to instantiate the cog for the local time, we first obtain the local zone name and the local time zone offset. We then create a new instance of a LiveClock cog called lc. We call the CogInit method to initialize the cog. We call the SetID method to register the id of the cog's mount point, the div container where the cog will render its output to. We make calls to the SetProp method to set the "timeLabel", "timezoneName", and "timezoneOffset" props. Finally, we call the Start method to start up the LiveClock cog. As usual, we check to see if the cog started up properly, and if it didn't, we print the error object in the web console.

In a similar manner, we instantiate the LiveClock cogs for Chennai, Singapore, and Hawaii, in much the same way as we did for the local time, except for one thing. For the other places, we explicitly provide the timezone name and the GMT timezone offset for each place:

  // Chennai Live Clock Cog
chennai := liveclock.NewLiveClock()
chennai.CogInit(env.TemplateSet)
chennai.SetID("chennaiLiveClock")
chennai.SetProp("timeLabel", "Chennai")
chennai.SetProp("timezoneName", "IST")
chennai.SetProp("timezoneOffset", int(+5.5*3600))
err = chennai.Start()
if err != nil {
println("Encountered the following error when attempting to start the chennai liveclock cog: ", err)
}

// Singapore Live Clock Cog
singapore := liveclock.NewLiveClock()
singapore.CogInit(env.TemplateSet)
singapore.SetID("singaporeLiveClock")
singapore.SetProp("timeLabel", "Singapore")
singapore.SetProp("timezoneName", "SST")
singapore.SetProp("timezoneOffset", int(+8.0*3600))
err = singapore.Start()
if err != nil {
println("Encountered the following error when attempting to start the singapore liveclock cog: ", err)
}

// Hawaii Live Clock Cog
hawaii := liveclock.NewLiveClock()
hawaii.CogInit(env.TemplateSet)
hawaii.SetID("hawaiiLiveClock")
hawaii.SetProp("timeLabel", "Hawaii")
hawaii.SetProp("timezoneName", "HDT")
hawaii.SetProp("timezoneOffset", int(-10.0*3600))
err = hawaii.Start()
if err != nil {
println("Encountered the following error when attempting to start the hawaii liveclock cog: ", err)
}

Now, we will be able to see the live clock cogs in action. Figure 9.8 is a screenshot of the live clogs displayed on the homepage.

Figure 9.8: The live clock cog in action

With every passing second, each live clock gets updated with the new time value. The virtual DOM kicks in and renders only the difference of what changed, efficiently re-rendering the live clock at every second.

The first two cogs that we have implemented so far, have been pure cogs that are entirely implemented in Go. What if we wanted to leverage an existing JavaScript solution to provide a specific feature? That would be a situation that calls for implementing a hybrid cog, a cog that is implemented in Go and JavaScript.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.247.53