-
Notifications
You must be signed in to change notification settings - Fork 0
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
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.
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-disableFor more information about the application model and deltas see the Application overview document.
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]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.