--- tags: mstu5013, tutorial, js, riot --- # RIOT Tutorial 04: CONTROL FLOW ## How conditionals work with RIOT? RIOT provide us with simple way to apply conditional logic via special attributes inside of tags. That would be the case of `if={}`, `show={}` & `hide={}`. These conditional attributes allow us display different elements and components, based on the state of the tag. Example: ```jsx <my-tag> // This component will be mounted and displayed if there is no user. <account-signup if={ !user }></account-signup> // This component will display if there is a user. <profile-editor show={ user }></profile-editor> // This button is hidden if the user is subscribed <button hide={ user.subscribed }>SUBSCRIBE NOW</button> <script> this.user = { username: "alpha", subscribed: true }; </script> <my-tag> ``` RIOT reacts to changes in tag state by hiding or showing elements based on the pre-set condition. For example, if I update `user.subscribed` to `false`, and update the tag, now the _SUBSCRIBE NOW_ button will be visible. ### Refresher: JS Types and Evaluation :::info As you may remember, in JS there are different kind of primitive data: 1. ***Boolean***, which evaluates to `true` or `false`. 2. ***Null***, which only value is `null` and therefore evaluates to `false`. 3. ***Undefined***, which is a result of a non defined variable or expression in our case. Whenever an expression is undefined it will evaluate to `false`. 4. ***Number***, which can take the value of `+infinity`, `-infinity` and `NaN`. `NaN` will also evaluate to `false`. 5. ***String***, which is used to represent textual data, and is defined inside double quote ("..."). An empty string will be evaluated by RIOT as `false`. ::: Using RIOT conditional attributes is a convenient way to show/hide, add/remove elements and components, based on how a particular state is evaluated. Let's see each conditional attribute in detail: ### `if` attribute To understand how the `if` attribute works in RIOT, I would like you to think of the *log in* & *log out* states. When a user is *logged in*, we are giving him access to information that is not available/visible in the *log out* state. Further, these are opposed states since you can't be both, logged in and logged out. Let's take Facebook as example. When you are logged out, there's not much to see, just a few inputs to get you logged in or signed up, and a `navbar` at the bottom. However, when you log in, you can see your information (e.g. name, pictures, general info), your contacts and posts from your friends. We can accomplish this using `if={}` attributes. They allow us to easily add (render) or remove elements from the DOM, and therefore, from the user. When a RIOT expression evaluates to `false` or is set to `false`, the tag containing the `if` statement will not be rendered. That's a main difference between `if={}` and `show={}` and `hide={}` statements. - `if` actually adds or removes elements from the DOM. - `show` and `hide` only change the style `display` on or off. Therefore, when using an `if={}` argument, we will be basically toggling between boolean values: - `true` will result in a rendered (added to DOM) and visible element. - `false` will result in a non-rendered (removed from DOM) element. The expression inside the `if={...}` statement is what is going to be evaluated. In its simpliest form, an `if` statement can be used as follow: ```jsx= <user-profile> //LOGGED OUT STATE <div if= { !userInfo }> <label>User Name:</label> <input type="text" ref="userName" placeholder="Enter your name"/> <label>Password:</label> <input type="password" ref="userPass" placeholder="Enter your password"/> <button onclick={ userLogin }>LOG IN</button> </div> //LOGGED IN STATE <div if={userInfo}> <h1>Hello {userInfo.userName}. Welcome back!</h1> <button onclick={ userLogout }>LOG OUT</button> </div> <script> this.userInfo = undefined; // Evaluates to false this.userLogin = function(e){ this.userInfo = { userName: this.refs.userName.value, password: this.refs.userName.value } } this.userLogout = function(e){ this.userInfo = false; } </script> </user-profile> ``` - ***Line 3:*** The conditional in this case depends on if `userInfo` was defined or not. Because `userInfo` was set to `undefined` (look at line 18), which evaluates to `false`, lines 3 to 9 will be rendered whereas lines 12 to 15 will not. - ***Line 12:*** Once the user inputs his `userName` and `password`, and hits the `LOG IN` button, the state of `userInfo` will change from `false` to `true`, which will cause lines 12 to 15 to be rendered, whereas lines 3 to 9 will not. ### `show` & `hide` conditional attributes `show = {}` & `hide = {}`, as the name indicate, will show or hide an element from the DOM. Therefore they are like 2 sides of the same coin: they're opposites. If the expression inside`hide = {...}` is evaluated to `true`, RIOT will apply `style= "display: none;"` to the tag containing such attribute. If on the contrary, `hide = {...}` is evaluated to `false`, `style= "display: '';"` will be applied and therefore the element will be visible to the user. If the expression inside `show = {...}` is evaluated to `true`, RIOT will apply `style= "display: '';"` to the tag containing such attribute. If on the contrary, `show = {...}` is evaluated to `false`, `style= "display: none;"` will be applied and therefore the element will be hidden from the user. :::danger The difference between `if ={}` ***and*** `show ={} & hide ={}` attributes is that, when we apply `show ={} & hide ={}`, the tag will always be rendered, and what will change is the `display` style in accordance with the boolean value. Nevertheless, when using the`if` attribute, whenever the expression is evaluated to `false`, the tag will be completly removed. If you're curious about this, give it a try and analyze the _Elements_ tool in _Inspector_. ::: ## How to use the Ternary Operator You may have run into an expression that looked something like this: Example: ```javascript var petType = "cat"; var petName = (petType == "cat") ? "Fluffy" : "Fido"; ``` This is a short-hand way of creating a conditional that returns a value based on whether that condition evaluates as `true` or `false`. In the above example: 1. `(petType == "cat")` evaluates to `true` 2. Therefore the value _on the left_ of the `:` is returned 3. And set to the variable `petName`, "Fluffy" Counter-Example: ```javascript var petType = "dog"; var petName = (petType == "cat") ? "Fluffy" : "Fido"; ``` 1. `(petType == "cat")` evaluates to `false` 2. Therefore the value _on the right_ of the `:` is returned 3. And set to the variable `petName`, "Fido" Ternary vs. If Statement ```javascript var petType = "cat"; var petName; // Ternary Operation petName = (petType == "cat") ? "Fluffy" : "Fido"; // If Statement if (petType == "cat") { petName = "Fluffy"; } else { petName = "Fido"; } ``` Ternary Operation Pattern ```javascript (Condition to be evaluated) ? "Value A" : "Value B" ``` - The **conditional ternary operator** is not RIOT specific, but native JS. It's a convenience form of an `if` statement. - If the ***condition*** is evaluated to `true`, then it will return the `"Value A"`; *else* it wil return `"Value B"`. - Let's take the Digital Pet exercise as example: ```jsx= <my-digital-pet> <h2>How happy is your pet?</h2> <button onclick={ increaseHappiness }>+</button> <button onclick={ decreaseHappiness }>-</button> <h3>{ petState }</h3> <h2>{ message }</h2> <script> this.petState = 5; this.increaseHappiness = function(){ this.petState ++; } this.decreaseHappiness = function(){ this.petState --; } this.on('update', function(){ this.message = (this.petState >= 6) ? "I'm a happy pet!" : "I'm a sad pet!" }); </script> </my-digital-pet> ``` ## How loops work with RIOT? RIOT has a built-in looping function that allows us to easily loop over different data structures like `arrays` & `array of objects`. We can accomplish that by using the RIOT `each = {}` attribute. Example (Conceptual): ```jsx // Tweet Component: <tweet> <p>{ tweetItem.author } says: { tweetItem.content }</p> ... </tweet> // Parent Component: <app> // Produce a component (tweet), for each item (tweetItem), in array (tweetList). <tweet each={ tweetItem in tweetList }></tweet> // Array of Tweet Objects this.tweetList = [{ author: "jmk2142", content: "I like pancakes." },{ author: "ab4464", content: "MSTU5013 is the best." },{ author: "abc1234", content: "RiotJS is neat." }]; </app> ``` Produces: ```jsx <app> <tweet> <p>jmk2142 says: I like pancakes.</p> </tweet> <tweet> <p>ab4464 says: MSTU5013 is the best.</p> </tweet> <tweet> <p>abc1234 says: RiotJS is neat.</p> </tweet> </app> ``` :::info ### Rule of Thumb 1. If you have an `Array` of data, 2. And you want to create a component for each `item` in the set of data, Use the `each={ item in array }` attribute and pattern. #### Notes `each={ ... }` can be used on any element, not just components. In example below, Riot will produce an `li` for each data in array passed in. It's important to understand here, that Riot will actually turn the `li` into a Riot tag instance. ```jsx <ul> <li each={ item in list }>{ item }</li> </ul> ``` ::: ### How Riot loops pass data into children: When we loop through an array of data using the Riot `each` attribute, we are passing individual items of the array, into child Riot tags. Let's break down the Twitter example data: ```javascript this.tweetList = [{ author: "jmk2142", content: "I like pancakes." },{ author: "ab4464", content: "MSTU5013 is the best." },{ author: "abc1234", content: "RiotJS is neat." }]; ``` - We have an array of data `tweetList`. - The array represents a list of data (individual tweet) objects (3). Conceptually, we want to loop through this list of tweets in `tweetList` and for each tweet object, create a Riot instance of the `<tweet>` component. That is, for each iteration through the loop, we want to pass 1 tweet object (data) into a Riot component instance. ```jsx // Tweet instance 1 // Pass in object: { author: "jmk2142", content: "I like pancakes." } <tweet> <p>jmk2142 says: I like pancakes.</p> </tweet> // Tweet instance 2 // Pass in object: { author: "ab4464", content: "MSTU5013 is the best." } <tweet> <p>ab4464 says: MSTU5013 is the best.</p> </tweet> // Tweet instance 3 // Pass in object: { author: "abc1234", content: "RiotJS is neat." } <tweet> <p>abc1234 says: RiotJS is neat.</p> </tweet> ``` Hence: ```jsx <tweet each={ tweetItem in tweetList }></tweet> ``` This will generate a `<tweet>` component instance for each data in the `tweetList`. If we look inside the `<tweet>` instance: We'll find that it has a property called `tweetItem` with the data of interest. Example of `<tweet>` tag instance: `console.dir(this)` ![](https://i.imgur.com/EOOCiPS.png) Each `<tweet>` component, a Riot instance of the `<tweet>` component, will have the property `tweetItem` set the the specific data object passed in from our `tweetList`. Data passed in from an array are attached to the child instance as properties. Since the specific data we want to interpolate into the component is nested inside this `tweetItem` property of the tag, we need to set up our template like this: ```jsx <tweet> <p>{ tweetItem.author } says: { tweetItem.content }</p> // `this` is assumed </tweet> ``` Let's look at another example: ```jsx <tweet each={ tweetList }></tweet> ``` - Similar to prior example. - Notice that we are _not_ using a `each={ item in list }` pattern. - Only the `list` is included. - This will work, but it has different implications. The above example will produce a `<tweet>` component for each item in the array `tweetList`. However, because there is no `item` variable assigned to refer to each object, the properties are directly attached to our child tag instance. Example of `<tweet>` tag instance: `console.dir(this)` ![](https://i.imgur.com/df0asqW.png) Our template in this case, would look like this: ```jsx <tweet> <p>{ author } says: { content }</p> // `this` is assumed </tweet> ``` How you articulate the `each` will determine how your data will be represented in the child tag instance(s) that Riot produces. It's a matter of preference, how you deal with lists of objects when iterating with Riot. If you are working with arrays of `Strings` or `Numbers` you should always use the `each={ item in list }` pattern. :::info **TIP:** Be keenly aware of how you are passing data into child Riot tag instances, and how that data is represented/referenced in the child instance. ::: ### More Examples: Because each data structure is different, for each data structure we will be using the `each= {}` attribute in a very specific way. Let's see some examples: ```jsx= <grocery-list> <h1>My grocery list</h1> // LOOPING OVER AN ARRAY <h2>Simple version:</h2> <ol> <li each={ item in foodList }>{ item }</li> </ol> // LOOPING OVER AN ARRAY OF OBJECTS <h2>Detailed version:</h2> <ol> <li each={ shoppingList }> { product } --> { quantity }</li> </ol> <script> // ARRAY OF STRINGS this.foodList = [ 'cucumber', 'tomatoes', 'oranges' ]; //ARRAY OF OBJECTS this.shoppingList = [ { product: 'cucumber', quantity: '1/2lb'}, { product: 'tomato', quantity: '1lb'}, { product: 'oranges', quantity: '3lb'} ]; </script> </grocery-list> ``` - **Line 7 & 13:** When `each` is used on a plain element, that element becomes a Riot tag instance. - I.e. it will have internal properties like our formally declared tags. - Data passed in is referenced internally like we have discussed prior. - **Line 7:** `foodList` is an *array of Strings*. For each `item` (word) in `foodList`, create a Riot tag instance of the `<li>` element. - Notice how, when looping over the *array* `foodList`, we are declaring a temporary variable `item` to store the value. - `item` represents a food (String) in each iteration through the `foodList`. - We could have named `item` anything. E.g. `each={ food in foodList }` - **Line 13:** `shoppingList` is an *array of objects*. We are not using the `item in list` pattern here and only reference the `list`. The properties of each data object are directly attached to the tag instance, `product` & `quantity`. - `product` will be a property of the `<li>` tag instance created by Riot - `this.product` - `quantity` will be a property of the `<li>` tag instance created by Riot - `this.quantity` - This is why we can interpolate via `{product}` and `{quantity}` as `this` (pointing to tag instance) is assumed. ## State and Classes: Riot Short-hand We saw in Bootstrap that adding and removing classes is an efficient way of changing styles to HTML elements. Example: ```htmlmixed <div class="">My Answer A</div> // GREY <div class="correct">My Answer B</div> // GREEN <div class="incorrect">My Answer C</div> // RED ``` We did this by directly manipulating DOM elements via Javascript: ```javacript var el = document.querySelector('.question'); el.className = "correct"; el.classList.remove('incorrect'); // etc. el.classList.add('correct'); ``` Remember that the RIOT way is to avoid any direct manipulation of the DOM. In RIOT: - We observe the STATE of the tag. - We update the template, dependent on the STATE. For content, we did this with `{ interpolation }`. To conditionally choose what to show or hide, we used conditional attributes like `if={ evaluation }` to evaluate what to render. To let Riot naturally update `classes` based on updated states, Riot provides a convenient _short-hand_ for this. Class Short-hand Pattern: ```jsx <element class={ classNameA:evaluation, classNameB:evaluation, ... }></element> ``` Riot will add or remove `className` based on the evaluation to `true` or `false`. In a simplified example below: ```jsx <div class={ correct:true, special:false, difficult:true }></div> ``` Becomes: ```htmlmixed <div class="correct difficult"></div> ``` This allows for us to only worry about directly maintaining the STATE of a tag. On the tag update, Riot will naturally look to the state and evaluate what/which classes to add or remove. Let's take the example from before. Imagine you're going shopping and you want to cross-off the `products` that you already have in your cart and highlight those that you still need to add. By changing the style of the products based on the `done` or `!done` property, you can easily keep track of what you still need to look for: ```jsx= <grocery-list> <h2>My weekly grocery shooping list:</h2> <ul> // Applying class based on STATE of `this.done` <li each={ shoppingList } class={ completed: done, notCompleted: !done }> { product } --> {quantity} </li> </ul> <script> // Notice each item has a `done` property this.shoppingList = [ { product: 'cucumber', quantity: '1/2lb', done: true}, { product: 'tomato', quantity: '1lb', done: false}, { product: 'oranges', quantity: '3lb', done: true} ]; </script> <style> .completed{ color: gray; text-decoration: line-through; font-style: italic; } .notCompleted { background-color: yellow; text-transform: uppercase; } </style> ``` - ***Line 15-17:*** As you may notice, we have a property `done` for each object, which is a boolean. - ***Line 7:*** In this case we are assigning the class `completed` when the property `done` has value of `true`. On the contrary, the class `notCompleted` will be applied if the value of `done` is `false`. The way you evaluate whether a class is added or not, can be more sophisticated: ```jsx <cat> <img class={ warning:(mood=="angry"), sad:(hunger<50) }></img> this.mood = "angry"; this.hunger = 25; </cat> ``` While it's okay to include some logic in the template portion like above, if it starts getting complex, it might be better to resolve the logic outside the template. Lastly, you can use a combination of static and dynamic classes: ```jsx <dog> <div class="pet dog { happy:isHappy, hungry:isHungry }"></div> this.isHappy = true; this.isHungry = false; </dog> ``` Will become: ```jsx <dog> <div class="pet dog happy"></div> </dog> ``` If you want to change the classes, you update `this.isHappy` to `false` or `this.isHungry` to `true`. In Riot, you manipulate the _STATE_, and let Riot react to that by automatically updating the view. :::info **AUTHORS** By Anabel Bugallo and Jin Kuwata :::