Vue JS === > Notes > MSTU 5013 - Spring 2021 --- [TOC] ## :checkered_flag: Introduction ### Vue is REACTIVE **STATE** → a particular condition that something is in at a specific time. * Vue components are objects that store dynamic data, so the data states of the application change from moment to moment. **REACTIVE** → when data state changes, updates relevant parts automatically. * Vue components watch for internal data state changes and will update and re-render when a change is detected. :::warning NOTE: Properties in data are only reactive if they existed when the instance was created ::: --- ## Creating a New Instance ### Setup: Setting up your Vue codepen 1. Select :gear: on JS panel 2. Under *Add External Scripts/Pens* :arrow_lower_right: :mag_right: Search for: **"Vue"** 3. Select **Vue 2.6.11** 4. :scissors: Remove **"min"**: :::danger https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.**~~min~~**.js ::: :::success https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js ::: 5. Hit ***Save and Close*** 6. In **HTML**: Create a `<div>` in HTML and give it an id ```html <div id="app"></div> ``` 7. In **JS**: Instantiate vue component (new instance) ```javascript let app = new Vue({}) ``` 8. Add `el` property -- with css selector as value that links it html element ```html el: #app" ``` 9. you can go to debug mode :arrow_right: inspect - VUE dev tool to see if you have root element **new Vue({})** * used to create new root Vue instance * takes is an object, called the options object, that will contain all the information (options) the Vue app needs to function ### {{ }} Expressions - used to produce or evaluate a value * Add a character or number to data {{ product \+ ‘?’ }} * Combine values * Perform conditional logica with a ternary operator {{clicked ? true : false}} Run methods on your data {{ message.split(‘’).reverse().join(‘’) }}** ## Options Object Vue({options}) - options object has a variety of **optional** properties that give the instance the ability to store data and perform actions. :::info ### el: value: string containing CSS selectors to identify existing DOM element * Short for element * Existing DOM element we MOUNT (attach) our component to * After the instance is mounted, the resolved element will be accessible as `vm.$el`. ::: ::: info ### data: An object that represents information for our application - *Key-Value Pairs*: * ++Key++ - name of data to be used in the template * ++Value++ - value to display when template is rendered <br> ```javascript= data: { message: "Hello there!", age: 25, isFun: false }, ``` <br> *Setting intial values* If you know you’ll need a property later -- but it starts out empty or nonexistent -- set some initial value ```javascript= data: { newTodoText: '', visitCount: 0, hideCompletedTodos: false, todos: [], error: null } ``` ***for Vue.component*** Component’s data option must be a **function** -- lets each instance maintain am independent copy of returned data object ::: :::info ### computed: Functions that calculate/compute data that we can easily use just like data properties. - These functions that RETURN something - *Key value pairs*: * ++Key++ -- name of property * ++Value++ -- function that can be used to generate a value rather than a specific value. <br> ```javascript= computed: { functionName() { return someValue; } } ``` - Computed properties are cached, and will only re-evaluate when some of its reactive dependencies have changed. HTML ```htmlmixed= <div id="app"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> ``` JavaScript ```javascript= var app = new Vue({ el: '#app', data: { message: "Hello" // Try RACECAR }, computed: { reversedMessage: function () { return this.message.split('').reverse().join('') } } }) ``` *tip*: - If your computed property doesn't change based on the change of data point prob could be a data point instead ::: :::info ### methods: where Vue apps store their instance methods. > Methods Don’t call on their own Methods can be used in many situations such as... * helper functions used in other methods * Event handlers (functions that are called in response to specific user interactions) <br> Methods, are a fancy name for functions, actions that can be performed on/by objects. * Vue components are objects * With methods/actions we can call * With access to data that exists in itself * Keyword this ::: --- ## Interpolation & Binding ### Interpolation Use double-curly-braces {{}} (also known as double-mustaches) to indicate places you want to interpolate data. ``` Dr. Kuwata is a {{ dataHere }} professor. ``` ### Binding #### v-bind ::: warning - Directive to link class, styles, attributes to data. - Dynamically binds an attribute to an expression ::: For images ```html <img v-bind:src=“image”> ``` #### :class ```html V-bind:class <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"> ``` *v-bind:class* - can also co-exist with the plain class attribute.... ```html= <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div> ``` And the following data: ```javascript= data: { isActive: true, hasError: false } ``` Ternary operater - use to toggle class conditionally ```htmlmixed= <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> ``` - This example will always apply errorClass, but will only apply activeClass when isActive is truthy ## Styling ### Inline Styling v-bind:style directive - set value to object **Object** keys - CSS properties Values - css property values Ex ```javascript= data: { myColor: red; myFontSize: ‘10px’ } ``` ``` In html element <> v-bind:style=“{ color: myColor, ‘font-size’: myFontSize}” ``` ::: warning NOTE: if we want to set value for a hyphenated CSS property, ex font-size, need to put property name in QUOTES ::: ### Computed Style Objects 1. Store style object as Vue app property 2. Make that object the value of v-bind:style #### Example: > in the example below... note line 4 in html: ==:style="styleObj"== and line 6 in js: ==styleObj: { color:"pink", fontSize: "1em" }== ```htmlembedded= <div id="app"> <span v-bind:id="dynamicID" :class="{ a:isA, b:isB, c:isC }" :style="styleObj" >...</span> <button v-bind:disabled="isDisabled">Button</button> </div> ``` ```javascript= data: { dynamicID: "x3DL03fM7", isA: true, isB: false, isC: true, styleObj: { color:"pink", fontSize: "1em" } } ``` - Using this pattern - can make style objects for specific, reusable use cases. - Create a single computed property that checks a condition and then returns an object with all of the relevant style rules. - Instead of creating a computed property for every rule we want to apply to an element if it passes a certain condition -  :::success **Usage Note:** v-bind:style=“objectName” Use QUOTES around computed property name to use object stored in property - not {} ::: ### Styling With Classes V-bind:class  Value = object * keys - class names * Values - Vue app properties that return a truthy or falsey value Similar to before with v-bind:style, you can also set the value of v-bind:class to a Vue app property that returns a class object instead of writing the object out in your HTML file ### Bootstrap & Vue #### Set up CodePen for Vue and Bootstratp Don't forget to import: * Bootstrap CSS * JQuery * Bootstrap JS * Vue JS Making the entire page part of the ROOT Vue application is the common way of working with single-page applications. #### Classes [Jin's pen with example]([https://](https://codepen.io/jmk2142/pen/gOpmqjE?editors=1010)) When class-binding with Bootstrap, what you end up usually doing is having a series of static BS classes working in conjunction with dynamic (bound) classes as provided by the BS CSS library. --- ## Custom Components Component: ++small++ and ++reusable++ unit, with set of logic, behaviors, and interface elements * Provides explicit structure in JS how to define and implement in HTML. * In Vue, a component is essentially a Vue instance with pre-defined options. Registering a component in Vue is straightforward: ```javascript= // Define a new component called todo-item Vue.component('todo-item', { props: ['todo'], template: '<li>This is a todo</li>' }) var app = new Vue(…) ``` Now you can compose it in another component’s template: ```htmlmixed= <ol> <!-- Create an instance of the todo-item component --> <todo-item></todo-item> </ol> ``` ### Component Properties **Data:** component’s data option must be a function, so that each instance can maintain an independent copy of the returned data object ## Conditional Rendering ### v-if Conditionally render the element based on the truthy-ness of the expression value. * The element and its contained directives / components are destroyed and re-constructed during toggles. * If the element is a `<template>` element, its content will be extracted as the conditional block. * This directive triggers transitions when its condition changes. ### <template></template> > If we want to toggle more than one element... use `v-if` on a `<template>` element, which serves as an invisible wrapper ```htmlmixed= <template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> ``` ### v-else use **v-else** to indicate an else block **Restriction:** previous sibling element must have `v-if` or `v-else-if` ```htmlembedded= <div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div> ``` ### v-else-if **Usage:** Denote the “else if block” for `v-if`. Can be chained. **Restriction:** previous sibling element must have `v-if` or `v-else-if`. ```htmlembedded= <div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div> ``` ### v-show Summary * Toggles visibility off and on * Differs from v-if * V-if - actually adds or removes the element from the DOM * V-show - toggles display on and off ```htmlembedded= <h1 v-show="ok">Hello!</h1> ``` ### Difference Between v-show and v-if v-show elements with v-show are always rendered and remain in the DOM >> It only toggles `display` CSS property of the element. Initial Condition: * `v-if` is also *lazy*: if the condition is **false** on ++initial render++, *it will not do anything* * - the conditional block won’t be rendered until the condition becomes true for the first time. * `v-show` is much simpler - element is always rendered regardless of initial condition, with CSS-based toggling. Cost: `v-if` : higher toggle costs `v-show`: higher initial render costs. #### When to use... `v-show` * prefered if you **need to toggle something very often** * Good for preloading large media into element before showing * E.g. images, videos, audio, etc. `v-if` * if the **condition is unlikely to change at runtime**. * ATTACH/DETACH to the DOM * MOUNT/UNMOUNT components * Tad bit slower performance ## List Rendering ### v-for Use v-for to render a list of items based on an array **Syntax**: `item in items` `items` is the source data array and `item` is an **alias** for the array element being iterated on ```htmlembedded= <ul id="example-1"> <li v-for="item in items" :key="item.message"> {{ item.message }} </li> </ul> ``` ```javascript= var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } }) ``` results: * Foo * Bar Iterate through properties of an object using v-for ```htmlembedded= <ul> <li v-for="value in myObject"> {{ value }} </li> </ul> ``` ```javascript= Vue.createApp({ data() { return { myObject: { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' } } } }).mount('#v-for-object') ``` You can also provide a second argument for the property's name (a.k.a. key): ```htmlembedded= <li v-for="(value, name) in myObject"> {{ name }}: {{ value }} </li> ``` ## Form Inputs forms & binding with class binding - data changes will change the class ### 2 Way binding form inputs - always think v-model and then whats hte property that were going ot bind this to ### v-model #### why v-model when we have v-bind? Vue's v-model is specifically designed to automate common routines around forms and form input UI. * Automatically triggers HTML events * Automatically GETS values of form elements. * Automatically SETS component data. * Detects radio-button "groups" without a name attribute * Detects checkbox "groups" and knows how to deal with it as an array. ```htmlembedded= <div id="app"> <input v-model="message"> <p>Message is: {{ message }}</p> </div> ``` ```javascript= var app = new Vue({ el: '#app', data: { message: "" } }) ``` #### Checkboxes * Checkbox groups all share same v-model, have different values, point to a single array in the component data** v-model automatically pushes checked input values into our array! ```htmlembedded= <label><input v-model="dateItems" type="checkbox" value="rose">Flowers</label> ``` :arrow_up: if checkbox next to text "Flowers" is checked - "rose" will be pushed to the array in Vue instance data: * property: name should match text in v-model * eg - dataItems: [] * value: **empty array** * in data property - vModelName: [] (empty array) #### Radio * Share same v-model but have individual values * bind radio-button to component data property set to **empty string** * in data property - vModelName: "" (empty string) ```htmlembedded= <label><input type="radio" v-model="dataRadio" value="feline" checked> Cat</label> <label><input type="radio" v-model="dataRadio" value="canine"> Dog</label> <label><input type="radio" v-model="dataRadio" value="avian"> Bird</label> ``` ## Events Event changes state of data - ```javascript= methods: { toggleLight(){ this.lightOn = !this.lightOn; } } ``` The basic concept of event handling in VUE is the same as Native JS. The strategy differs in that: ``` Handlers methods are designed to change STATE of the component not to directly manipulate the DOM ``` Vue vs Native * In native, the handler would manipulate classes, styles. * In VUE, the handler would manipulate the data → Vue reacts to that data change to apply the class or style. **events manipulate states** - computed value may then change what is returned based on the state that was manipulated in event dataItem: state on event --> changes state computed can then use changed state to change what is viewed OR we could use v-if= ## v-on v-on attaches event listener to the DOM and sets the handler function that responrs :::success event handlers should be defined in methods property ::: :::danger Avoid using inline event handler except for special situations. ::: shorthand: v-on:event is @event **@click, @focus, @keypress, @mouseenter** ```htmlmixed= <div id="app"> <button v-on:click="add">Add</button> <p>Add {{ addCounter }} times.</p> <button v-on:click="deleteCounter -= 1">Delete</button> <p>Delete {{ deleteCounter }} times.</p> </div> ``` ```javascript= var app = new Vue({ el: "app", data: { addCounter: 0, deleteCounter: 0 }, methods: { add: function() { this.addCounter += 1; } } }) ``` ## Data Driven Event Handling *Common Pattern* 1. Handler function - designed to **CHANGE** data state of component 2. the data **change** --> **causes** vue to react and update the render Critical Questions: 1. **Data** - Which data relates to visual changes you want and how? 2. **UI + Event** - What does the user do and when does it happen? 3. **Handler** - What data should change to cause the component to update the view? ## Event Modifiers and Key Modifiers .enter / .tab / .delete / .space / .up / .down / .left / .right / .prevent - A common scenario is on a keyup event you only want it to be handled when a user pushes the ENTER key. <input v-on:keyup.enter="printName"> Vue will only execute the handler when the ENTER key triggers the keyup event. .stop Stop all event propagation ## Submit event forms have event called submit when submit event happens you can run function @submit="functionName" function will run when form is submitted .prevent @submit.prevent - prevent default action that form would normally undergo ## Animation & Transitions ```css= .v-enter { /* starting style */ opacity: 0; } .v-enter-active { /* active entering style */ transition: opacity 2s ease-in; } .v-leave-active { /* active leaving style */ transition: opacity 2s ease-out; } .v-leave-to { /* ending style */ opacity: 0; } ``` Here the `v-` prefix is the default when you use a `<transition>` element with no name. If you use `<transition name="my-transition">` for example, then the `v-enter` class would instead be `my-transition-enter` name transitions about what they do not the element they transion **enter/leave** : position that animation starts with on the first frame, **enter-active/leave-active** : while the animation is running- this is the one you’d place the animation properties themselves on **enter-to/leave-to** : specifies where element should be ++on the last frame++. ![transition classes](https://css-tricks.com/wp-content/uploads/2017/01/transition.png) `ease-out` This works and looks fine for something like opacity . But you may find that if you’re transitioning properties such as transform, you might want to separate the two and use `ease-out` for the enter-active class and `ease-in` for the enter-leave class (or cubic-beziers that vaguely follow the same curve ## Vue CLI Allows us to... * select libraries our project will be using * automatically plugs them into the project * Configures webpack * JS files, CSS and our dependencies get properly bundled together, minified and optimized * --> resulting in faster loading speed * Write our HTML CSS & JS however we like * can use single-file .vue components, TypeScript, SCSS, Pug, the latest version of ECMAScript, etc. * Enable Hot Module Replces (HMR) * App updates live whenever you save a change ### Installing the CLI run command: ``` npm i -g @vue/cli # OR yarn global add @vue/cli ``` ### Creating a Vue project Two ways: 1. using Vue UI 2. directly from the command line #### Directly from command line To start the creation of a Vue project, with the name of “real-world-vue”: ``` vue create real-world-vue ``` prompts: defaul or **manual** >highligh **Mannually select features** --> hit enter then presented with a list of feature options: >Using the down arrow key, we’ll move down and use the spacebar to select **Router** and **Linter / Formatter**. --> hit enter. Select version of vue >Then we’ll say **Y** (yes) to using History mode for Vue Router. We’ll then be asked to choose a Linter / Formatter, which is entirely up to you. > choose **ESLint + Prettier** and tell it to **Lint on save**. ### Serving my project Once our project is done being created, we can `cd` into it. In order to view it live in our browser, we’ll run the command `npm run serve`, which compiles the app and serves it live at a local host. ## Vue Router Need if: your website has multiple pages (or “views”) and you’re using Vue ### Server-side routing vs Client-side routing #### Server-side routing User: types in url Browser: request url from server :arrow_right: Server: returns page :arrow_left: If user click on link on page… Browser: request that url and server returns new page Browser refreshes on every page load #### Client-side routing ![https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2F4.opt.gif?alt=media&token=d8f79fe9-dc02-4b1d-8ee5-91fd87e953db](https://lh4.googleusercontent.com/bwaSMqIWLMCQbM22Dn9XCRcG5T2Ryw9a_yK4gJcTzIC8t32MVb-rA9lmyliAwUlzRJcWd6wJEuMD_acRxYWzcVe7-79GPCg5fNHpWTkoJj1aHXwzfFQFhll7mR71NKntvz2h8aMf)![https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2F3.opt.1603835634884.jpg?alt=media&token=436ffcea-e024-4ebb-b51d-fefbfffca84e](https://lh3.googleusercontent.com/MHefvxPrz9S5dzqQMqmy35jzgwN4JnYLlKCjTmQ3_5LGvRTcNQJTiF2riCXdvg0RlYalhZRzsarxEfHyPHD4MUu9EFE4zDJ-5KBQLv3jOyaSyBDLVk-Fs-xHyzVXH_FUy34r4uZT) User: types in url Browser: request url Server - returns that page, specifically index.html and sends along with it hte rest of the app and because the app already has all of the code that it needs within the browser, Vue with the help of Vue Router compares and then renders the differences without ever having to reload the page. ### Vue Router Configuration #### src/router/index.js At top - import vue-router library `import { createRouter, createWebHistory } from 'vue-router'` Then import a component we’ll use in our routes ``` import Home from '../views/Home.vue' ``` Then we use this route ``` const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', ...// Skipping this part, which we will come back to later } ] ``` `path` * indicates actual route user will be taken to in terms of the URL * / means root, the homepage of our application `name` * allows us to give route a name so we can use that name throughout our app to refer to this route `component` * specify which component to render at that route ```javascript= { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ``` About isn’t imported at top of file like Home * instead about will only be loded into one’’s browser once they navigate to /about * This is a performance optimization - might not be necessary in small simple app ##### createRouter use `createRouter` to create the router telling it to use the browser’s History API (we selected this as an option when we configured the project with Vue CLI), and sending in the `routes` we created above, before finally exporting it from this file. ### Built-in Vue Router Components `<router-link>` is a component (from the vue-router library) whose job is to link to a specific route. * think of them like an embellished anchor tag:`to` attribute behaves similar to href. * in App.vue - `<router-link>` = global, Vue Router-specific components we have access to `<router-view/>` is essentially a placeholder where the contents of our “view” component will be rendered onto the page. ### Dynamic Route Matching Very often we will need to map routes with the given pattern to the same component. For example, we may have a User component which should be rendered for all users but with different user IDs. In Vue Router we can use a dynamic segment in the path to achieve that, we call that a param: ```javascript= import User from './User.vue' // these are passed to `createRouter` const routes = [ // dynamic segments start with a colon { path: '/users/:id', component: User }, ] ``` ### Routes' Matching Syntax Optional parameters You can also mark a parameter as optional by using the ? modifier (0 or 1): js const routes = [ // will match /users and /users/posva { path: '/users/:userId?' }, // will match /users and /users/42 { path: '/users/:userId(\\d+)?' }, ] Note that * technically also marks a parameter as optional but ? parameters cannot be repeated. ## Single File component A typical .vue component has three sections: `<template>`, `<script>`, and `<style>`. `<template></template>` HTML Template `<script></script>` Add JS here This is where data(){}, `<style scoped></style>` `scoped` attribute: allows us to _scope_ and isolate these styles to just this component. >these styles are specific to this component and won’t affect any other part of our application ### App.vue place content in our **App.vue**’s template that you want to be displayed globally across every view the our application. useful for: * search bar * nav bar * header ## Pinia