darkylmnx
darkylmnx

Reputation: 2071

Using Vue to enhance existing Multi page / server rendered / classic web app

I’v been searching for hours now and didn’t find anything close to my use case.

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<!-- server rendered -->
<div id="app">
  <h2>Freelance list</h2>
  
  <article>
    <h3>louane</h3>
    <p>
      City : <strong>courbevoie</strong>
      <br> Phone : <strong>05-36-23-51-89</strong>
    </p>
  </article>
  <article>
    <h3>indra</h3>
    <p>
      City : <strong>rheden</strong>
      <br> Phone : <strong>(354)-415-2419</strong>
    </p>
  </article>
  <article>
    <h3>angelo</h3>
    <p>
      City : <strong>montpreveyres</strong>
      <br> Phone : <strong>(883)-474-9314</strong>
    </p>
  </article>
  
  <a href="/prev-link">prev</a>
  <a href="/next-link">next</a>
</div>
<!-- server rendered -->

// fake url link, normally this would be taken from the href or something
var url = 'https://randomuser.me/api/?seed=abc&results=3&page=';
var page = 1;
var $articles = $('.articles');
var tpl = $articles.children().eq(0).clone();

$('.prev').click(function(e) {
	e.preventDefault();
  
  if (page <= 1) {
  	return
  }

  page--;
  $.getJSON(url + page)
  	.done(onReqDone);
});

$('.next').click(function(e) {
	e.preventDefault();

  page++;
  $.getJSON(url + page)
  	.done(onReqDone);
});

function onReqDone(res) {
  $articles.html('');

  res.results.forEach(function(user) {
    var $node = tpl.clone();
    $node.find('h3').text(user.name.first);
    $node.find('strong:eq(0)').text(user.location.city);
    $node.find('strong:eq(1)').text(user.phone);
    $articles.append($node);
    window.scroll(0, 0);
  });

}
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- server rendered -->
<div id="app">
  <h2>Freelance list</h2>

  <div class="articles">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <a href="/prev-link" class="prev">prev</a>
  <a href="/next-link" class="next">next</a>
</div>
<!-- server rendered -->

Any ideo on how to do it ? Here are my tries : https://jsfiddle.net/7270zft3/2/ : problem, it doesn’t remove the old dom

PS : Before anyone talks about SSR with Vue ou just doing an SPA, here’s why i cant :

Upvotes: 1

Views: 1114

Answers (3)

darkylmnx
darkylmnx

Reputation: 2071

Someone on the Vue forum found a good approach close to what was posted here but suited better my need : https://forum.vuejs.org/t/using-vue-to-enhance-existing-multi-page-server-rendered-classic-web-app/30934/20

Upvotes: -1

Roy J
Roy J

Reputation: 43881

An example of hydration. I wasn't able to get Vue to stop warning me that my generated HTML didn't match the original; it's not critical. In development, Vue will "bail and do a full render", but in production it will leave the pre-rendered. You just want to be sure they match, so that when it does update, it's what you expect.

I left jQuery in for the getJSON. Other than that, it's jQuery free.

// fake url link, normally this would be taken from the href or something
var url = 'https://randomuser.me/api/?seed=abc&results=3&page=';
var page = 1;
$.getJSON(url + page).done((res) => {
  const articles = res.results;

  new Vue({
    el: '#app',
    template: `
  <div id="app">
    <h2>Freelance list</h2>
    <div class="articles">
      <article v-for="article in articles">
        <h3>{{article.name.first}}</h3>
        <p>
          City : <strong>{{article.location.city}}</strong>
          <br> Phone : <strong>{{article.phone}}</strong>
        </p>
      </article>
    </div>
    <a href="/prev-link" class="prev" @click.prevent="--page">prev</a>
    <a href="/next-link" class="next" @click.prevent="++page">next</a>
  </div>
  `,
    data: {
      page,
      url,
      articles
    },
    methods: {
      getPage() {
        $.getJSON(this.url + this.page)
          .done((res) => {
            this.articles = res.results;
          });
      }
    },
    watch: {
      page() {
        this.getPage();
      }
    }
  });
});
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app" data-server-rendered="true">
  <h2>Freelance list</h2>
  <div class="articles">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <a href="/prev-link" class="prev">prev</a>
  <a href="/next-link" class="next">next</a>
</div>
<!-- server rendered -->

Upvotes: 1

Agney
Agney

Reputation: 19194

You can attach a DOM ref to the server rendered content and then just as in jQuery, just clear the contents to the DOM element.

You need to perform this action only once, so you can may be add checks to see if your DOM ref is empty if page === 1

new Vue({
  el: "#app",
  data: {
    users: null,
    page: 1
  },
  methods: {
    loadData: function(prev) {
      var page = this.page

      if (prev) {
        page--
      } else {
        page++
      }

      fetch('https://randomuser.me/api/?seed=abc&results=3&page=' + page)
        .then(res => res.json())
        .then(data => {
          this.$refs.serverContent.innerHTML = '';
          this.users = data.results
          this.page = page
          window.scroll(0, 0)
        })
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

article {
  margin: 8px 0;
  background: #eee;
  padding: 20px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
  <h2>Freelance list</h2>
  <div ref="serverContent">
    <article>
      <h3>louane</h3>
      <p>
        City : <strong>courbevoie</strong>
        <br> Phone : <strong>05-36-23-51-89</strong>
      </p>
    </article>
    <article>
      <h3>indra</h3>
      <p>
        City : <strong>rheden</strong>
        <br> Phone : <strong>(354)-415-2419</strong>
      </p>
    </article>
    <article>
      <h3>angelo</h3>
      <p>
        City : <strong>montpreveyres</strong>
        <br> Phone : <strong>(883)-474-9314</strong>
      </p>
    </article>
  </div>

  <!-- Vue part -->
  <!-- how to plug Vue to handle the pagination ? -->
  <article v-for="user in users">
    <h3>{{ user.name.first }}</h3>
    <p>
      City : <strong>{{ user.location.city }}</strong>
      <br> Phone : <strong>{{ user.phone }}</strong>
    </p>
  </article>
  <!-- Vue part -->

  <button v-show="page > 1" @click="loadData(true)">prev</button>
  <button @click="loadData()">next</button>
</div>
<!-- server rendered -->

But to do it Vue style, it is advised that you remove the rendering of first page from the server and let Vue handle data-fetching by itself so that Vue can use the data it stores as the source of truth instead of manipulating the DOM.

Upvotes: 1

Related Questions