Skip to content

Rendering the Login Screen

Ryan Neufeld edited this page Jul 9, 2013 · 5 revisions

To follow along with this section, start with tag v2.1.4.

In this section you will render the new login screen. While doing this work you will learn how to use static templates.

Slicing the login template

In the namespace tutorial-client.html_templates add the code to extract the login template as a static template.

(tfn (tnodes "login.html" "login"))

The new version of tutorial-client-templates is shown below.

(defmacro tutorial-client-templates
  []
  {:tutorial-client-page (dtfn (tnodes "game.html" "tutorial") #{:id})
   :login-page (tfn (tnodes "login.html" "login"))})

Instead of using a dynamic template for login, a static template is used. Static templates are filled in once when added to the DOM and cannot be updated. That is exactly what is required for the login page.

After making this change to tutorial-client.html_templates you will need to restart the server or reload this file.

Recording a login

Before updating the renderer to work with the login screen, make a new recording which includes an interaction with the login screen. The recording should include the display of the login screen as well as the transition from this screen to the game. You can then play through this recording while updating the renderer.

To make the recording, go to the Data UI and press Alt-shift-R. After you see the indicator that the recording is in progress, enter a name in the text field and click the Login button. Once the game screen appears, stop the recording and save it as "Login".

Updating the renderer

In the tutorial-client.rendering namespace, configure a handler for the [:login] path.

[:node-create  [:login] add-login-template]
[:node-destroy [:login] h/default-destroy]

As before, create a function to render a template.

(defn add-login-template [renderer [_ path :as delta] input-queue]
  (let [parent (render/get-parent-id renderer path)
        id (render/new-id! renderer path)
        html (:login-page templates)]
    (dom/append! (dom/by-id parent) (html {:id id}))))

This function is similar to the previous template rendering function. The main difference is that it is rendering a static template. The template is retrieved with

(:login-page templates)

and added to the DOM with

(dom/append! (dom/by-id parent) (html {:id id}))

This will render the form on the page. You can confirm this by playing through the recording. The only other thing you need to render are the :transform-enable and :transform-disable deltas.

[:transform-enable [:login :name] add-submit-login-handler]
[:transform-disable [:login :name] remove-submit-login-event]

This is done with the following two functions.

(defn add-submit-login-handler [_ [_ path transform-name messages] input-queue]
  (events/collect-and-send :click "login-button" input-queue transform-name messages
                           {"login-name" :value}))

(defn remove-submit-login-event [_ _ _]
  (events/remove-click-event "login-button"))

The add-submit-login-handler function uses collect-and-send to capture values at specific ids and use these values to populate messages before sending them on the input queue. The call to collect-and-send above is setting up a click listener on the element with id "login-button" which, when clicked, will get the value of the element with id "login-name" and fill in any missing :value message parameters before sending the message on the input-queue

remove-click-event will simply remove the click event from the element with id "login-button".

With these changes in place, the application will now work with the login screen.

A bug has been introduced

According the current rules of the game, a bubble should be removed each time a remote player scores. The code which removes bubbles assumes that the local player name is "Me". This is no longer the case. It will now remove a bubble any time there is a score. On top of this, the bubble removal code is located in the renderer. Instead of trying to fix this problem in the renderer, the logic for controlling the removal of bubbles should be moved to the application logic.

In the tutorial-client.behavior namespace, add a remove-bubble function.

(defn remove-bubbles [rb other-counters]
  (assoc rb :total (apply + other-counters)))

As the derive configuration below shows, this function will receive the value of all the remote counters.

[#{[:other-counters :*]} [:remove-bubbles] remove-bubbles :vals]

From this collection of numbers it calculates a sum. Any time that sum changes a bubble should be removed. Update the :add-bubbles emitter to also handle :remove-bubbles.

[#{[:add-bubbles]
   [:remove-bubbles]} (app/default-emitter [:main])]

Try this in the Data UI to confirm that the correct value is being put in the data model under [:remove-bubbles].

Next, update the renderer to get rid of the logic around removing bubbles.

Remove the call to removeBubble from set-score.

(defn set-score [renderer [_ path _ v] _]
  (.setScore (game renderer) (last path) v))

Add a remove-bubbles handler function.

(defn remove-bubbles [renderer _ _]
  (.removeBubble (game renderer)))

Finally, route value changes to [:main :remove-bubbles] to remove-bubbles in render-config

[:value [:main :remove-bubbles] remove-bubbles]

Bubbles should now be removed correctly. Disaster averted.

Next steps

It is kind of weird how the game just starts when you login. It's kind of like walking into a conversation which has already started. In the next section you will create another screen where players can wait before a game starts. You will also provide the ability to explicitly start a game.

The tag for this section is v2.1.5.

Home | Start a Game

Clone this wiki locally