Skip to content

Increment the Counter

Brenton Ashworth edited this page May 31, 2013 · 16 revisions

Increment the Counter

In this step, we will add the ability to increment the counter by generating an event in the user interface. This will not require many changes to our code but introduce the following concepts:

  • Emitters
  • the Default Emitter
  • the application model
  • application model deltas
  • transform-enable and transform-disable

Starting the dev tools

Before we make any changes to the code, start up the development application:

lein repl
(use 'dev)
(run)

and navigate to the Data Renderer.

http://localhost:3000/tutorial-client-data-ui.html

All of the changes that we will make will be made to the tutorial-client.behavior namespace. Before we begin, require the io.pedestal.app namespace.

[io.pedestal.app :as app]

We are now ready to begin.

Observing change

In the last section we touched briefly on the idea that we need some way to observe changes to the data model. We did not have to explicitly set this up but were able to rely on the defaults.

In Pedestal Emitters are used to report change. We can always right our own emitter but Pedestal comes with a powerful one build in. The Default Emitter is added for us when we do not add one ourselves.

A dataflow description can include an :emit key which configures a sequence of emitters. The default emitter which was added to our dataflow can be manually configured like this:

(def example-app
  {:version 2
   :transform [[:inc [:my-counter] inc-transform]]
   :emit [[#{[:*]} (app/default-emitter [])]]})

Emitters are configured with a vector, the same as transform functions. Each emitter vector has a set of inputs and an emitter function.

[#{[:*]} (app/default-emitter [])]

The inputs set for the vector above is #{[:*]} and the emitter function is the default emitter. The vector passed to default-emitter is a prefix which will be added to all emitted deltas. Let's make a slight modification to this and add a prefix.

(def example-app
  {:version 2
   :transform [[:inc [:my-counter] inc-transform]]
   :emit [[#{[:*]} (app/default-emitter [:tutorial])]]})

If we save this file and refresh the page we can see that :my-counter now exists under :tutorial. We can see the emitted deltas by looking at the console.

[:node-create [] :map]
[:node-create [:tutorial] :map]
[:node-create [:tutorial :my-counter] :map]
[:value [:tutorial :my-counter] nil 1]

When the page is loaded, our emitter emits the above deltas. These deltas describe the changes which have be made to our data model and which should be made to the user interface. These changes build up the nodes of our application tree and then set the value at [:tutorial :my-counter] to 1.

Each delta has the same general format

[:op [:path :to :node] args]

The op is one of:

:node-create
:node-destroy
:value
:attr
:transform-enable
:transform-disable

For more information about the application model and deltas see the Application overview document.

Attaching transforms to nodes

What we would like to do is let the renderer know that some user action related to the :my-counter node can send a message which will cause the counter to increment. We know the message that we need to send.

{msg/type :inc msg/topic [:my-counter]}

We use a :transform-enable delta to describe this.

[:transform-enable [:tutorial :my-counter]
 :increment-counter [{msg/type :inc msg/topic [:my-counter]}]]

Here the op is :transform-enable. The path is [:tutorial :my-counter]. The arguments to :transform-enable are the transform name and a vector of messages. When this transform is triggered, all of these messages will be sent.

There is a shorter way to write this.

[:transform-enable [:tutorial :my-counter] :inc [{msg/topic [:my-counter]}]]

Each message which does not have a msg/type will be assigned a type that the same as the transform name.

We need to arrange for our emitter to send this :transform-enable delta. There are many ways that this can be done. Since we only need to send this once when the application is initialized we will create an initialization emitter to do the job.

First we create an emitter function which will send this delta.

(defn init-emitter [_]
  [[:transform-enable [:tutorial :my-counter] :inc [{msg/topic [:my-counter]}]]])

Emitter functions receive one argument, which we are ignoring here, and return a sequence of deltas.

We would like for this emitter function to be called only during initialization. To do this we will use the map form for configuring an emitter and place this emitter function under the :init key.

(def example-app
  {:version 2
   :transform [[:inc [:my-counter] inc-transform]]
   :emit [{:in #{[:*]} :fn (app/default-emitter :tutorial) :init init-emitter}]})

Emitters placed under the :init key are called only when Focus changes. Focus will be covered later in this tutorial.

With these few changes, the Data Renderer will now show a button which we can click to cause the counter to increment.

If you look in the console as you click this button you will see the deltas being rendered with each click.

[:value [:tutorial :my-counter] 1 2]

Next steps

We plan to make this application collaborative by allowing any number of people to connect to our service and everyone will be able to see everyone else's counter. This means that we will need to be able to send our counter value off to the server and receive counter values from others. In the next section, we will update the data model so that we have a place to store these counters and we will add a simulator which will allow us to act as if we were sending and receiving counters even before we have created the service to handle this.

The tag for this step is step3.

Home

Clone this wiki locally