0

I have input which I use to filter my array of objects in Vue. I'm using Salvattore to build a grid of my filtered elements, but it doesn't work too well. I think I have to call rescanMediaQueries(); function after my v-model changes but can't figure how.

Here is my Vue instance:

var articlesVM = new Vue({
      el: '#search',
      data: {
        articles: [],
        searchInput: null
      },
      ready: function() {

          this.$http.get('posts').then(function (response) {
            this.articles = response.body;
          });

      }
});

And here is how I have built my search

<div class="container" id="search">
  <div class="input-field col s6 m4">
    <input v-model="searchInput" class="center-align" id="searchInput" type="text" >
    <label class="center-align" for="searchInput"> search... </label>
  </div>

  <div id="search-grid" v-show="searchInput" data-columns>
    <article v-for="article in articles | filterBy searchInput">
        <div class="card">
            <div class="card-image" v-if="article.media" v-html="article.media"></div>
            <div class="card-content">
                <h2 class="card-title center-align">
                    <a v-bind:href="article.link">{{ article.title }}</a>
                   </h2>
                   <div class="card-excerpt" v-html="article.excerpt"></div>
            </div>
            <div class="card-action">
                <a v-bind:href="article.link"><?php _e('Read More', 'sage'); ?></a>
            </div>
        </div>
    </article>
  </div>

I did get the grid system working by adding watch option to my Vue, but every time I wrote something to my input and then erase it my filterBy method wouldn't work at all. It didn't populate any data even if I tried to retype the same keyword as earlier. Here is the watch option I used:

watch: {
   searchInput: function (){
       salvattore.rescanMediaQueries();
   }
}
Eljas
  • 1,187
  • 4
  • 17
  • 37

1 Answers1

1

I think your problem is with the scoping of this in your success handler for http. Your articles object in Vue component is not getting any values from your http.get(..) success handler.

Inside your ready function, your http success handler should be as follows:

this.$http.get('posts').then(response => {
  this.articles = response.body;  // 'this' belongs to outside scope
});`

Alternatively you can also do:

var self = this;  // self points to 'this' of Vue component
this.$http.get('posts').then(response => {
  self.articles = response.body;  // 'self' points to 'this' of outside scope
});`

Another similar issue: https://stackoverflow.com/a/40090728/654825

One more thing - it is preferable to define data as a function, as follows:

var articlesVM = new Vue({
  el: '#search',
  data: function() {
    return {
      articles: [],
      searchInput: null
    }
  },
  ...
}

This ensures that your articles object is unique to this instance of the component (when you use the same component at multiple places within your app).

Edited after comment #1

The following code seems to work alright, the watch function works flawlessly:

var vm = new Vue({
    el: '#search',
    template: `<input v-model="searchInput" class="center-align" id="searchInput" type="text" >`,
    data: {
        searchInput: ""
    },
    watch: {
        searchInput: function() {
            console.log("searchInput changed to " + this.searchInput);
        }
    }
})

The input in template is an exact copy of your version - I have even set the id along with v-model, though I do not see the reason to set an id

Vue.js version: 2.0.3

I am unable to see any further, based on details in the question. Can you check if your code matches with the one above and see if you can get the console debugging messages?

Edited after comment #4, #5

Here is another thought which you need to verify:

  • Role of vue.js: Render the DOM
  • Role of salvattore plugin: Make the DOM layouts using CSS only

Assuming the above is true for salvattore plugin, and hopefully it does not mess with vue.js observers / getters / setters, then you can do the following: provide a time delay of about 50 ms so that vue.js completes the rendering, and then call the salvattore plugin to perform the layouts.

So your watch function needs to be as follows:

watch: {
   searchInput: function (){
       setTimeout(function(){
           salvattore.rescanMediaQueries();
       }, 50);
   }
}

Alternatively you may also use Vue.nexttick() as follows:

Vue.nextTick(function () {
    // DOM updated
})

The nextTick is documented here: https://vuejs.org/api/#Vue-nextTick

I do not know if you may need to provide a little bit of extra time for salvattore plugin to start the layouts, but one of the above should work out.

Let me know if it works!

Community
  • 1
  • 1
Mani
  • 23,635
  • 6
  • 67
  • 54
  • Thanks for the tips, but I don't think my problem was because these. I think it has something to do with my watch option. Because I can get all the data just fine and without watch option my search works but elements doesn't get organized correctly by Salvattore. After I add my watch function to searchInput, elements organizes just fine but I can do only one search because after I erase search string and write it again it doesn't filter (or show) any data anymore. – Eljas Oct 20 '16 at 11:07
  • Hmm, this is interesting. I see the watch works so it must be issue in Salvattore js plugin.. I made a filter which computed new array based on searchInput and it populates correctly but still after calling salvattore.rescanMediaQueries(); in my watch it doesn't manipulate my DOM anymore (after the first search). (So I can see that data is computed correctly, but DOM doesn't update after salvattore.rescanMediaQueries(); – Eljas Oct 20 '16 at 12:11
  • I do not know the role of `salvattore` plugin, but it is not getting any local values or context in your above code. Should you not pass some values like `this.articles` or `this.searchInput` to it, so that it gets something to work on? – Mani Oct 20 '16 at 12:18
  • https://github.com/rnmp/salvattore It should recreate columns after rescanMediaQueries(); Nothing to do with Vue data, because I append my data to DOM via Vue and after that I should recreate columns so they take correct form on my app. Does this make any sense? – Eljas Oct 20 '16 at 12:23
  • Yes, it does, and I also just checked the plugin webpage. I assume the *salvattore* plugin wants to change the DOM layouts dynamically, while *vue.js* has full control over the DOM rendering. So I can see some conflicts happening. Unfortunately I cannot think of a solution as I do not know anything about this plugin or how it modifies DOM layouts. Lets wait for more responses from those who use both vue.js and the layout plugin. – Mani Oct 20 '16 at 12:26
  • Here is another thought during my evening walk here - you may just have to wait for vue.js to render the columns, provide some time delay and then allow salvattore plugin to do its work. I have edited my answer to provide some more details, especially on `Vue.nextTick()`. Hope it works this time! – Mani Oct 20 '16 at 13:22
  • Vue.nextTick() and creating new filtered and computed array and just v-for that to DOM. In watch I used Vue.nextTick(); – Eljas Oct 23 '16 at 11:35
  • Good to hear that it worked out using `nextTick`. By the way, you can also use `this.$nextTick` inside your component, if you are using ES6 modules. – Mani Oct 23 '16 at 11:59