0

I am working on a Vue.js app. I am determined to understand the Composition API. However, the syntax throws me off a bit. I feel like I'm creating closures inside of closures, which leads me to believe I'm doing it wrong. I'm trying to set whether a button is enabled/disabled when my component loads. To demonstrate, I've created this fiddle, which includes the following:

example

myApp.component("banner", 
  { 
    template: '<button :disabled="isDisabled">generate</button>',
    setup(props) {
      let isDisabled = true;
      function initializeButton() {
        fetch('<some-url>')
          .then(res => { isDisabled = !res.ok; } )
        ;
      }
      initializeButton();
      
      return {
        isDisabled
      };
    }
  }
);

However, this does not work. I've narrowed it down to the fact that isDisabled is not "accessible" in the then part of the promise. However, I'm unsure how to actually set isDisabled in then. Plus, I would love to put the initializeButton outside of the setup method, even that, I couldn't do though. I appreciate anyone's help who can help me out.

Thank you.

Dev
  • 921
  • 4
  • 14
  • 31
  • use an arrow function to capture the current context. `const initializeButton = () => { ... }` – Pierre Said Sep 14 '20 at 14:02
  • @PierreSaid Thanks for your response. However, that didn't work. With an arrow function, `this` is `undefined`. If I do `console.log(this);` inside of the callback, I see `undefined`. `console.log(isDisabled);` prints `true` as desired. However, even if I manually enter `isDisabled = false;` *in* the callback, the button state doesn't change. Notably, if I manually set `isDisabled` to `false` outside of the `fetch` call, the button becomes enabled as desired. For that reason, I know a) the data-binding in my template is correct. b) I'm not setting it properly within `fetch`. What am I missing? – Dev Sep 15 '20 at 13:15

2 Answers2

0

To have an asynchronous setup method you need to use suspense.

@vue-composition / How can I use asynchronous methods in setup()

Example :

Vue.productionTip = false;

const Todo = {
  async setup(props) {
    const todo = await fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(r => r.json());
    return {
      todo
    }
  },
  template: `
    <div>
      Todo : {{ todo.title }}
    </div>
  `
};

const vm = Vue.createApp({
  components: {
    Todo
  }
}).mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app">
  <Suspense>
    <template #default>
      <Todo />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</div>

Also isDisabled is not reactive. You need to use ref to make this varible reactive.

basic example for reactivity with ref:

Vue.productionTip = false;

const vm = Vue.createApp({
  setup(props) {
    const count = Vue.ref(0);
    const inc = () => {
      count.value += 1;
    }
    return {
      count,
      inc
    }
  }
}).mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app">
  Count : {{ count }}
  <button @click="inc">+1</button>
</div>
Pierre Said
  • 3,660
  • 1
  • 16
  • 28
-2

Since initializeButton is an asynchronous operation you should use the await syntax (https://javascript.info/async-await)

async setup() {
  function async initializeButton() {
    return fetch('<some-url>').then(res => { isDisabled = !res.ok; } );
  }
  await initializeButton();
}

Bruno Francisco
  • 3,841
  • 4
  • 31
  • 61