Undefined properties in Route#serialize?


#1

I’m coding a conceptual blog app using FixtureAdapter, in which I have Tag and Post models linked through a many to many relationship as follows:

App.Tag = DS.Model.extend({
    name: DS.attr('string'),
    posts: DS.hasMany('App.Post', { embedded: true})
});

App.Post = DS.Model.extend({
    // other properties
    tags: DS.hasMany('App.Tag',{
        embedded: true
    }),
    // other properties
});

Then in TagRoute#serialize I’m doing this

serialize: function(model) {

    console.log(model);                  // logs an object with the correct data
    console.log(model.get('name'));      // logs undefined
    console.log(model.get('id'));        // logs the correct id

    var i = model.get('id');
    var n = model.get('name');
    return {
        tag_id: i,
        tag_name: n
    }
}

If I go the a post, I can see my tag links, but I noticed it’s linking to ~/#/tags/undefined, however, if I open the console, I can see that the serialize is logging a model with data. If I expand model._data.attributes in console I see the name property with the value it was defined, but if I model.get('name'), it’s undefined while model.get('id') gives me the correct value.

My Router looks like this:

App.Router.map(function() {
    this.route('about');
    this.resource('posts', { path: 'blog' }, function() {
        this.route('post', { path: ':year/:month/:post_id/:title' });
    });
    this.route('tag', {path: 'tags/:tag_name' });
});

The posts.post route also has a specific serialization in PostRoute#serialize, defined as follows:

serialize: function(model) {
    var i = model.get('id');
    var m = model.get('dateAdded').getMonth() + 1; if(12 < m) { m = 1; }
    var y = model.get('dateAdded').getFullYear();
    // don't crucify me. I'm not using Em.String.dasherize due to commas and dots, and this is an experiment
    var t = model.get('title').replace(/,/gi, '').replace(/\./gi, '').replace(/ /gi, '-').toLowerCase();
    return {
        year: y, month: m, post_id: i, title: t
    }
}

However it generates the correct url.

I’m not quite sure it has anything with the hasMany since the property I’m trying to get is not in a relationship. I’m just curious because this issue doesn’t occur in the posts.post route.

Has anyone see anything like this? How did you about it? Or am I doing something terribly wrong?

Environment:

DEBUG: ------------------------------- 
DEBUG: Ember.VERSION : 1.0.0-rc.3 
DEBUG: Handlebars.VERSION : 1.0.0-rc.3 
DEBUG: jQuery.VERSION : 1.9.1 
DEBUG: ------------------------------- 

#2

@tsantos83 I don’t have much experience with many-to-many relationship and the fixture adapter.

Shall we try to put together a jsFiddle to ease out the debugging?


#3

Fiddle is up http://jsfiddle.net/schawaska/dDWdv/

Funny thing is that after a couple of changes, If I click a tag, my template doesn’t load and I get:

Uncaught TypeError: Object App.Tag:ember439:3 has no method ’addArrayObserver’

But if I click go back and click the same tag it loads perfectly.

I’m thinking it might be related to how I declared embedded, but I can’t be sure at this point


#4

I’ve updated the code here.

And I was doing something terribly wrong (thanks @ghedamat for pointing it out) in regards to embedded. That’s not how it’s supposed to be used. I’ve removed all references to embedded from that code.

The main issue was the JSON format which I couldn’t properly test with the Fixture Adapter, so I’ve removed all fixtures and the adapter in favor of the RESTAdapter (simulating GET request/response with $.mockjax in that fiddle).

As per revision 12, Foreign Keys should have a suffix _id or _ids, as explained in the Breaking Changes that I failed to read, or did so carelessly.

I still have one issue with Route#serialize in this tryout. In the very first time this method is called, model.get('name') returns either null or undefined. The second time and on, it works as intended. If I visit the Tags route before I go to a blog post, it also works as intended.

App.TagsTagRoute = Em.Route.extend({
    model: function(params) {
        var m = App.Tag.filter(function(tag) {
            return dasherize(tag.get('name')) == params.tag_name;
        });
        return m;
    }, 
    setupController: function (controller, model) {
        controller.set('content', model);
    },
    serialize: function(model) {
        var i = model.get('id');
        var n = model.get('name');
        return {
            tag_id: i, tag_name: dasherize(n)
        }
    }
});

And there is a request being made to ~/tags when I visit a blog post:


#5

Did you ever figure out what the problem was with the serialize method? I’m having the exact same issue where the first time this method is called every property on the model is either undefined or null but every time after that it works fine.


#6

Can you post your serialize @stephendeyoung? I have been using it throughout our product without issue.


#7

Hi,

I haven’t had the time to touch this ever since I asked here. I’m not sure if there was any change in the framework which touches that feature, but have you tried with the latest version of Ember? if that still occurs, I would say it could be logged as a bug in the repository. I won’t have time to test it this week, just thought of replying here.


#8

There’s a JSBin here.

@tsantos83 The problem seems to be that when the model first gets sent to the serialize method it has not been loaded which is why none of its properties are defined. But maybe @kingpin2k has an answer.


#9

Okay, I’ll give a quick run down of the issue. And an ember data expert is more than welcome to correct me if I’m wrong, because I don’t use ember data. It appears that belongsTo and hasMany aren’t resolved synchronously. They are materialized asynchronously later. Unfortunately, the serialize method is run once, it doesn’t depend on any property, and doesn’t go around changing itself. Really it’s meant to give the parameters for returning to that particular model. That being said, I recommend using fix one, because it more truly represents your model (having the group name in the url doesn’t nothing for looking up that models data).

Fix one

I do understand that maybe this jsbin doesn’t fully show your issue at hand, so I propose fix two. Since the materialization isn’t done right off the bat, you can cheat, and find the suggestion models before the suggestiongroup models and then the materialization will black magically happen before your serialization method and give you the url you are looking for. (I don’t like this, very hacky in my opinion)

Fix two

And then there is option 3. Change your template so instead of using the linkTo, use an action. Then serialization is out of the picture and you rely on transitionTo (which will serialize correctly) and you could also send in multiple models if applicable. I still like #1 the most, but best of luck either way.

Fix three


#10

Thanks for those suggestions.

I actually came up with another solution where my controller triggers an event when the models have been loaded which my view listens to and then re-renders the template.

This seems like a bug to me that the serialize method receives a model without it being loaded. I decided in the end not to use the groupName property in the url but instead generate a url based on the model’s title so you get pretty urls. The serialize method is useful in this regard so I don’t think it should be given a non-loaded model.


#11

I have a related issue.

I use serialize to link to a user by handle rather than id

App.UserRoute = Ember.Route.extend
  model: (params) ->
    App.User.find(params.user_id)

  serialize: (model, params) ->
    { user_id: model.get 'handle' }

Then in my template

= linkTo 'user' user

The link transitions to the correct controller. But the href is to /user/undefined.

This works fine if I change the serialize method to use the id

serialize: (model, params) ->
  { user_id: model.get 'id' }

So I guess there is a bug in binding the href in linkTo. Maybe it is hardcoded to bind to id?

UPDATE:

I tried changing the user’s id and it didn’t update the href, so I guess a LinkView does not bind the href to properties on the model.


#12

This topic was automatically closed after 7 days. New replies are no longer allowed.