1

I'm pretty new to using APIs, so please forgive me if this is really trivial.

There are two GET requests I want to use:

  • /workflow/search will allow me to get an entire list of workflows along with their data (i.e. the workflow id).
  • /workflow/{workflowId} will allow me to get a specific workflow with more details.

I currently have importedWorkflows in my data function, which is populated with what I get from the /workflow/search call. If I want to know more information about a workflow, then I would want to call /workflow/{workflowId}.

However, this has been really difficult, especially since I'm using Jquery to get the JSON data.

$.getJSON(dataURL, function(data) {
      self.importedWorkflows = data.results;
    });

Since it's all asynchronous, I really just want access to the information found with /workflow/{workflowId} whenever it's ready.

This was my attempt in making that happen: I have computed array called importedWorkflowDefinitions that goes through all the workflows found with /workflow/search and makes the /workflow/{workflowId} call for every workflow. I store the returned JSON data for every call in the computed array. Then, I use a method called generateWorkflowById() to lookup the workflow JSON in importedWorkflowDefinitions.

However...

In my Vue application, I have a table made with the v-for directive.

<tbody v-for="workflow in workflowsOnPage">
     <tr class="expandable-row" :id="workflow.workflowId">
          <td>{{ generateWorkflowById(workflow.workflowId).workflowName }}</td>
          <td>{{ workflow.workflowId }}</td>
          <td>{{ workflow.status }}</td>
          <td class="text-align-right">{{ workflow.startTime }}</td>
          <td class="text-align-right">{{ workflow.endTime }}</td>
          ...

Note: workflowsOnPage is a computed JSON array that divides importedWorkflows.

So, what essentially has been happening is that the table will render without the generateWorkflowById(workflow.workflowId).workflowName value, because the first call /workflow/search had not finished. Also, even though workflowDefinitions is computed and will change when the /workflow/search call is done, the table has already finished rendering and won't run the generateWorkflowById(..) method again.

Is there a way to render information found with /workflow/{workflowId} call onto the table without this race happening with all my data?

By the way, I'm working with Netflix Conductor, so if there are better API calls or a different way to have access to all the details pertaining to a workflow so I don't have to make these dependent API calls, please let me know!

Thank you!

Helen
  • 87,344
  • 17
  • 243
  • 314
aztheog
  • 43
  • 4
  • 1
    any reason your using jQuery for the get and vue for the render. why not axios inside of the vue component to retreive the data and use an arrow function so `this` is not bound so you can populate the view components data using `this.importedWorkFlows` – Michael Mano Aug 05 '20 at 04:34

3 Answers3

1

There's a couple of things here...

  1. Computed properties are used to return new values based on reactive properties. They should not be performing any asynchronous functions
  2. Calling methods to render parts of your template is very inefficient and can lead to further problems like infinite loops
  3. Mixing Vue and jQuery? Just say "no" ‍♂️

Here, a different approach would be to fetch all your data in the created hook of your component and show loading indicators for parts that are still coming together

export default {
  data: () => ({
    importedWorkflows: []
  }),
  computed: {
    workflowsOnPage () {
      return .... // whatever you do to importedWorkflows to "divide" them
    }
  },
  async created () {
    const res = await fetch('/workflow/search')
    const workflows = (await res.json()).results

    // assign the new array values once all fetches are resolved
    this.importedWorkflows = await Promise.all(workflows.map(async workflow => {
      const res = await fetch(`/workflow/${workflow.workflowId}`)
      const details = await res.json() // assuming this is correct
      return {
        ...workflow,
        details
      }
    }))
  }
}
<p v-if="workflowsOnPage.length === 0">Loading...</p>

<!-- snip -->

<!-- this won't render until the "workflowsOnPage" array has elements -->
<tbody v-for="workflow in workflowsOnPage">
  <tr class="expandable-row" :id="workflow.workflowId">
    <td>{{ workflow.details.workflowName }}</td>
    <td>{{ workflow.workflowId }}</td>
    <td>{{ workflow.status }}</td>
    <td class="text-align-right">{{ workflow.startTime }}</td>
    <td class="text-align-right">{{ workflow.endTime }}</td>
    <!-- etc -->
  </tr>
</tbody>

Note that I've loaded the extra details from /workflow/{workflowId} into the details property of each workflow object returned by /workflow/search.

Phil
  • 157,677
  • 23
  • 242
  • 245
  • Thank you so much, Phil! This worked for me! Right now, the console is showing me errors about me trying to read the property length of an undefined object. I guess this is because workflowsOnPage is undefined before the API call returns. Would it be better to compute workflowsOnPage in the created() hook? – aztheog Aug 05 '20 at 20:44
  • You haven't shown what that computed property does so I can't really comment other than to say it should always return an array, empty or otherwise – Phil Aug 05 '20 at 22:11
0

It's already stored in Elasticsearch. Check your elasticsearch information from: http://localhost:8080/api/admin/config

You can query data from there, much faster than conductor api and relief the load to the API that mainly used for execution. Read from ES also allow you to do various transformation easier and more efficient.

HoaPhan
  • 1,714
  • 1
  • 13
  • 35
-1

Short answer: Use vuex store, register an action that will do the get request and create a mutation that it will store the response of the request to a state variable inside the store variable and then retrieve the store variable using computed and use it to your template

LastM4N
  • 1,890
  • 1
  • 16
  • 22