A Room with a Vue

A beginner's dive into Vue.js

@ryanrousseau

Solution Architect @ Octopus Deploy

Vue.js Beginner

Links

https://ryanrousseau.com/presentations/a-room-with-a-vue/

https://github.com/ryanrousseau/presentations/tree/master/a-room-with-a-vue/

What is Vue.js?

Vue is a progressive framework for building user interfaces.

Vue is designed from the ground up to be incrementally adoptable.

The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects.

Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.

Why Vue.js?

Approachable

Already know HTML, CSS, and JavaScript?

You're ready to start!

Versatile

As much or as little as you want

Performant

Small core library - 20KB min+gzip

Virtual DOM makes page updates fast

What about React, Angular, Ember, etc?

It really comes down to preference.

Do you like fully-featured, opiniated frameworks?

Do you like JSX?

Do you have time for the learning curve?

My reasons for choosing Vue.js

I wanted something fast in both performance and time to be productive.

I really only wanted the templating and data binding.

It reminds me of Knockout.

Let's learn the basics!

Declarative Rendering

                            
                                <div id="ex1">
                                    {{ text }}
                                </div>
                            

                            
                                var ex1 = new Vue({
                                    el: '#ex1',
                                    data: {
                                        text: 'This text was databound with Vue.'
                                    }
                                });
                            
                        
{{ text }}

No really!

Use your browser's dev tools console to run the code below or click here. Then go back to the slide above.

                            
                                ex1.text = "These slides are demos!";
                                Reveal.navigateUp();
                            
                        

v-bind

                            
                                <div id="ex2">
                                    <a v-bind:href="url" target="_blank">
                                        This link should open something.
                                    </a>
                                </div>
                            

                            
                                var ex2 = new Vue({
                                    el: '#ex2',
                                    data: {
                                        url: 'http://localhost:5000'
                                    }
                                });
                            
                        

Use your browser's dev tools console to run the code below or click here. Then go back to the slide above.

                            
                                ex2.url = "https://ryanrousseau.com/presentations/a-room-with-a-vue/";
                                Reveal.navigateUp();
                            
                        

Conditionals

                            
                                <div id="ex3">
                                    <span v-if="visible">This text is visible by default</span>
                                    <span v-show="!visible">This text is hidden by default</span>
                                </div>
                            
                        
This text is visible by default This text is hidden by default

Try it in dev tools or click here to toggle the setting.

                            
                                ex3.visible = !ex3.visible;
                                Reveal.navigateUp();
                            
                        

Loops

                            
                                <div id="ex4">
                                    <ol>
                                        <li v-for="todo in todos">
                                            {{ todo.text }}
                                        </li>
                                    </ol>
                                </div>
                            
                        
                            
                                var ex4 = new Vue({
                                    el: '#ex4',
                                    data: {
                                        todos: [
                                            { text: 'Item #1' },
                                            { text: 'Item #2' },
                                            { text: 'Item #3' },
                                            { text: 'Item #4' }
                                        ]
                                    }
                                });
                            
                        
  1. {{ todo.text }}

Try it in dev tools or click here to remove an item or click here to add an item.

                            
                                // remove last item
                                if (ex4.todos.length > 0) { ex4.todos.pop(); }

                                // add a new item
                                ex4.todos.push({ text: 'Item #' + (ex4.todos.length + 1) });

                                Reveal.navigateUp();
                            
                        

Events!

                            
                                <div id="ex5">
                                    <p>{{ text }}</p>
                                    <button v-on:click="reverseText">Reverse Text</button>
                                </div>
                            

                            
                                var ex5 = new Vue({
                                    el: '#ex5',
                                    data: {
                                        text: 'Click the button to reverse this text'
                                    },
                                    methods: {
                                        reverseText: function () {
                                            this.text = this.text.split('').reverse().join('')
                                        }
                                    }
                                });
                            
                        

{{ text }}

v-model

                            
                                <div id="ex6">
                                    <p>{{ text }}</p>
                                    <input v-model="text">
                                </div>
                            

                            
                                var ex6 = new Vue({
                                    el: '#ex6',
                                    data: {
                                        text: 'Type in the box to change this slide'
                                    }
                                });
                            
                        

{{ text }}

