Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Vue.js 2 and Bootstrap 4 Web Development

You're reading from   Vue.js 2 and Bootstrap 4 Web Development Build responsive SPAs with Bootstrap 4, Vue.js 2, and Firebase

Arrow left icon
Product type Paperback
Published in Sep 2017
Publisher Packt
ISBN-13 9781788290920
Length 310 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Olga Filipova Olga Filipova
Author Profile Icon Olga Filipova
Olga Filipova
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Please Introduce Yourself – Tutorial FREE CHAPTER 2. Under the Hood – Tutorial Explained 3. Let's Get Started 4. Let It Pomodoro! 5. Configuring Your Pomodoro 6. Please Authenticate! 7. Adding a Menu and Routing Functionality Using vue-router and Nuxt.js 8. Let's Collaborate – Adding New Workouts Using Firebase Data Storage and Vue.js 9. Test Test and Test 10. Deploying Using Firebase Index

Making things functional with Vue.js

So, what do we want to achieve with our form? We want the new message to be created. This message has to be composed of title, text, and the timestamp. We also want to add this message to our messages reference array.

Let's call this new message newMessage and add it to the data attributes of App.vue:

//App.vue
<script>
  <...>
  export default {
    data () {
      return {
        newMessage: {
          title: '',
          text: '',
          timestamp: null
        }
      }
    },
  <...>
  }
</script>

Now, let's bind the title and the text of this newMessage object to input and textarea of our form. Let's also bind a method called addMessage to the submit handler of our form so that the whole form's markup looks like this:

<template>
<...>
  <form @submit="addMessage">
    <div class="form-group">
      <input class="form-control"v-model="newMessage.title"maxlength="40"autofocus  placeholder="Please introduce yourself :)" />
    </div>
    <div class="form-group">
      <textarea class="form-control"v-model="newMessage.text" placeholder="Leave your message!"  rows="3"></textarea>
    </div>
    <button class="btnbtn-primary" type="submit">Send</button>
  </form>
  <...>
</template>

Well, we have bound the "addMessage" method to the submit callback of the form, but we haven't defined this method yet! So, let's define it. Add the methods object to our App.vue export section and define the addMessage method inside it. This method will receive the event attribute from our form and will just grab the newMessage object and push it into the messagesRef array. Doesn't it sound easy?

//App.vue
<script>
  export default {
  <...>
    methods: {
      addMessage (e) {
        e.preventDefault()
        this.newMessage.timestamp = Date.now()
        messagesRef.push(this.newMessage)
      }
    }
  }
</script>

Now, open the page, fill in the form, and hit the Send button. You'll see your message immediately appearing on the list of messages:

Making things functional with Vue.js

The message we introduce in the form is immediately propagated to the messages list

There is still something we need to fix. We don't want the values we fill the form with to remain there after our message is added to the messages list. So, we need to clear it inside the addMessage method. Probably, some basic check, at least for the title, would also be nice. So, rewrite the method as follows:

//App.vue
addMessage (e) {
  e.preventDefault()
  if (this.newMessage.title === '') {
    return
  }
  this.newMessage.timestamp = Date.now()
  messagesRef.push(this.newMessage)
  this.newMessage.text = ''
  this.newMessage.title = ''
  this.newMessage.timestamp = null
}

Now, if you start adding more messages, things look a bit weird. The way we're displaying the messages is probably not the best way for our case. Do you remember we wrapped up our message cards into div with the card-group class? Let's try to replace it with the card-columns class and check whether it looks better. In fact, it does. Let's keep it like that.

Adding utility functions to make things look nicer

We already have a fully functional single-page application, but it still lacks some awesomeness. For example, it's not really beautiful that the time appears as a timestamp. Let's write the utility function that will transform our timestamp into something beautiful.

