--- tags: mstu5013, tutorial, js, riot --- # RIOT Tutorial 03: EVENTS ## How to handle events using RIOT? As you may remember, the **event listeners** will listen for specific user's and browser's actions (e.g. onclick, onkeydown, onload) that will trigger the **event handler**, certain functions associated to them. Riot uses all standard HTML listener attributes (e.g. onclick, onchange, onkeypress, etc.) The beauty of RiotJS is that it retains much of the default syntactical form that is present in standard HTML and JS. E.g. HTML + Native JS ```htmlmixed= <!-- Event Listener `onclick` --> <button onclick="saveInfo()">SAVE</button> ``` ```javascript= // Event Handler var saveInfo = function(event){ alert("Info Saved!"); }; ``` The RIOT way of adding event handlers to your `HTML` elements and attach their corresponding functions would be: **Explicit Version** ```jsx= <app> <button onclick={ saveInfo }>SAVE</button> // `this.saveInfo` assumed <script> this.saveInfo = function(event){ alert("Info Saved!"); } </script> </app> ``` - **Line 2:** Notice that with RiotJS, we do not _call_ `saveInfo` in the listener. - See HTML `onclick="saveInfo()"` vs. RiotJS `onclick={ saveInfo }` - Riot automatically `calls()` event handlers (e.g. `saveInfo`) when the event is triggered. - **Line 5:** Like other variables, function references are declared in the context of the component/tag using the keyword `this`. - In the template portion, the keyword `this` is assumed. - By declaring functions in the scope of `this` component, we ensure that the function can only be called inside this component. - In otherwords, a `this.saveInfo` function in component A will not trigger `this.saveInfo` function in component B. - Conversely a `saveInfo` function declared in the global space will be accessible everywhere, causing issues such as calling the wrong one or overwriting the original function. - E.g. Two different save functions can exist - one to save an image declared in image component, another for save a blog declared in editor component. **ES6 Syntax (Short) Version** There is a shorter way to declare RiotJS event handlers. This form uses a newer ES6 Syntax. It's not any better or worse, but a matter of preference. It's recommended you stick to one style consistently. Riot will convert ES6 Syntax to Explicit function declaration for you. ```jsx= <app> <button onclick={ saveInfo }>SAVE</button> <script> saveInfo(event) { alert("Info Saved!"); } </script> </app> ``` - **Line 5:** Keyword `function` is omitted in ES6. - **Line 5:** Keyword `this` is omitted but assumed when using ES6 format. ES6 (Shorthand) ```javascript saveInfo(event) { alert("Info Saved!"); } ``` Riot Converts ES6 (above) to Explicit Declaration (below) during compilation ```javascript this.saveInfo = function(event) { alert("Info Saved!"); }; ``` ## How to access the user's input value? Often times, when we use event listeners and handlers, we're interested in getting user data from a particular element (e.g. input, select, textarea, etc.) We traverse the DOM to select those elements of interest. E.g. Regular HTML/JS ```htmlmixed= <input id="username" onchange="setUsername()" /> ``` ```javascript= var username = ""; var setUsername = function(event) { var inputEl = document.querySelector('#username'); // traverse DOM and select input username = inputEl.value; // get input element value and set it to username } ``` With RiotJS, we don't use `document.querySelector`, `document.getElementById`, etc. to search through the HTML in our component. In the Riot way, we can explicitly add references via the HTML `ref` attribute. E.g. Using the `ref` attribute and object ```jsx= <user-profile> // Notice the `ref` attribute set to `myUsername` <input type="text" ref="myUsername" onchange={ setUsername }> <script> this.username = ""; setUsername(e) { this.username = this.refs.myUsername.value; console.dir(this); } </script> </user-profile> ``` Riot components (e.g. `<user-profile>`) have a special property called `refs`. Any `ref="..."` attribute that you set on an element will store a reference to the element inside the `refs` property of the component. E.g. Conceptual Model of component object and `refs` ```jsx // Set reference to <input> element via `ref="myUsername"` // Component saves a reference to the <input> under the `refs` property <user-profile component> = { username: "", setUsername: function(){...}, // All refs will be stored here. refs: { myUsername: <input element> // Points to the input element } } ``` E.g. `console.dir(this)` ... `this` is the component/tag. ![](https://i.imgur.com/bo7K7OS.png) 1. So in the above example, `this` refers to the component/tag `<user-profile>`. 2. `this.refs` is a property of the component where all elements with `ref="..."` references are stored as an object. 3. `this.refs.myUsername` points to the `input` element where `ref="myUsername"` E.g. More complete example: ```jsx= <user-profile> <form> <label>First Name</label> <input type="text" ref="firstName"> <label>Last Name</label> <input type="text" ref="lastName"> <button onclick={ saveInfo }>SAVE</button> </form> <script> saveInfo(e){ var user = { first: this.refs.firstName.value, last: this.refs.lastName.value }; } </script> </user-profile> ``` - When the `button` is clicked the `saveInfo` function is called. - The first input element reference is stored via attribute `ref="firstName"` - The second input element reference is stored via attribute `ref="lastName"` - The `<user-profile>` component has a property called `refs` where all element references are stored. - Therefore we can access each referenced element through the component `refs` property. - `this.refs.firstName` is the first input element. - `this.refs.lastName` is the second input element. ```jsx // CONCEPTUAL MODEL <user-profile component> = { refs: { firstName: <input element>, // ref="firstName" lastName: <input element> // ref="lastName" } } ``` - **Line 14:** We're going to store the user data as an object. `user = {};` - We give `user` object 2 properties: `first` and `last`. - We set each via the `value` property of the respective element ```jsx var user = { first: this.refs.firstName.value, // i.e. <input>.value last: this.refs.lastName.value } ``` ### Using `event.target` or `event.currentTarget` Recall from regular HTML/JS, you can use the event object and special properties `target` or `currentTarget` to reference elements of interest. `event.target` references the element that triggered the event. `event.currentTarget` references the element on which the event handler was attached. E.g. HTML/JS ```jsx= // Click on the "SAVE" word <button onclick="save()"> <strong>SAVE</strong> </button> ``` ```javascript= var save = function(event) { console.log(event.target); // <strong> = element that dispatched event console.log(event.currentTarget); // <button> = element with listener } ``` In traditional HTML/JS, we often like doing this because it makes it easier to traverse the DOM in a relational way. For example, we might use the `event.target` as a relative starting point to find the `event.target.nextElementSibling.innerHTML` and manipulate that directly. In RIOT, utilizing the `event` object that is passed into the handler function as an argument is a completely legitimate way of referencing elements of interest. However, with RIOT we tend not to do this. `ref="..."` provides a direct way to point to specific elements of interest. And as you'll see next, in Riot we try to **avoid direct DOM manipulation** all together (e.g. `element.innerHTML = ...`, `element.textContent = ...`, etc.) ## DO update STATE... DON'T manipulate the DOM directly. Traditionally in order to get user generated data to appear as _content_ on the page, we directly manipulate DOM elements by writing to `textContent` or `innerHTML`. E.g. Direct DOM Manipulation (the old HTML/JS way) ```htmlmixed= <h1 id="title">Untitled</h1> <div id="author">Anonymous</div> <input id="blogTitle" /> <input id="blogAuthor" /> <button onclick="setTitle()">SET TITLE</button> ``` ```javascript= var setTitle = function(event) { var userTitle = document.querySelector('#blogTitle').value; var userAuthor = document.querySelector('#blogAuthor').value; // Manipulate the DOM, <h1> and <div> document.querySelector('#title').textContent = userTitle; document.querySelector('#author').textContent = userAuthor; } ``` 1. User inputs a blog title and author name. 2. User clicks the button. 3. Handler function traverses, selects DOM elements, gets values. 4. DOM is manipulated (e.g. `.textContent`), directly writing user data values into the HTML. Up until now this worked fine. The state of this post (updated title and author) exists in the DOM itself. But this also creates some complication. What if now, I wanted to _save_ this data to the database? What is the title and author of this blog? Well, we could grab this data from the DOM itself and create a `blog object`... ```javascript= var blog = { title: document.querySelector('#title').textContent, author: document.querySelector('#author').textContent } ``` But what if we update the title and author again? Now the `blog object` is out of sync (still contains old blog data.) We'd have to update this blog data object or suffer from stale data. When you start manipulating the view directly, you have to then synchronize and manage data in multiple locations. Now imagine if you have to manage and synchronize your data with another source, such as a database. Directly managing multiple sources of data can lead to _misses and errors_. **There is a better way.** In RIOT (and most component based frameworks,) we think of any data that is used in the context of a component, as the **state** of the component. There are some principles to this. **Principles of Data in component based thinking:** 1. There should only be a SINGLE source of data. 2. Any state change should be reflected by manipulating that single source of data only. 3. All view changes should react to changes in that single source of data. - In other words, all changes trickle down from the source data. Let's look at a RIOT example where we combine what we learned so far with these concepts of single source of data. E.g. Setting STATE the RIOT way ```jsx <blog-editor> <h1>{ title }</h1> <div>{ author }</div> <input ref="blogTitle" /> <input ref="blogAuthor" /> <button onclick={ save }>SAVE</button> <script> // SINGLE SOURCE OF DATA (the TRUTH) this.title = "Untitled"; this.author = "Anonymous"; // Handler only updates the component states (above) save(event) { this.title = this.refs.blogTitle.value; // refers to data above this.author = this.refs.blogAuthor.value; // refers to data above } // RIOT automatically updates the component right after handler is called, // refreshing the view, and interpolating new data into template. </script> </blog-editor> ``` In the above example: - The initial state of the component's properties, `title` and `author` are set to `Untitled` and `Anonymous`. - Riot interpolates those values into the `h1` and `div` (initial view state.) - User types a blog title and author, presses the save button. - Handler sets/updates the state of the component (`title` and `author` properties of the component) to new values that the user just typed. - RIOT automatically interpolates updated component data into view. - Hence, view is now in sync with our data. There is a one-way directional flow. If we want to know the current state of the `blog`, we only need to look to `this.title` and `this.author`. Since we work in a singular direction, a downward flow where all changes are from the source data, it's easy to keep track and in sync. This is particularly important because in more complex applications, _multiple components_ might require updating from a singular source data. For example, you might imagine Twitter, where the navbar needs to know your `username` and your content (i.e. tweets) also need to know your `username`. If both components react to a single source of data (e.g.`user`) then we can refresh all components based on the original source data. If our `username` changes, no problem. Only a single source of data needs to change and the rest of the components react to it. On the other hand, if we have multiple components pointing to different data, and we change our `username` in one, but not the other - now we have two components that are not in sync. Which is correct? Again, life is complicated if we don't manage this well. ### EXTRA: RIOT event triggers, event handling, and `update()` In the above examples, we went over the basic flow of how data and views are kept in sync. At a more detailed level, here is what is happening. - In RIOT, any time an event handler is called, a special internal Riot function called `update()` is automatically called right after. The `update()` function is what tells the component it should _refresh_ the view, by interpolating any new state changes that have been made since the last time `update()` was run. Utilizing this feature of Riot, and others like interpolation, references, etc. We can significantly decrease the amount of code necessary to produce and change data-driven views. Using Riot increases the performance of your code, as element references are internally cached, reducing the need to _search_ through or _traverse_ the DOM. It isn't wrong to use our "old-ways" of directly traversing and manipulating the DOM, but if you ever find yourself doing this - you're doing it the hard, error-prone way. Riot makes our lives easier. And code more fun. See `this.update` on [RiotJS Documentation](http://riotjs.com/api/#updating). :::info **AUTHORS** By Anabel Bugallo; Edited by Jin Kuwata :::