Search code examples
javascriptjqueryvue.jsnetflix-conductor

Where should I store the JSON retrieved from a GET request so that I have access to it in a v-for directive?


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!


Solution

  • 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.