We will use the Moment.js library (https://momentjs.com/). Install it in the application folder:

npm install moment --save

Create a folder and call it utils. Add a file called utils.js to this folder. Import moment and write the following function:

//utils.js
import moment from 'moment'

function dateToString (date) {
  if (date) {
    return moment(date).format('MMMM Do YYYY, h:mm:ss a')
  }
  return''
}

Export it in the end of the file:

//utils.js
<...>
export { dateToString }

Let's import this function to App.vue and use it to format our timestamp. Open the App.vue file and add the import statement at the beginning of the script section:

//App.vue
<script>
  import Firebase from 'firebase'
  import { dateToString } from './utils/utils'
  <...>
</script>

In order to be able to use this function within the Vue template, we have to export it in the methods section. Just add a new entry to the methods object:

//App.vue
<script>
  export default {
    <...>
    methods: {
      dateToString: dateToString,
      <...>
    }
</script>

Since we use ES6, we can just write the following lines of code:

methods: {
  dateToString
}

Now, we can use this method inside the template section. Just wrap the message.timestamp binding object in the dataToString method:

<p class="card-text"><small class="text-muted">Added on {{ dateToString(message.timestamp) }}</small></p>

Check out the page! Now, you can see beautiful dates instead of Unix timestamps.

Exercise

I have a small exercise for you. You saw how easy it was to add a utility function to transform the timestamp into the nicely formatted date. Now, create another utility function and call it reverse. This function should be used to display the array of messages in the reversed order, so the most recent messages should appear first. Check the code for this chapter in case you're in doubt.

Extracting message cards to their own component

You probably noticed that the first message of the demo application is always there. It's not moved by other, fresh message items. So, it seems that it's kind of a special message, and it's treated in a special way. In fact, it is. If you want to make a card sticky, just add it before the card element that iterates through other messages. You can also add some class to this card to show that it's really special. In my case, I added Bootstrap's card-outline-success class that outlines the element in a nice green color:

//App.vue
<div class="card-columns">
  <div class="card card-outline-success">
    <div class="card-block">
      <h5 class="card-title">Hello!</h5>
      <p class="card-text">This is our fixed card!</p>
      <p class="card-text"><small class="text-muted">Added on {{ dateToString(Date.now()) }}</small></p>
    </div>
  </div>
  <div class="card" v-for="message in messages">
    <div class="card-block">
      <h5 class="card-title">{{ message.title }}</h5>
      <p class="card-text">{{ message.text }}</p>
      <p class="card-text"><small class="text-muted">Added on {{ dateToString(message.timestamp) }}</small></p>
    </div>
  </div>
</div>

Now, you have a nice sticky card with a color that differs from other cards' color. But… don't you see any problem? We have the very same code repeated twice in our template. I'm pretty sure that you are aware of the rule of thumb of any developer: DRY—don't repeat yourself!

Let's extract the card to an individual component. It's really easy. Add a component called Card.vue to the components folder. The code for this component is really simple:

//Card.vue
<template>
  <div class="card">
    <div class="card-block">
      <h5 class="card-title">{{ title }}</h5>
      <p class="card-text">{{ text }}</p>
      <p class="card-text"><small class="text-muted">{{ footer }}</small></p>
    </div>
  </div>
</template>

<script>
  export default {
    props: ['title', 'text', 'footer']
  }
</script>

Now, let's invoke this component from App.vue with different values for title, text, and footer. First of all, it should be imported and exported in the Vue components object:

//App.vue
<script>
<...>
  import Card from './components/Card'
  <...>
  export default {
  <...>
    components: {
      Card
    }
  }
</script>

Now, we can use the <card> element within our template. We need to bind title, text, and footer. Footer is actually the text that says Added on …. So, the markup for the first card will look like this:

//App.vue
<template>
  <div class="card-columns">
    <card class="card-outline-success":title="'Hello!'":text="'This is our fixed card!'":footer="'Added on ' + dateToString(Date.now())"></card>
  </div>
</div>
</template>

The list of other messages will follow the same logic. For each message from the messages array, we will bind the corresponding message's entries (title, text, and timestamp). So, the markup for the list of message cards will look like this:

<div class="card-columns">
<...>
  <card v-for="message in messages":title="message.title":text="message.text":footer="'Added on ' + dateToString(message.timestamp)"></card>
</div>
</div>

As you can see, we have replaced fourteen lines of code with only two lines! Of course, our component also contains some lines of code, but now, we can reuse it again and again.

Exercise

The way we've extracted the card code into its individual component is, without any doubt, great, but the way we are binding attributes for the first message is a bit ugly. What if at some point we need to change the message's text? First of all, it's not easy to find the text inside the markup. Also, it is pretty difficult to manage the text inside the markup attributes, because we have to be really careful not to mess up with double/single quotes. And, admit it, it's just ugly. Your task for this exercise is to extract title, text, and date for the first message into something nicer (for example, export it in the data object) and bind it the same way we bind other messages. If you have doubts regarding this exercise, check out this chapter's code.

Note

Don't be confused by the v-bind directive in the provided code. We've been using it already, just its shortened version—the name of a bound property written after the semicolon. So, for example, v-bind:messages is the same as :messages.

You have been reading a chapter from
Vue.js 2 and Bootstrap 4 Web Development
Published in: Sep 2017
Publisher: Packt
ISBN-13: 9781788290920
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime