Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Computed object won't populate chart #170

Closed
ssuess opened this issue Aug 15, 2017 · 19 comments
Closed

Computed object won't populate chart #170

ssuess opened this issue Aug 15, 2017 · 19 comments

Comments

@ssuess
Copy link

ssuess commented Aug 15, 2017

Expected Behavior

Passing a prop array and computing a data value from it to populate a chart should work.

Actual Behavior

Empty chart.

Looking in vue console tools, I see that my computed object is present and (seemingly) valid:

actionbarlabels:Object
  datasets:Object
    data:Array[4]
      0:4
      1:1
      2:2
      3:1
  labels:Array[4]
    0:"call"
    1:"join"
    2:"attend meeting"
    3:"attend march

And my template code looks like this:
<bar-chart :data="actionbarlabels" :options="{responsive: true, maintainAspectRatio: false}"></bar-chart>

Environment

  • vue.js version: 2.3.4
  • vue-chart.js version: 2.8.2
  • npm version: 4.2.0
  • quasar version: 0.14.0
@apertureless
Copy link
Owner

Please provide a minimal jsfiddle / codepen for reproduction.

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

I don't have much experience setting up codepens, but I copied one and modified it to present the same issue (I think): https://codepen.io/ssuess/pen/zdPqGW

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

In playing around with the codepen, I found the problem for that simple example anyway: I had to add array brackets around object brackets on the data object before passing it back.

so {data} did not work, but [{data}] did....

But in my own code doing the same thing does not fix it. I am still investigating.

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

I'm stumped. If I put the EXACT SAME data into the data element, the chart will render. But if I leave it in a computed property only the chart title (label) in datasets array will render on a blank chart. See below how my computed object is identical to the data one (except for the _meta element which I assume is added by the plugin itself when a successful chart is drawn). Any ideas why this might be happening?
screen shot 2017-08-15 at 3 06 00 pm

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

Do you have any examples of a computed property being used in a chart that I could look at?

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

Found this on stackexchange which seems to describe almost exactly the same problem, but trying their solution did not work for me.

@ssuess
Copy link
Author

ssuess commented Aug 15, 2017

I have been examining this further, and it is definitely related to the prop data being passed down from the parent. It is for some reason inaccessible to the vue for the chart (although it is accessible and computing proper values as shown above in the debugger). When I copy my data to the data object and then compute THAT, it works fine. So somehow the prop with data I am passing down the chart refuses to see/use.

@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

I give up. At least chartist works, I will content myself with that for the time being. Thanks anyway.

@apertureless
Copy link
Owner

Hey.
I will try to reproduce it, and see where the problem is.
Generally it should work with computed properties. I used them in here
https://medium.com/@apertureless/wordpress-vue-and-chart-js-6b61493e289f

import { Bar } from 'vue-chartjs'
export default Bar.extend({
  props: ['labels', 'datasets'],
  computed: {
    'chartdata': function () {
      return {
        labels: this.labels,
        datasets: this.datasets
      }
    }
  },
 mounted () {
    this.renderChart(this.chartdata, 
    {responsive: true, maintainAspectRatio: false})
  }
})

@apertureless apertureless self-assigned this Aug 16, 2017
@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

Thanks, but as I said above, the problem seems not to be with computed properties per say, but with the prop (array) being passed down (even though it does pass down for my computed element in the vue dev tools). I notice in your example above your don't specify the prop as array btw, which I was under the impression you had to do for it to work in that case, no?

@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

yes, trying with your notation above fails for me, I am curious how you passed labels and datasets in that code since they are not defined as arrays?

@apertureless
Copy link
Owner

apertureless commented Aug 16, 2017

What do you mean, they are not defined as an array?

The labels and datasets are arrays.
Or you do mean the vue prop type-check?

<?php if(have_rows('datasets')): ?>
  <?php
    $datasets = [];
   ?>
