Join the Stack Overflow Community
Stack Overflow is a community of 6.4 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Simple Todo-App. Please excuse my ignorance for making a rather basic question.

But how would you go about and edit a certain item on an array?

Normally I would try to bind the value of my input to a new property on my data object and then assign this new property to the old property on click throuch Vue's two way databinding.

Like this: http://jsfiddle.net/z7960up7/

Well in my case I use the v-repeat directive, which loops through my data array but I can't use the v-model directive to use the two way databinding, because the values of the properties get corrupted if I do so. (See here: http://jsfiddle.net/doL46etq/2/)

And now I wonder, how I would go about updating my array of tasks:

My idea is to pass the VueObject (this) through my method on click, and then define the index on my event handler and then updating the tasks array, using the index, like this:

HTML:

<input v-el="editInputField" type="text" value="{{ task.body }}" v-on="keyup: doneEdit(this) | key 'enter'"/>

<button v-on="click: editTask(this)">
     Edit
</button>

JS:

methods: {
    editTask: function (task) {
        var taskIndex = this.tasks.indexOf(task.task);

        this.tasks[taskIndex] = {
            'body': document.querySelector('input').value,
            'completed': false
        };

        console.log(task.task.body);
    },
}

Here is my fiddle about it:

http://jsfiddle.net/doL46etq/3/

But the data object is not updated at all and I wonder how I would go about it and update it.

What is the best way to edit an element on the array, using Vue?

Edit: An easy way, would just be to delete the element, and add the new to the array, using the push method, but I really want just to update the element, because I like to keep the dataobject in sync with my backend.

share|improve this question

The short answer: Use a component in an extended constructor, then pass the index to that component in HTML as property and use computed properties to link back and forth to your data.

But don't be satisfied with just the short answer. Here is the long one:

Situation: I am using your JSFiddle as base for this answer.

in HTML you have:

<td>{{ task.body }}</td>
                <td>
                    <div>
                        <input v-el="editInputField" type="text" value="{{ task.body }}" v-on="keyup: doneEdit(this) | key 'enter'" v-model="newEdit"/>
                    </div>
                </td>
                <td>
                    <button v-on="click: editTask(this)" class="mdl-button mdl-js-button mdl-button--icon"> <i class="material-icons">create</i>

                    </button>
                </td>

We want to replace this code with the component. Using this component allows us to identify the index/row we are working on in your set of data.

<td v-component="listitem" index="{{$index}}"></td>

Next step: defining the component.

In order not to cloud our instance with the component, we will create a separate constructor for the vue object, so we can assign the new element to our new object.

This is done using extend.

window.newVue =  Vue.extend({
components:
{
    'listitem': {

        props: ['index'],

        computed: {
        // both get and set
        body: {
          get: function () {

            return this.$parent.tasks[this.index].body;
          },
          set: function (v) {
            this.$parent.tasks[this.index].body = v
          }
        }
      },
        template: '<td>{{ body }}</td><td><div><input type="text" v-model="body" value="{{ body }}"/></div></td><td></td>',
    }
}

});

Since we can't define our data properly using an extend, we'll just assume the data already exists while writing the component.

Step 3: defining the actual data: Instead of using Vue as our object constructor, we'll now use our newly created instantiator.

new newVue({
el: '#todoapp',

data: {
    tasks: [{
        'body': 'Eat breakfast',
        'completed': false
    }, {
        'body': 'Drink milk',
        'completed': false
    }, {
        'body': 'Go to the store',
        'completed': false
    }],

    newTask: '',
},

});

That's it!

In case you couldn't follow what happened: Here's the Fiddle!

PS: More information about the working of these code can be found on vuejs.org

share|improve this answer
    
Thank you Sangun. Though it will take me some time to work through your answer, as I haven't diven into vue components yet. Though I was wondering, if you could explain, how the edit/update has been done here, I guess without the use of components: github.com/tastejs/todomvc/blob/gh-pages/examples/vue/js/app‌​.js – LoveAndHappiness Aug 9 '15 at 12:29
    
Also, when would be the time to fire an Ajax Request in your jsfiddle? – LoveAndHappiness Aug 9 '15 at 12:31
1  
That depends what you want to do with the Ajax Request. If you wish to update the information in a database, then firing from the Setter on the component would be the best (However, keep in mind that an request would be made whenever a single character changes, so you might want to put out a delay and buffer all changes!) If in case you wish to update the data, I'd suggest adding a new property rather than defining data. This will allow you to edit the value through a property as well. This means that if you'd use the v-repeat item, you can use {{item.property}} to update the variable. – Sangun Aug 9 '15 at 13:36
    
Thank you Sangun, I've accepted your answer. Yet I might reinstate the question requesting a solution without "components", since I (maybe falsely) believe, that components are there to help make basic tasks easier, and not to make basic tasks possible. I'll work through a couple of times through the todomvc.com app and through your solution and will probably be smarter till then. Thank you very much. – LoveAndHappiness Aug 9 '15 at 18:55
1  
You can do the same without components using the router addon made by the same person Vue.JS made. However, this plugin is unexplored by me and I am unable to give you a satisfying answer with that regard. If you wish to have a solution without components (which works just as well, in my opinion), you may wish to work with github.com/vuejs/vue-router. Hope that helps! – Sangun Aug 10 '15 at 13:12
up vote 1 down vote accepted

Actually the simplest way to update an array item, is to two-way bind the task body with the v-model directive.

Example:

http://jsfiddle.net/z7960up7/2/

<div id="demo">
    {{ message }}

    <div class="edit">
        <input type="text" v-model="message">         
        <button v-on="click: editMessage">Edit</button>
    </div>        
    <pre>{{ $data | json }}</pre>
</div>

And fire an event whenever you blur out of the input box or the edit button is hit.

Also hide the input field with css, by using the v-class directive.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.