9

Algorithm:

  1. I find the difference between two arrays of objects (what is new, what is deleted, renamed, etc). Differences are stored in data[1-5]
  2. Based on #1 I prepare a text summary (objects of text) for a modal to notify user what difference has been found. This summary is stored in someDataToShowInModal.
  3. When the difference is found the modal with the summary must be shown to a user. User should accept (click OK) or decline (click CANCEL) to apply changes.
  4. QUESTION: How to wait until user clicks modal's OK or CANCEL button?

    In the code I show two possible solutions, but do not know how to implement them:

    1. Wrap modal into Promise.

    2. Use state.doSave and somehow wait until it is changed by myModalComponent.

  5. Apply changes if user clicks OK.

Below is pseudo-code which shows the logic I try to implement:

state.js

modalTextSummary = {}

action.js

async myAction ({ state }) {
  let modalClosed
  let someDataToShowInModal = {}

  let data1 = []
  let data2 = []
  let data3 = []
  let data4 = []
  let data5 = []

  // #1. Push difference to "data[1-5]"
  data1.push(xyz1)
  data2.push(xyz2)
  data3.push(xyz3)
  data4.push(xyz4)
  data5.push(xyz5)

  // #2. Based on data[1-5] prepare "someDataToShowInModal"
  someDataToShowInModal = {xyz}

  // #3. We change "state.modalTextSummary" and by this we open
  // a Modal (because "myModalCompont" has "watch: {modalTextSummary}")
  state.modalTextSummary = someDataToShowInModal

  // #4. HOW TO WAIT UNTIL USER CLICKS Modal's "OK" or "CANCEL"?

  // v1:
  // something like...
  modalClosed = await myModalComponent

  // v2:
  // I can add "state.doSave = ''" which can be
  // set by "myModalComponent" to either 'OK' or 'CANCEL', but how
  // in this case I can wait for state changes?
  modalClosed = await state.doSave !== ''

  // #5. Apply changes
  if (modalCloses === 'OK') {
    // ... code which handles data from data[1-5]
  }
}

myModalComponent.js

<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState([
      'modalTextSummary'
    ])
  },

  watch: {
    modalTextSummary: function () {
      if (this.modalTextSummary !== {}) {
        // Here I show bootstrap-vue modal
      }
    }
  }
}
</script>

I know how to call a function once a modal is closed by OK button, but in this case it is necessary to temporary save data[1-5] in vuex in order to pick them again inside a called function and I want to avoid it using a simpler approach.

TitanFighter
  • 4,582
  • 3
  • 45
  • 73
  • Due to JavaScript's async nature, you won't really be able to "wait" until the modal is closed; you would have to either implements callbacks or some `Promise`. If you're using modern JS, you may try using `await`, though, behind the scenes, that will use a `Promise`. – Haroldo_OK Feb 05 '19 at 11:37
  • Yes, I know. This is the reason you can find `async/await` in my code. I will clarify it. Thanks. – TitanFighter Feb 05 '19 at 11:41
  • I see... I can't write the sample code right now, but to use a `Promise`, you would need to wrap the capture of the click events of the OK/CANCEL buttons inside a `Promise`, and the click events themselves would need to call the `resolve` function. – Haroldo_OK Feb 05 '19 at 12:32
  • This is similar to what I'm talking about, though instead of capturing events from some emitter, you would capture events from the buttons: https://stackoverflow.com/a/29933215/679240 – Haroldo_OK Feb 05 '19 at 12:38

3 Answers3

5

I want to offer you to make some refactor :)

1) Store`s actions SHOULD NOT know about interface (server-side-rendering issues, ...) - bad practice.

2) It is better to store modal's data in modal's parent component. Here is an example: https://jsfiddle.net/yyx990803/70yyx8z2/

Then you'll be able to do something like this (pseudocode):

<my-modal
    v-if="showModal"
    @cancel="processModalCancel"
    @submit="processModalSubmit"
    >
    <!-- mutating-manipulations with modalData -->
</my-modal>

---

openModal() {
    this.showModal = true;
    this.modalData = store.dispatch('prepareDataToShowInModal'); // myAction, first part
},
processModalCancel() {
    this.showModal = false;
    this.modalData = null;
},
processModalSubmit() {
    this.showModal = false;
    store.dispatch('saveModalData', this.modalData); // myAction, second part
    this.modalData = null;
},
gleam
  • 861
  • 6
  • 12
  • 1. I do not need SSR, at least in the near future. 2. It seems you missed the line `// ... code which handles data from data[1-5]`. The key piece here is `data[1-5]` (ie in the 1st part I find a difference, then I show this difference to a user, and in the 2nd part I handle data from data1, data2, etc.). As I can see, in case of using your code, I need to store temporary `data[1-5]` somewhere at the 1st step and get access at the 2nd step. – TitanFighter Feb 05 '19 at 15:31
  • `store.dispatch('saveModalData', this.modalData);` handles your data[]. You can return needed values from `prepareDataToShowInModal` -> store it in `this.modalData` -> and save to store by `saveModalData`. – gleam Feb 05 '19 at 16:02
