简体   繁体   中英

Can't access property from object in Javascript

EDIT: Re-structured question, cleaer, and cleaner:

I have a data object from Sequelize that is sent by node-express:

{
    "page": 0,
    "limit": 10,
    "total": 4,
    "data": [
        {
            "id": 1,
            "title": "movies",
            "isActive": true,
            "createdAt": "2020-05-30T19:26:04.000Z",
            "updatedAt": "2020-05-30T19:26:04.000Z",
            "questions": [
                {
                    "questionsCount": 4
                }
            ]
        }
    ]
}

The BIG question is, how do I get the value of questionsCount ? The PROBLEM is, I just can't extract it, these two methods give me undefined result:

  1. category.questions[0].questionsCount
  2. category.questions[0]['questionsCount']

I WAS ABLE to get it using toJSON() (From Sequelize lib I think), like so:

category.questions[0].toJSON().questionsCount

But I'd like to know the answer to the question, or at least a clear explanation of why do I have to use toJSON() just to get the questionsCount ?


More context:

I have this GET in my controller:

exports.getCategories = (req, res) => {
  const page = myUtil.parser.tryParseInt(req.query.page, 0)
  const limit = myUtil.parser.tryParseInt(req.query.limit, 10)

  db.Category.findAndCountAll({
    where: {},
    include: [
      {
        model: db.Question,
        as: "questions",
        attributes: [[db.Sequelize.fn('COUNT', 'id'), 'questionsCount']]
      }
    ],
    offset: limit * page,
    limit: limit,
    order: [["id", "ASC"]],
  })
    .then(data => {

      data.rows.forEach(function(category) {
        console.log("------ May 31 ----> " + JSON.stringify(category.questions[0]) + " -->" + category.questions[0].hasOwnProperty('questionsCount'))
        console.log(JSON.stringify(category))
        console.log(category.questions[0].toJSON().questionsCount)
      })

      res.json(myUtil.response.paging(data, page, limit))
    })
    .catch(err => {
      console.log("Error get categories: " + err.message)
      res.status(500).send({
        message: "An error has occured while retrieving data."
      })
    })
}

I loop through the data.rows to get each category object.

The console.log outputs are:

------ May 31 ----> {"questionsCount":4} -->false

{"id":1,"title":"movies","isActive":true,"createdAt":"2020-05-30T19:26:04.000Z","updatedAt":"2020-05-30T19:26:04.000Z","questions":[{"questionsCount":4}]}

4

try this

category.data[0].questions[0].questionCount

the reason why you have to use toJSON is because it's sometimes it is used to customise the stringification behavior. like doing some calculation before assinging the value to the object that will be returned, so it is most likley been used here to calculate the "numb of questions and then return an object with the property questionscount and the number calculated so the object you retreived more or less looks like this

var cathegory = {
  data: 'data',
  questions:[{
    // some calulation here to get the questionsCount 
      result=4,
    toJSON () {
      return  {"questionsCount":this.result}       
    } 
  }
 ]
};

console.log(cathegory.questions[0].toJSON().questionsCount) //4
console.log(JSON.stringify(cathegory))  // {"data":"data","questions":[{"questionsCount":4}]}
console.log("------ May 31 ----> " + JSON.stringify(cathegory.questions[0]) + " -->" + cathegory.questions[0].hasOwnProperty('questionsCount')) //false

try to do so category.data[0].questions.questionCount

As mentioned by others already, you need category.data[0].questions[0].questionCount .

Let me add to that by showing you why. Look at your object, I annotated it with how each part would be accessed:

category = { // category
    "page": 0,
    "limit": 10,
    "total": 2,
    "data": [ // category.data
        { // category.data[0]
            "id": 1,
            "title": "movies",
            "createdAt": "2020-05-30T19:26:04.000Z",
            "updatedAt": "2020-05-30T19:26:04.000Z",
            "questions": [ // category.data[0].questions
                { // category.data[0].questions[0]
                    "questionCount": 2 // category.data[0].questions[0].questionCount
                }
            ],
            "questionsCount": "newValue here!"
        }
    ]
}

https://github.com/sequelize/sequelize/blob/master/docs/manual/core-concepts/model-querying-finders.md

By default, the results of all finder methods are instances of the model class (as opposed to being just plain JavaScript objects). This means that after the database returns the results, Sequelize automatically wraps everything in proper instance objects. In a few cases, when there are too many results, this wrapping can be inefficient. To disable this wrapping and receive a plain response instead, pass { raw: true } as an option to the finder method .

(emphasis by me)
Or directly in the source code, https://github.com/sequelize/sequelize/blob/59b8a7bfa018b94ccfa6e30e1040de91d1e3d3dd/lib/model.js#L2028

@returns {Promise<{count: number, rows: Model[]}>}

So the thing is that you get an array of Model objects which you could navigate with their get() method. It's an unfortunate coincidence that you expected an array, and got an array so you thought it is "that" array. Try the {raw:true} thing, I guess it looks something like this:

db.Category.findAndCountAll({
    where: {},
    include: [
      {
        model: db.Question,
        as: "questions",
        attributes: [[db.Sequelize.fn('COUNT', 'id'), 'questionsCount']]
      }
    ],
    offset: limit * page,
    limit: limit,
    order: [["id", "ASC"]],
    raw: true                            // <--- hopefully it is this simple
  }) [...]


toJSON() is nearby too, https://github.com/sequelize/sequelize/blob/59b8a7bfa018b94ccfa6e30e1040de91d1e3d3dd/lib/model.js#L4341

 /** * Convert the instance to a JSON representation. * Proxies to calling `get` with no keys. * This means get all values gotten from the DB, and apply all custom getters. * * @see * {@link Model#get} * * @returns {object} */ toJSON() { return _.cloneDeep( this.get({ plain: true }) ); }

So it worked exactly because it did what you needed, removed the get() stuff and provided an actual JavaScript object matching your structure (POJSO? - sorry, I could not resist). I rarely use it and thus always forget, but the key background "trick" is that a bit contrary to its name, toJSON() is not expected to create the actual JSON string, but to provide a replacement object which still gets stringified by JSON.stringify() . ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON_behavior )

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