0

I have a little sandbox here with two components. A list of objects ("Groups" for the example) and a confirmation modal. The modal is triggered when a delete button is clicked. (This sandbox is extracted from another post in which I asked for different ways to send the result of the modal to the parent component, the GroupList).

Here's the modal component:

<template>
  <b-modal
    id="modalConfirmation"
    title="Confirmation"
    ok-variant="danger"
    @cancel="resolvePromise(false)"
    @ok="resolvePromise(true)"
    @close="resolvePromise(false)"
  >Are you sure you want to delete this row ?</b-modal>
</template>

<script>
export default {
  name: "ModalConfirmation",
  data() {
    return {
      group: null,
      resolvePromise: null,
      rejectPromise: null
    };
  },
  methods: {
    show(group) {
      return new Promise((resolve, reject) => {
        this.group = group;
        this.$bvModal.show("modalConfirmation");
        this.resolvePromise = resolve;
        this.rejectPromise = reject;
      });
    }
  }
};
</script>

The best solution for me was this one. However, while I understand the principle of JavaScript promises, I couldn't figure out how it works in this case. It works perfectly, but I don't like to use code that I don't understand.

In the ModalConfirmation, for the b-modal tag, these are the event that set the result of the modal. But how does vuejs / bootstrap-vue make the matching between that and the promise ?

@ok="resolvePromise(true)"
@cancel="resolvePromise(false)"
@close="resolvePromise(false)"

Because the promise constructor is called when the modal is showed and that's all...

Moreover, if I comment this

  resolvePromise: null,
  rejectPromise: null

in the modal component, it still works. Can someone explain me the flow of promise resolution in this case?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
lbris
  • 1,068
  • 11
  • 34

1 Answers1

2

The data from the template is this within Vue component methods.

Here's what happens:

  1. When show is called, the function that's passed into new Promise (the promise executor function) is called synchronously with resolve and reject functions specific to that promise: When called, they will resolve or reject the promise. The promise executor in show stores them in the Vue component data as resolvePromise and rejectPromise:

    this.resolvePromise = resolve;
    this.rejectPromise = reject;
    
  2. show returns the promise.

  3. The component is rendered, using those functions as click handlers:

    @ok="resolvePromise(true)"
    @cancel="resolvePromise(false)"
    @close="resolvePromise(false)"
    

    Those functions are available in the template just like anything else on the data object.

  4. When you click one of the buttons, it either resolves or rejects the promise show returned, via those functions.

You can see that flow in the debugger (recommended), or by adding logging statements, for instance (updated sandbox):

<template>
  <b-modal
    id="modalConfirmation"
    title="Confirmation"
    ok-variant="danger"
    @cancel="resolvePromise(false)"
    @ok="resolvePromise(true)"
    @close="resolvePromise(false)"
  >Are you sure you want to delete this row ?</b-modal>
</template>

<script>
export default {
  name: "ModalConfirmation",
  data() {
    // *** Added log
    console.log("Making data");
    return {
      group: null,
      resolvePromise: null,
      rejectPromise: null
    };
  },
  methods: {
    show(group) {
      // *** Added log
      console.log("`show` called");
      return new Promise((resolve, reject) => {
        this.group = group;
        this.$bvModal.show("modalConfirmation");
        // *** Wrapped call to `resolve` with a function doing a log statement
        this.resolvePromise = flag => {
          console.log(`Calling resolve(${flag})`);
          resolve(flag);
        };
        // *** Wrapped call to `reject` with a function doing a log statement
        this.rejectPromise = error => {
          console.log(`Calling reject(${error})`);
          reject(error);
        };
      });
    }
  }
};
</script>

The reason you were able to comment out this part of data

resolvePromise: null,
rejectPromise: null

without changing anything is that they aren't really necessary there. Specifying them right at the start prevents the data object's shape (the properties and prototype it has) from changing later when show is called, which helps JavaScript engines optimize. But as you noticed commenting them out, you don't have to do that.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I see ! The trickiest part for me is to understand how `resolvePromise(true)` can tell the promise to be resolved. I think I figured out resolvePromise() is a standard method then ? – lbris Mar 09 '20 at 08:10
  • 1
    `resolvePromise` is the property on the Vue instance that the `resolve` function associated with the promise was stored in by the promise executor. I've edited #1 above to be a bit clearer. (FWIW: My new book is out in ~June, and I go into promises in some depth in Chapter 8. :-) See my profile for links, etc.) – T.J. Crowder Mar 09 '20 at 08:18
  • 1
    That's it ! That's the thing that I didn't understand and that's why I couldn't figure out how it actually resolves the promise. Thanks ! It's really clear now ! – lbris Mar 09 '20 at 08:22
  • Last thing : You say initializing the resolvePromise and rejectPromise optimizes JavaScript engine but you also say I don't have to do that. The optimization isn't necessary ? – lbris Mar 09 '20 at 08:24
  • 2
    @lbris I think what he means is that while having them predefined isn't nesesarry. Having them there will optimize the code for the JS engine, so there's an actual benefit to predefining them instead of simply removing them. – Hiws Mar 09 '20 at 08:30
  • 2
    @lbris - What Hiws says above is basically it. The fewer times the shape of an object changes, the better it is for optimization. That said, it's a small optimization in this case (relative to the component as a whole), and it's not free (more source text, having the names in two places is a maintenance thing...). – T.J. Crowder Mar 09 '20 at 08:41
  • 1
    Okay. Thank you for your time this very clear answer ! – lbris Mar 09 '20 at 08:44