<?php while ( have_rows('datasets') ) : the_row();
    $datasets[] = array(
      'label' => get_sub_field('dataset_label'),
      'backgroundColor' => get_sub_field('dataset_color'),
      'data' => explode(', ', get_sub_field('dataset_data'))
    );
  endwhile;
$labels = get_field('x_axis');
  $legend = get_field('legend');
$chartDatasets = htmlspecialchars(json_encode($datasets));
  $chartLabels = htmlspecialchars(json_encode(explode(', ', $labels)));
?>
<section class="chart-box text">
  <div class="text__wrapper">
    <bar-example
      :labels="<?= $chartLabels; ?>"
      :datasets="<?= $chartDatasets; ?>" >
    </bar-example>
  </div>
</section>
<?php endif; ?>

Well without seeing your code, parent + chart components, its hard to debug.
🙈

@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

I'm sorry, I am new to Vue and I was under the impression that one was required to define prop type as array ie props: { labels: { type: Array }} I will try without. Also, I will try to simply my code and upload it here, thanks for looking at this, I appreciate it.

@apertureless
Copy link
Owner

Oh, I see. No, this is only for type checking.
You can define the type of a prop and vue will check it and if it missmatches, it will throw an error.

If you don't define a type, the type will be any and you can pass everything into it.

You can also make own prop validation methods.

@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

I have spent the last few hours looking at this, and have pinpointed the problem, but not a solution.

If I populate the parent with a hand typed array to pass down as my prop, ie:

items: [
        {
          'itemID': '1',
          'myitem': 'something_red'
        },
        {
          'itemID': '2',
          'myitem': 'something_red'
        },
        {
          'itemID': '3',
          'myitem': 'something_blue'
        },
        {
          'itemID': '4',
          'myitem': 'something_green'
        },
        {
          'itemID': '5',
          'myitem': 'something_red'
        },
        {
          'itemID': '6',
          'myitem': 'something_blue'
        }
      ]

And then perform my computed function on that, it WILL populate my bar chart. But if my prop ("items" in this case) is an array returned from a call to my api, it will NOT populate the chart, even though the calculated data looks just fine in dev tools. So there is something about the formatting of the array that the bar chart refuses to render. I have copied the array directly from the dev tools and pasted it into the data array of the parent, and I do get eslint warnings, but only about using single quotes over doubles. Other than that, it looks identical and I can't figure out why that would make a difference to the bar render.

@apertureless
Copy link
Owner

apertureless commented Aug 16, 2017

Oh, you are working with an API ?
Well, please keep in mind that if you're using api calls with axios or fetch that they are async!

So your chart component will get created and mounted before your data arrives.

You could use a loading state.

So in your data() you define

return {
  loaded: false
}

and in your api call (if you're using axios) you could set it it to true.

axios.get(`https://api.com/my-endpoint`)
    .then(response => {
        this.rawData = response.data
        this.loaded = true
    })
    .catch(err => {
        ....
    })

And then add an v-if="loaded" to your chart component. So it will only be shown if your data is complete.

Another way would be to add a watcher and rerender the chart if your computed prop change.

You can check out the resources in the docs. There are some tutorials on how to interact with APIS

http://vue-chartjs.org/#/home?id=resources

@ssuess
Copy link
Author

ssuess commented Aug 16, 2017

OMFG! That was it!!! Thank you!!! Sorry to have put you through all of this.

@apertureless
Copy link
Owner

You're welcome! ✌️

@dizid
Copy link

dizid commented Nov 4, 2019

How can i get v-if="loaded" working with vuefire?
vuefire doesn't really make API calls but just binds to Firestore.

so, this does not work:

 firestore() {
 return {
    tweetdata: db.collection('tweets').where('screen_name', '==', this.twitter_screen_name)
    }
    this.loaded = true
  },

I have read dozens of questions. threads, tutorials etc but the computed properties loads first and that seem to be it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants