Vojtěch
Vojtěch

Reputation: 12416

Hiding vue.js template before it is rendered

I am trying to hide the vue.js template's contents before it is fully rendered. Consider following template:

<div class="loader"> 
  <table class="hidden">
    <tr v-for="log in logs">
      <td>{{log.timestamp}}</td>
      <td>{{log.event}}</td>
    </tr>
  </table>
</div>

When I render this template on the server, the user sees the contents of the <tr> element before it is rendered. For this reason I use the class hidden to hide it, before it is fully rendered.

Also before it is rendered, I am showing a loader element with some animated progressbar.

Once it is rendered, I would just call element.show() in jQuery and hide the progressbar as well. My question is: is it okay to mix jQuery and vue.js to achieve this?

var vueLogs = new Vue({
  el: "#checkInLogsHolder",
  data: {logs: []}
});
var holder = $("#checkInLogsHolder");

function response(payload) {
  // hiding the loader animation
  holder.find(".loader").remove();
  // showing the rendered table
  holder.find("table").show();
  vueLogs.logs.unshift(payload);
}

Is there some better way to do this?

Upvotes: 36

Views: 47035

Answers (6)

massimoi
massimoi

Reputation: 384

I've created a pure CSS solutions, building on @Maniruzzaman Akask solutions. (I don't have font awesome installed in my template, and the result is cool ;-)

Steps

1) Put the v-cloak tag inside your #app div (as reccomended in the answer above)

<div id="app">

  <div v-cloak>

  <div class="v-cloak--inline"> <!-- Parts that will be visible before compiled your HTML -->
    <div class="spinner"></div> <!-- this is a cool spinner taken from spinKit -->
  </div>

  <div class="v-cloak--hidden"> 
           <!-- Parts that will be visible After compiled your HTML -->
  </div>

  <footer>Content</footer> <!-- my footer is static, so I show it from the beginning -->
</div>

2) Use a from https://tobiasahlin.com/spinkit/

3) Add the CSS of both elements

Css for the Spinner

.spinner {
  width: 40px;
  height: 40px;
  background-color: red;

  margin: 400px auto;
  -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  animation: sk-rotateplane 1.2s infinite ease-in-out;
}

@-webkit-keyframes sk-rotateplane {
  0% { -webkit-transform: perspective(120px) }
  50% { -webkit-transform: perspective(120px) rotateY(180deg) }
  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }
}

@keyframes sk-rotateplane {
  0% { 
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) 
  } 50% { 
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) 
  } 100% { 
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}

CSS for v-cloak

[v-cloak] .v-cloak--block {
  display: block;
}
[v-cloak] .v-cloak--inline {
  display: inline;
}
[v-cloak] .v-cloak--inlineBlock {
  display: inline-block;
}
[v-cloak] .v-cloak--hidden {
  display: none;
}
[v-cloak] .v-cloak--invisible {
  visibility: hidden;
}
.v-cloak--block,
.v-cloak--inline,
.v-cloak--inlineBlock {
  display: none;
}

Upvotes: 2

craig_h
craig_h

Reputation: 32704

Vue already has a v-cloak directive built in, you just need to add the relevant css class:

[v-cloak] {
  display: none;
}

And then apply it to your element like so:

<div v-cloak>
  {{message}}
</div>

Here's the JSFiddle: https://jsfiddle.net/2jbe82hq/

If you remove v-cloak in that fiddle you will see the mustached {{message}} before the instance has been mounted.

If you want to display a loader while you retrieve data from your server, then you combine a boolean flag with v-if to show and hide the loader:

var vm = new Vue({
  el: "#app",
  created(){
    this.$http.get('url').then(response => {
      // set loader to false
      this.loading = false;
    });
  },
  data: {
    message: 'Hello Vue!',
    loading: true
  }
});

You can then do:

<div v-if="loading">
   Loading...
</div>
<div v-else>
    {{message}}
</div>

Here's the JSFiddle for that: https://jsfiddle.net/fa70phLz/

It's also possible to use a loading class as well and then use v-bind:class to apply and remove the class based on the flag, which is useful if you want to overlay the entire page with a loader.

<div :class="{'loading': loading}"></div>

Upvotes: 67

hoogw
hoogw

Reputation: 5525

By adding v-cloak to whole page root div will work

<div id="app">
    <div v-cloak>
        Hello {{ name }}
    </div>
 </div>

<style>
   [v-cloak] {
       display: none;
  }
 </style>

Hide elements during loading using "v-cloak"

Upvotes: 2

atilkan
atilkan

Reputation: 5028

Other than v-cloak. You could also use v-text attribute. So you don't have to deal with it in CSS.

Upvotes: 0

Maniruzzaman Akash
Maniruzzaman Akash

Reputation: 5025

I'm just adding a useful things of v-cloak to add a spinner before rendering the full HTML file and thanks to Adam for his Gist - https://gist.github.com/adamwathan/3584d1904e4f4c36096f

If you want to add a spinner before loading your page, you can read this -

  • Add v-cloak inside #app.
  • Make one div v-cloak--inline which we want to show before HTML page rendered.
  • Another Div which will contain the full page with v-cloak--hidden.
  • Must have to add the css's given here.

Let's code - In Master Page,

 <div id="app">

  <div v-cloak>

   <div class="v-cloak--inline"> <!-- Parts that will be visible before compiled your HTML -->
      <i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>
      <span class="sr-only">Loading...</span>
   </div>

   <div class="v-cloak--hidden"> <!-- Parts that will be visible After compiled your HTML -->
      <!-- Rest of the contents -->
      @yield('content')
   </div>

  </div>

</div>

With adding these Extra CSS's for v-cloak.

[v-cloak] .v-cloak--block {
  display: block;
}
[v-cloak] .v-cloak--inline {
  display: inline;
}
[v-cloak] .v-cloak--inlineBlock {
  display: inline-block;
}
[v-cloak] .v-cloak--hidden {
  display: none;
}
[v-cloak] .v-cloak--invisible {
  visibility: hidden;
}
.v-cloak--block,
.v-cloak--inline,
.v-cloak--inlineBlock {
  display: none;
}

Then Before compiling the HTML file, a spinner will render. After compiled, spinner will hide. You can use this in your master page. Thus in every time when you load a new page, that will see the effects.

See the Gist - https://gist.github.com/adamwathan/3584d1904e4f4c36096f

Upvotes: 7

William Carron
William Carron

Reputation: 410

I would hesitate to mix jQuery in Vue.js code for the reason that Vue.js provides a suite of tools that generally eliminate the need for jQuery.

Since you are loading the page and displaying a progress bar, you can take advantage of the Vue.js lifecycle hooks: [created, mounted, beforeUpdate, updated, etc. etc.].

What I would do is to create a small div containing the progress bar, and then an adjacent element with the actual content to display after load. Set a data on the component instance, and then use the lifecycle hooks to toggle the display.

<div v-if='loading'>
  <div class='progress-bar'>
  <!-- Loading bar markup here -->
  </div>
</div>

<div v-else class='content'>
  <table>
  <!-- Content -->
    <tr v-for="log in logs">
      <td>{{log.timestamp}}</td>
      <td>{{log.event}}</td>
    </tr>

  </table>
</div>

Then in the script part of the template:

export default {
  data() {
    return: {
      loading: true,
      logs: [],
      message: ''
    };
  },
  methods: {
    fetchLogs() {
      // using vue-resource / ajax library like axios
      this.$http.get('/api/logs').then((logs) => {
        if (logs) {
          this.logs = logs;
          setTimeout(() => {
            this.loading = false;
          }, 1000);
        } else {
          this.message = 'No logs to display';
          this.loading = false;
        }
      });
    }
  },
  mounted() {
    this.fetchLogs();
  }
};

Using this method, you can also bind classes to the elements to create smooth CSS transitions.

Upvotes: 1

Related Questions