Vue.component

                            
                                // Define a new component called todo-item
                                Vue.component('todo-item', {
                                    template: '
  • This is a todo
  • ' })
    <div id="ex7"> <ol> <!-- Create an instance of the todo-item component --> <todo-item></todo-item> </ol> </div> var ex7 = new Vue({ el: '#ex7' });

    Let's build a website!

    Speaker Vue

    A conference site built with Vue.js

    Final Version

    Layout

    I'm using the Phantom Template from html5up

    Let's get Vue.js working with the layout

                                
                                    <div id="main">
                                        <div class="inner">
                                            ...
                        
                                            <div v-if="working">Vue.js is working.</div>
                                        </div>
                                    </div>
                                
        
                                
                                    var main = new Vue({
                                        el: '#main',
                                        data: {
                                            "working": true
                                        }
                                    });
                                
                            

    Version 0

    Let's make convert the header to Vue.js

                                
                                    <header id="header">
                                        <div class="inner">
                                            <!-- Logo -->
                                            <a href="index.html" class="logo">
                                                <span class="symbol"><img v-bind:src="logo" alt="" /></span><span class="title">{{ title }}</span>
                                            </a>
                                            ...
                                        </div>
                                    </header>
                                
        
                                
                                    var header = new Vue({
                                        el: '#header',
                                        data: {
                                            "title": "Speaker Vue",
                                            "logo": "/presentations/templates/phantom/images/logo.svg"
                                        }
                                    });
                                
                            
                                
                                        Vue.component('sv-header', {
                                            props: ['header'],
                                            template: `
                                        <div class="inner">
                                            <!-- Logo -->
                                            <a href="index.html" class="logo">
                                                <span class="symbol"><img v-bind:src="header.logo" alt="" /></span><span class="title">{{ header.title }}</span>
                                            </a>
                                    
                                            <!-- Nav -->
                                            <nav>
                                                <ul>
                                                    <li><a href="#menu">Menu</a></li>
                                                </ul>
                                            </nav>
                                        </div>`
                                        });
                                
                            
                                
                                    <header id="header">
                                        <sv-header v-bind:header="header"></sv-header>
                                    </header>
                                
        
                                
                                    var header = new Vue({
                                        el: '#header',
                                        data: {
                                            "header": {
                                                "title": "Speaker Vue",
                                                "logo": "/presentations/templates/phantom/images/logo.svg"
                                            }
                                        }
                                    });
                                
                            

    Version 1

    Let's make convert the menu to Vue.js

                                
                                        Vue.component('sv-menu', {
                                            props: ['site'],
                                            template: `
                                        <nav id="menu">
                                            <h2>Menu</h2>
                                            <ul>
                                                <li><a v-bind:href="site.root">Home</a></li>
                                                <li><a v-bind:href="site.root + 'sessions/list.html'">Sessions</a></li>
                                                <li><a href="generic.html">About</a></li>
                                                <li><a href="generic.html">Register</a></li>
                                            </ul>
                                        </nav>`
                                        });
                                
    
                                
                                    <div id="menu-div">
                                        <sv-menu v-bind:site="site"></sv-menu>
                                    </div>
                                
                            
                                
                                    var menu = new Vue({
                                        el: '#menu-div',
                                        data: {
                                            "site": site
                                        }
                                    });
                                
                            

    Version 2

    We're doing the footer next

    But we don't really need to see that

    Let's build some speaker tiles!

    But first we need data...

                                
                                    data.speakers = [
                                        {
                                            id: 'spider-man',
                                            name: 'Spider-Man',
                                            bio: 'Your friendly neighborhood webslinger.'
                                        }
                                    ];
                                
                            

    What does a tile look like?

                                
                                    <section class="tiles">
                                        <article class="style1">
                                            <span class="image">
                                                <img src="/presentations/templates/phantom/images/pic01.jpg" alt="" />
                                            </span>
                                            <a href="generic.html">
                                                <h2>Magna</h2>
                                                <div class="content">
                                                    <p>Sed nisl arcu euismod sit amet nisi lorem etiam dolor veroeros et feugiat.</p>
                                                </div>
                                            </a>
                                        </article>
                                    </section>
                                
                            
                                
                                    <section class="tiles">
                                        <article class="style1">
                                            <span class="image">
                                                <img src="/presentations/templates/phantom/images/pic01.jpg" alt="" />
                                            </span>
                                            <a v-bind:href="'./speakers/#' + speaker.id">
                                                <h2>{{ speaker.name }}</h2>
                                                <div class="content">
                                                    <p>{{ speaker.bio }}</p>
                                                </div>
                                            </a>
                                        </article>
                                    </section>
                                
                            
                                
                                    var main = new Vue({
                                        el: '#main',
                                        data: {
                                            "working": true,
                                            "speaker": data.speakers[0]
                                        }
                                    });
                                
                            

    Version 3

    Okay, but let's have more than one speaker...

                                
                                    var main = new Vue({
                                        el: '#main',
                                        data: {
                                            "working": true,
                                            "speakers": data.speakers
                                        }
                                    });
                                
                            
                                
                                    <section class="tiles">
                                        <article class="style1" v-for="speaker in speakers">
                                            <span class="image">
                                                <img v-bind:src="speaker.image" alt="" />
                                            </span>
                                            <a v-bind:href="'./speakers/#' + speaker.id">
                                                <h2>{{ speaker.name }}</h2>
                                                <div class="content">
                                                    <p>{{ speaker.bio }}</p>
                                                </div>
                                            </a>
                                        </article>
                                    </section>
                                
                            
                                
                                    <section class="tiles">
                                        <template v-for="speaker in speakers">
                                            <article v-bind:class="speaker.style">
                                                <span class="image">
                                                    <img v-bind:src="speaker.image" alt="" />
                                                </span>
                                                <a v-bind:href="'./speakers/#' + speaker.id">
                                                    <h2>{{ speaker.name }}</h2>
                                                    <div class="content">
                                                        <p>{{ speaker.bio }}</p>
                                                    </div>
                                                </a>
                                            </article>
                                        </template>
                                    </section>
                                
                            
                                
                                    <section class="tiles">
                                        <template v-for="speaker in speakers">
                                            <sv-speaker-tile v-bind:speaker="speaker"></sv-speaker-tile>
                                        </template>
                                    </section>
                                
                            

    Version 4

    It's time for that speaker page

    Let's add session data

                                
                                    data.speakers = [
                                        {
                                            id: 'spider-man',
                                            name: 'Spider-Man',
                                            bio: "Bitten by a radioactive spider, high school student Peter Parker gained the speed, strength and powers of a spider. Adopting the name Spider-Man, Peter hoped to start a career using his new abilities. Taught that with great power comes great responsibility, Spidey has vowed to use his powers to help people.",
                                            image: 'http://i.annihil.us/u/prod/marvel/i/mg/3/50/526548a343e4b.jpg',
                                            style: 'style',
                                            sessions: [ 'session-1', 'session-2' ]
                                        },
                                        ...
                                    ]
    
                                    data.sessions = [
                                    {
                                        id: 'session-1',
                                        title: 'How To Do Whatever a Spider Can',
                                        speaker: 'Spider-Man',
                                        speakerId: 'spider-man',
                                        blurb: 'Learn how to channel your inner arachnid and fight crime.',
                                        description: 'Session 1 Description',
                                        time: 'Saturday 10am - 11am'
                                    },
                                    ...
                                    ]
                                
                            

    Our link looks like this

    /speaker-vue/v5/speakers/#spider-man

    So we can grab the speaker id from the url

                                
                                    var main = new Vue({
                                        el: '#main',
                                        created() {
                                            var speakerId = (window.location.hash) ? window.location.hash.split("#")[1] : '';
                                            var speaker = data.speakers.find((speaker) => speaker.id === speakerId);
                                            var sessionIds = speaker.sessions;
                        
                                            this.speaker = speaker;
                                            this.sessions = data.sessions.filter((session) => sessionIds.indexOf(session.id) > -1);
                                        }
                                    });
                                
                            
                                
                                    <div id="main">
                                        <div class="inner">
                                            <h1>{{ speaker.name }}</h1>
                                            <p>{{ speaker.bio }}</p>
                        
                                            <h2>Sessions</h2>
                        
                                            <section class="tiles">
                                                <template v-for="session in sessions">
                                                    <sv-session-tile v-bind:session="session" v-bind:speaker="speaker"></sv-session-tile>
                                                </template>
                                            </section>
                                        </div>
                                    </div>
                                
                            
                                
                                    Vue.component('sv-session-tile', {
                                        props: ['session', 'speaker'],
                                        template: `
                                    <article v-bind:class="speaker.style">
                                        <span class="image">
                                            <img v-bind:src="speaker.image" alt="" />
                                        </span>
                                        <a v-bind:href="'../sessions/#' + session.id">
                                            <h2>{{ session.title }}</h2>
                                            <div class="content">
                                                <p>{{ session.blurb }}<br/>{{ session.time }}</p>
                                            </div>
                                        </a>
                                    </article>
                                    `
                                    });
                                
                            

    Version 5

    Y'all want to build the session details page?

                                
                                    data.sessions = [
                                        {
                                            id: 'session-1',
                                            title: 'How To Do Whatever a Spider Can',
                                            speaker: 'Spider-Man',
                                            speakerId: 'spider-man',
                                            blurb: 'Learn how to channel your inner arachnid and fight crime.',
                                            description: `Spider-Man. Spider-Man. I do whatever a spider can...

    And now so can you! I'll share my crimefighting secrets that you can start using today to capture the bad guys and keep your Aunt May safe.`, time: 'Saturday 10am - 11am' }, ... ]
                                
                                    var main = new Vue({
                                        el: '#main',
                                        created() {
                                            var sessionId = (window.location.hash) ? window.location.hash.split("#")[1] : '';
                                            this.session = data.sessions.find((session) => session.id === sessionId);
                                            
                                            this.speaker = data.speakers.find((speaker) => speaker.id === this.session.speakerId);
                                            this.site = site;
                                        }
                                    });
                                
                            
                                
                                    <div id="main">
                                        <div class="inner">
                                            <h1>{{ session.title }}</h1>
                                            <h2>{{ session.time }}</h2>
                                            <p v-html="session.description"></p>
                        
                                            <h2>Speaker</h2>
                                            <section class="tiles">
                                                <sv-speaker-tile v-bind:speaker="speaker" v-bind:site="site"></sv-speaker-tile>
                                            </section>
                                        </div>
                                    </div>
                                
                            

    Version 6

    And a page that lists all sessions?

                                
                                    <div class="inner">
                                        <section class="tiles">
                                            <template v-for="session in sessions">
                                                <sv-session-tile v-bind:session="session" v-bind:speaker="session.speaker" v-bind:site="site"></sv-session-tile>
                                            </template>
                                        </section>
                                    </div>
                                
                            
                                
                                    var main = new Vue({
                                        el: '#main',
                                        created() {
                                            this.sessions = data.sessions.map((s) => {
                                                var session = Object.assign({}, s);
                                                session.speaker = data.speakers.find((speaker) => speaker.id === session.speakerId);
                        
                                                return session;
                                            });
                        
                                            this.site = site;
                                        }
                                    });
                                
                            

    Version 7

    Filtering and Computed Properties

    Let's filter sessions by day

                                
                                    <div class="col-6 col-12-medium">
                                        <ul class="actions">
                                            <li><a v-on:click="toggleFriday" class="button" v-bind:class="{ primary: fridayVisible }">Friday</a></li>
                                            <li><a v-on:click="toggleSaturday" class="button" v-bind:class="{ primary: saturdayVisible }">Saturday</a></li>
                                        </ul>
                                    </div>
                                
                            
                                
                                    computed: {
                                        visibleSessions: function() {
                                            if (this.fridayVisible && this.saturdayVisible) {
                                                return this.sessions;
                                            }
                                            
                                            if (this.fridayVisible) {
                                                return this.sessions.filter((session) => session.time.startsWith('Friday'));
                                            }
                    
                                            if (this.saturdayVisible) {
                                                return this.sessions.filter((session) => session.time.startsWith('Saturday'));
                                            }
                                        }
                                    },
                                
                            
                                
                                    methods: {
                                        toggleFriday: function() {
                                            this.fridayVisible = !this.fridayVisible;
                                        },
                                        toggleSaturday: function() {
                                            this.saturdayVisible = !this.saturdayVisible;
                                        }
                                    }
                                
                            

    Version 9

    Finally, let's do some data input

    We need a page to add sessions

                                
                                    <input v-model="session.id" type="text" name="id" id="title" placeholder="ID" autofocus />
                                    <select v-model="session.speakerId" name="speaker" id="speaker">
                                        <option value="">- Speaker -</option>
                                        <option v-for="s in speakers" v-bind:value="s.id">
                                            {{ s.name }}
                                        </option>
                                    </select>
                                    <li><a v-on:click="saveSession" class="button primary">Save</a></li>
                                    <li><a v-on:click="clearSession" class="button">Clear</a></li>
                                
                            
                                
                                    var main = new Vue({
                                        el: '#main',
                                        data: {
                                            session: Object.assign({}, emptySession),
                                            speakers: data.speakers,
                                            site: site
                                        },
                                        methods: {
                                            clearSession: function() {
                                                this.session = Object.assign({}, emptySession);
                                            },
                                            saveSession: function() {
                                                var session = JSON.parse(JSON.stringify(this.session));
                                                data.addSession(session);
                                                this.session = Object.assign({}, emptySession);
                                            }
                                        }
                                    });
                                
                            

    Version 10

    Thank you!