0

If you are using bsModal you have to write separate function bindings for both buttons,until user click out either button or click outside the modal it will keep open.so put the remainder of the function inside those close or confirm buttons click events

<button (click)="confirm()"></button>
<button (click)="Cancel()"></button>
import { BsModalRef} from 'ngx-bootstrap';
constructor(){public modalRef: BsModalRef}

confirm(){
//do anything 
this.modalRef.close();
}

Cancel(){
//do anything
this.modalRef.close();
}
Heshan
  • 772
  • 8
  • 22
  • My question about VueJs, not about Angular (you can be downvoted for such answer here). However I understand what you try to show me, but this does not solve my question. – TitanFighter Feb 08 '19 at 12:48
0

Found the way how to implement it via v2 thanks to this post.

gleam's answer is shorter and cleaner, so you should prefer his solution. My solution below is posted just for educational purpose.

state.js

modalTextSummary = {}
doSave = false

action.js

// My 'store' file is at the root. Where is yours - I do not know :)
import store from '@/store'

async myAction ({ state }) {
  state.doSave = false

  let modalClosed
  let someDataToShowInModal = {}

  let data1 = []
  let data2 = []
  let data3 = []
  let data4 = []
  let data5 = []

  // #1. Push difference to "data[1-5]"
  data1.push(xyz1)
  data2.push(xyz2)
  data3.push(xyz3)
  data4.push(xyz4)
  data5.push(xyz5)

  // #2. Based on data[1-5] prepare "someDataToShowInModal"
  someDataToShowInModal = {xyz}

  // #3. We change "state.modalTextSummary" and by this we open
  // a Modal (because "myModalCompont" has "watch: {modalTextSummary}")
  state.modalTextSummary = someDataToShowInModal

  // #4. Wait until user closes modal or clicks OK
  const unwatch = store.watch(
    (state) => state.doSave,
    (value) => {

      // #5. Apply changes
      if (value === true) {
        unwatch()
        // ... code which handles data from data[1-5]

      } else if (value === 'unwatch') {
        unwatch()
      }
    }
  )
}

Why do we need to unwatch()?

In my case myAction is called by "someButton" (example name) and as a result opens modal. If we open/close modal 5 times wihout clicking on OK button, we will watch state.doSave also 5 times and when after the 6th opening the OK button is clicked, data[1-5] will be processed 6 times. So, if modal is closed without clicking OK button, we need to unwatch state.doSave.

myModalComponent.js

<template>
  <b-modal
    ref="myModal"
    ok-title="OK"
    cancel-title="Close"
    @keydown.native.enter="closeModalAndSave"
    @ok="closeModalAndSave"
    @hide="closeModalAndUnwatch"
  >
    {{ modalTextSummary }}
  </b-modal>
</template>

<script>
import { mapState, mapMutations } from 'vuex'

export default {
  computed: {
    ...mapState([
      'modalTextSummary'
    ])
  },

  watch: {
    modalTextSummary: function () {
      if (this.modalTextSummary !== {}) {
        this.$refs.myModal.show()
      }
    }
  },

  methods: {
    ...mapMutations('media', [
      'doSave'
    ]),

    closeModalAndUnwatch (bvEvent) {
      // "this.$refs.myModal.hide()" fires @hide with "bvEvent.trigger === null".
      // We need to 'unwatch' just when bvEvent.trigger === 'cancel' or 'backdrop',
      // or 'header-close', ie not null.
      if (bvEvent.trigger !== null) {
        this.doSave('unwatch')
      }
    },

    closeModalAndSave () {
      this.doSave(true)
      this.$refs.myModal.hide()
    }
  } // End of "methods"
}
</script>
TitanFighter
  • 4,582
  • 3
  • 45
  • 73