简体   繁体   中英

Ember.RSVP.hash gives 'cannot convert object to primitive value' in model hook of route

EDIT: Upon looking further, something is calling toString on the array being returned from the model hook. Hard-coded, this method works fine, but being returned like I have below, it doesn't.

TL;DR: I'm manipulating one array of data to a different "format" so that I can use the Ember Charts add on. To do this, I'm using Ember.RSVP in the model hook of my route, but I'm getting a TypeError: Cannot convert object to primitive value and don't know why.

I've been learning Ember (followed their quickstart and then super-rentals tutorial), and I am now trying to build my own project with it.

The issue that I'm having is dealing with async operations within the model hook of my route. This may be a long read, but I'm trying to be very specific. The end problem, however, is that I'm getting a TypeError: Cannot convert object to primitive value at the end of it all.

I'm using Mirage to fake a backend API, and it returns my data like this:

    {
        "data": [
            {
                "id": "May, 2017",
                "type": "month",
                "attributes": {
                    "label": "05/2017",
                    "value": 6
                }
            }, {
                "id": "April, 2017",
                "type": "month",
                "attributes": {
                    "label": "04/2017",
                    "value": 5
                }
            }, {
                "id": "March, 2017",
                "type": "month",
                "attributes": {
                    "label": "03/2017",
                    "value": 4
                }
            }, {
                "id": "February, 2017",
                "type": "month",
                "attributes": {
                    "label": "02/2017",
                    "value": 3
                }
            }, {
                "id": "January, 2017",
                "type": "month",
                "attributes": {
                    "label": "01/2017",
                    "value": 2
                }
            }, {
                "id": "December, 2016",
                "type": "month",
                "attributes": {
                    "label": "12/2016",
                    "value": 1
                }
            }
        ]
    };

I'm also using Ember Charts from Addepar ( https://github.com/Addepar/ember-charts ), which requires data that looks like this (group label is optional and I am not using it for my app, but this is taken from Addepar's docs):

[{
    "label": "Label 1",
    "group": "Group One",
    "value": 20
},
{
    "label": "Label 2",
    "group": "Group One",
    "value": 22
},
{
    "label": "Label 3",
    "group": "Group One",
    "value": 18
},
{
    "label": "Label 4",
    "group": "Group One",
    "value": 2
},
{
    "label": "Label 5",
    "group": "Group One",
    "value": 6
},
{
    "label": "Label 1",
    "group": "Group Two",
    "value": 26
},
{
    "label": "Label 2",
    "group": "Group Two",
    "value": 18
},
{
    "label": "Label 3",
    "group": "Group Two",
    "value": 150
},
{
    "label": "Label 4",
    "group": "Group Two",
    "value": 160
},
{
    "label": "Label 5",
    "group": "Group Two",
    "value": 200
},
{
    "label": "Label 1",
    "group": "Group Three",
    "value": 14
},
{
    "label": "Label 2",
    "group": "Group Three",
    "value": 31
},
{
    "label": "Label 3",
    "group": "Group Three",
    "value": 44
},
{
    "label": "Label 4",
    "group": "Group Three",
    "value": 30
},
{
    "label": "Label 5",
    "group": "Group Three",
    "value": 62
},
{
    "label": "Label 1",
    "group": "Group Four",
    "value": 75
},
{
    "label": "Label 2",
    "group": "Group Four",
    "value": 114
},
{
    "label": "Label 3",
    "group": "Group Four",
    "value": 19
},
{
    "label": "Label 4",
    "group": "Group Four",
    "value": 129
},
{
    "label": "Label 5",
    "group": "Group Four",
    "value": 52
},
{
    "label": "Label 1",
    "group": "Group Five",
    "value": 200
},
{
    "label": "Label 2",
    "group": "Group Five",
    "value": 14
},
{
    "label": "Label 3",
    "group": "Group Five",
    "value": 31
},
{
    "label": "Label 4",
    "group": "Group Five",
    "value": 44
},
{
    "label": "Label 5",
    "group": "Group Five",
    "value": 30
}]

Basically, because I'm following the JSONAPI specs, the data I'm receiving from the API looks different than what needs to be passed into the Ember Charts Vertical Bar Graph component.

My solution so far (which isn't working) is to loop through the initial data and populate a new array that looks the way Ember Charts needs it to. As far as I understand, this can only happen asynchronously, because I have to make an API call (well, a call to my store), and then operate on the results that I get before returning the new array from the model hook.

Here's the actual code that I'm using right now:

import Ember from 'ember';

export default Ember.Route.extend({
    model() {
        var results = this.get('store').findAll('month');

        var months = results.then(function(data) {
            var monthsArray = [];

            data.content.forEach(month => {
                monthsArray.push(month.__data);
            });

            return Ember.RSVP.all(monthsArray);
        });

        return Ember.RSVP.hash({
            data: months
        });
    }
});

This gets accessed in my template like this (the component is provided by the Ember Charts addon) {{vertical-bar-chart data=model.data}} .

Like stated at the beginning, I get a TypeError: Cannot convert object to primitive value . My only lead as to where it's coming from is the fact that if I hard-code the data (in the second, correct format) and return it instead of pulling it from Mirage, the bar graph populates perfectly fine. But if I do it the way I have above, I get the error. I used the afterModel hook to console.log(resolvedModel) , and the data is there either way. Maybe that's not a good indicator though.

Anyway, I'm super green with Ember and I may be suffering from just misunderstanding all of this stuff. Any help is appreciated. Sorry for the long post.

EDIT: the answer still lies with JSON.parse(JSON.stringify(data)) , but I updated with kumkanillam's much simpler approach returning the data.

It turned out the error was coming from Array.toString being called on the data being returned from the model hook. It worked fine on a hard-coded array of objects, but not when returned via RSVP as I have in my question.

Upon inspecting both, it turned out, when using Ember.RSVP, the objects in the returned array lacked a toString method on them, somehow... I tried solving this via setting the prototype with Object.create(), but didn't work.

Bottom line is, and I'm still not 100% clear, I thought maybe Ember was turning my array into something else (an Ember Object?), not just a plain JS array of objects. So I ended up converting the returned data into a plain JS object again, via this answer.

Route now looks like this:

    model() {
    // Fetch month data from store
    return this.get('store').findAll('month').then(data => {
        var monthsArray = [];
        data.content.forEach(month => {
            monthsArray.push(month.__data);
        });

        // Must convert into normal JS object to avoid TypeError: cannot convert object into primitive value
        return JSON.parse(JSON.stringify(monthsArray));
    });
}

The JSON.parse(JSON.stringify(data)) basically turns the data into a string and then converts it back into a JS object. It feels a little dirty, but after 5 hours of working on this same thing, it's the only thing that worked!

Why do you need RSVP.all and RSVP.hash ?. I am missing something from your requirement. May be you can give the below code one attempt,

model() {
        return this.get('store').findAll('month').then(data => {
            var monthsArray = [];
            data.content.forEach(month => {
                monthsArray.push(month.__data);
            });
            return monthsArray;
        });
    }

and temlate you need to access it like model

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM