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

How do you add a visual indicator such as a spinner and a tick when you save a new task in the example below.

Basically when I click the save button the following should happen:

  1. Save button should become invisible

  2. Cancel icon should become invisible, a spinner should appear instead to indicate something is happening

  3. A tick icon should briefly appear if successfully saved and then disappear before displaying the delete icon.

The v-show that i'm using doesn't seem to work when I try to replicate a sleep effect to display the spinner when the updateTask() method is fired?

<div class="container" id="el">

    <div class="row">
           <div class="col-md-10"><h1>Tasks</h1></div>
           <div class="col-md-2">
              <button id="" class="btn btn-primary pull-right" type="button" v-on:click="createTask()">
                  Add New
              </button>
           </div>
    </div>

    <div class="row" >
              <div class="col-sm-10 col-md-8">
                <table class="table table-striped">
                  <tr class="row" v-for="task in tasks">
                    <td class="col-sm-5">
                      <div class="form-group">
                        <label class="sr-only" for="name">
                          Name</label>
                          <input v-if="editKey == task.id" name="name" type="text" class="form-control"   v-model="task.name">

                          <span v-else v-on:click="editTask(task)">{{ task.name }}</span>
                         </div>
                    </td>
                    <td class="col-sm-5">
                      <div class="form-group">
                        <label class="sr-only" for="date">
                          Date</label>
                         <input v-if="editKey == task.id" name="date" type="text" class="form-control date-picker"  v-pikaday="task.date">
                         <span v-else v-on:click="editTask(task)">{{ task.date }}</span>

                      </div>
                    </td>
                    <td class="col-sm-2">
                      <ul class="list-inline">
                        <li v-if="editKey == task.id" >
                          <button class="btn btn-success btn-sm" type="button" v-on:click="updateTask(task)" v-show="!loading">
                             Save                               
                          </button>
                         </li>
                        <li v-if="editKey == task.id ">
                           <span v-show="!loading"><i class="fa fa-times text-danger" v-on:click="cancelEdit(task)" title="Cancel"></i></span>
                           <span v-show="loading"> <i class="fa fa-spinner"></i></span>
                        </li>
                        <li v-if="editKey !== task.id">
                           <i class="fa fa-trash-o text-muted" v-on:click="removeTask(task)" title="Delete"></i>
                        </li>
                        <li v-if="editKey !== task.id && task.id == -1">
                           <i class="fa fa-exclamation-triangle text-warning" title="Unsaved"></i>
                        </li>
                      </ul>

                   </td>
                  </tr>
               </table>
            </div>

            <pre>{{$data | json }}</pre>

    </div>

</div>

    <script>
          Vue.directive('pikaday', {
          twoWay: true,

          bind: function () {
            var self = this
            $(this.el)
               .pikaday({
                 format: 'D MMM YYYY',
                 defaultDate: moment().toDate()
               })
               .on('change', function () {
                    self.set(this.value)
                  })
          },
          update: function (value) {
            $(this.el).val(value).trigger('change')
          },
          unbind: function () {
            $(this.el).off().pikaday('destroy')
          }
        })

        var vm = new Vue({
          el: '#el',
          data: {
              editKey: '',
              loading: false,
              beforeEditCache: {
                id: '',
                name: '',
                date: ''
              },
              editedTask: null,
              tasks: [
                {id: 1, name: 'Task A', date: '25 Dec 2015'},
                {id: 2, name: 'Task B', date: '26 Dec 2015'}
              ]

          },
          methods: {
            createTask: function() {

                // if previously we were editing a task, lets cancel the edit
                if (this.editedTask){
                  this.cancelEdit();
                }

                // add new task with id -1 to indicate it hasn't been persisted
                this.tasks.push({
                    id: -1,
                    name: '',
                    date: ''
                });  

                // set edit key
                this.editKey = -1; 

            },
            storeTask: function(task) {

                // check if mandatory field completed
                if (!task.name || !task.date) {
                  return;
                }

                // persist the task by generating valid id
                task.id = Math.floor((Math.random() * 100) + 1);

            },

            editTask: function(task) {

                // if we were previously editing a task and clicked on another to edit without saving, let cancel the edit
                if (this.editedTask){
                  this.cancelEdit();
                }

                this.setBeforeEditCache(task);
                this.editedTask = task;
                this.editKey = task.id;
            },

            updateTask: function (task) {

              // if its a new task
              if (task.id == -1){
                this.storeTask(task);
              }
              // otherwise we are editing an existing task
              else {

                if (!this.editedTask.name || !this.editedTask.date) {
                  return;
                }

                this.loading = true;

                this.sleep(3000);

                this.editedTask = null;

                this.editKey = '';

                this.loading = false;

              }

            },

            cancelEdit: function (task = null) {

                if (task && task.id == -1) {
                  this.removeTask(task);
                }
                else {

                  this.editedTask.name = this.beforeEditCache.name;
                  this.editedTask.date = this.beforeEditCache.date;

                  this.editedTask = null;

                  this.editKey = '';
                }

            },

            removeTask: function(task) {
                  this.tasks.$remove(task);
            },

            setBeforeEditCache: function(task) {
                this.beforeEditCache.id = task.id;
                this.beforeEditCache.name = task.name;
                this.beforeEditCache.date = task.date;
            },

            sleep: function(milliseconds) {
              var start = new Date().getTime();
              for (var i = 0; i < 1e7; i++) {
                if ((new Date().getTime() - start) > milliseconds){
                  break;
                }
              }
            }
          }
        })

    </script>

This is the fiddle https://jsfiddle.net/ozzii/6oe2k3py/

* UPDATE *

So I've managed to update this - see the new fiddle, to provide the functionality required. But it's a very messy solution - code is all over the place. Anyone know of a better/cleaner way to refactor and achieve the same functionality and perhaps provide a fade in/out affect to the tick when you save the element?

share|improve this question

Here is the part about the sleep effect. You can use setTimeout and use the bind function to make sure the this context inside it is the Vue component.

this.loading = true;

setTimeout((function(){ 

    this.editedTask = null;

    this.editKey = '';

    this.loading = false; 

}).bind(this), 3000);
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.