Anis Souames
Anis Souames

Reputation: 344

Directive v-if doesn't work when changing tab

I'm trying to build a simple listing system that shows a list of items for different platforms, each platform is on a seperate tab . I created the tab switching logic via VueJS from scratch .

What I'm doing:

Basically I have two platforms : twitter and facebook, when user click on one of the tabs, the frontend send an ajax request to my server to fetch posts for that platform and render them via v-for .

I added a button called edit for each post, when user press it , it calls a function edit(p), where p is the current post that user want to edit .

in edit(p) I change an atrribute p.editing which using v-if shows a text area and a timepicker (I'm using flatpicker) for that post .

What's Wrong:

All this works fine when I'm on the first tab, but once I switch the tab, it stop working, after debugging I noticed that v-if is not working event p.editing is updated when edit(p) is called, here's the code :

var posts_app = new Vue({
    el: "#posts_app",
    data: {
        platforms : ['facebook','twitter'],
        current_tab: {
            'facebook' : true,
            'twitter': false
        },
        platform_posts: {
            'facebook': [],
            'twitter': []
        },
        posts: undefined,
    },
    methods:{
        showTab: function(i){
            platform = this.platforms[i]
            // UI stuff : to make the clicked tab active
            for(p in this.current_tab){
                if(p == platform){
                    this.current_tab[p] = true
                }
                else{
                    this.current_tab[p] = false
                }
            }
            // Show content by platform
            this.posts = this.platform_posts[platform]
        },
        edit: function(p){
            p.editing = true
            console.log(p)
            Vue.nextTick(function(){
                document.getElementsByClassName("dt-input")[0].flatpickr({enableTime : true});
            })
        },
        save: function(p){
            p.editing = false
        }
    },
    created(){
        self = this
        posts_loaded = false

        for(var i = 0;i < this.platforms.length; i++){
            (function(index){           
                self.$http.get('/fan/posts',{params:{platform : self.platforms[index]}}).then(function(resp){
                self.platform_posts[self.platforms[index]] = resp.body
                posts_loaded = true

            })//Promise of Ajax call
            }// Closure body
            )(i)//Closure
        }

        this.showTab(0)
    },
    delimiters: ['[[',']]']

})

and my basic html template :

 <div class = "panel-body">
        <img class = "pull-right responsive" v-bind:src = "p.image"/>
        <textarea v-if = "p.editing" class = "post-text-input" v-model = "p.text"></textarea>
        <p class = "post-text" v-if = "!p.editing">[[p.text]]</p>
        <p class = "post-source" v-if = "p.type == 'article'"> Source : [[post_source(p)]]</p>
        <p class = "post-time"><b>Scheduled on <i v-if = "!p.editing">[[p.time]] </i></b>
            <input placeholder="Choose a date and a time" class = "flatpickr dt-input" v-model = "p.time" v-if = "p.editing" />
        </p>

       </div>
       <div class = "panel-footer clearfix">
         <button class = "btn btn-danger">Delete</button>
         <button class = "btn btn-info pull-right" @click = "edit(p)" v-if = "!p.editing">Edit</button>
         <button class = "btn btn-success pull-right" @click = "save(p)" v-if = "p.editing">Save</button>

       </div>

Code explanation:

So, when a tab is clicked, showTab(index) is called where index is the number of tab, if index is 0 then we switched to facebook tab, if it's 1 then we're in the twitter tab, we send an AJAX request to get the posts for that current platform (tab) and fill it in platform_posts[current_platform], we then render them via v-for . All of this is working like a charm .

Second part, when user click on the edit button for a given post, it replace the text paragraph element with a textarea using v-model to keep track of change and update the time paragraph with an input which acts as datetime picker via flatpickr library . Basically this lib can turn any input into a datetime pickr using this line of code :

elemnt.flatpickr({config_options})

Where element is an HTML element . You can notice that I'm using Vue.nextTick, this is to make sure that the input is not hidden anymore (it shouldn't be since p.editing is updated) . All of this work like a charm when I'm on the first tab, the problem is that when I switch the tab it stop working .

Here's a gif I made to show you the error : http://imgur.com/a/QME4P

As you can see, the behaviour is very weird, it works perfectly on the twitter tab and it's weird on the facebook tab .

Upvotes: 2

Views: 648

Answers (1)

For the Name
For the Name

Reputation: 2529

It looks like you're breaking reactivity somewhere. Read about Reactivity. In short, you cannot mutate an array or add a new property to an already created object.

For example, instead of p.editing = true you can do:

this.$set(p, 'editing', true)

Another key concept is to replace an array instead of mutating an array.

I would recommend moving your get posts out of created() and into a method called by created(), as you may want to reuse it to pull posts again. Also you should set posts to an empty array instead of undefined.

Upvotes: 1

Related Questions