开发者

Sproutcore datasources and model relationships

I currently have a Sproutcore app setup with the following relationships on my models:

App.Client = SC.Record.extend({
    name: SC.Record.attr(String),
    brands: SC.Record.toMany('App.Brand', {isMaster: YES, inverse: 'client'})
});

App.Brand = SC.Record.extend({
    name: SC.Record.attr(String),
    client: SC.Record.toOne('App.Client, {isMaster: NO, inverse: 'brands'})
});

When I was working with fixtures my fixture for a client looked like this:

{
    guid: 1,
    name: 'My client',
    brands: [1, 2]
}

And my fixture for a brand looked like this:

{
    guid: 1,
    name: 'My brand',
    client: 1
}

Which all worked fine for me getting a clients brands and getting a brands client. My question is in regards 开发者_StackOverflowto how Datasources then fit into this and how the server response should be formatted.

  1. Should the data returned from the server mirror exactly the format of the fixtures file? So clients should always contain a brands property containing an array of brand ids? And vice versa.

  2. If I have a source list view which displays Clients with brands below them grouped. How would I go about loading that data for the source view with my datasource? Should I make a call to the server to get all the Clients and then follow that up with a call to fetch all the brands?

Thanks

Mark


The json you return will mostly mirror the fixtures. I recently had pretty much the same question as you, so I built a backend in Grails and a front end in SC, just to explore the store and datasources. My models are:

Scds.Project = SC.Record.extend(
    /** @scope Scds.Project.prototype */ {
      primaryKey: 'id',
      name: SC.Record.attr(String),
      tasks: SC.Record.toMany("Scds.Task", {
            isMaster: YES,
            inverse: 'project'
          })
    });


Scds.Task = SC.Record.extend(
    /** @scope Scds.Task.prototype */ {

      name: SC.Record.attr(String),
      project: SC.Record.toOne("Scds.Project", {
            isMaster: NO
          })

    });

The json returned for Projects is

[{"id":1,"name":"Project 1","tasks":[1,2,3,4,5]},{"id":2,"name":"Project 2","tasks":[6,7,8]}]

and the json returned for tasks, when I select a Project, is

{"id":1,"name":"task 1"}

obviously, this is the json for 1 task only. If you look in the projects json, you see that i put a "tasks" array with ids in it -- thats how the internals know which tasks to get. so to answer your first question, you dont need the id from child to parent, you need the parent to load with all the children, so the json does not match the fixtures exactly.

Now, it gets a bit tricky. When I load the app, I do a query to get all the Projects. The store calls the fetch method on the datasource. Here is my implementation.

Scds.PROJECTS_QUERY = SC.Query.local(Scds.Project);
var projects = Scds.store.find(Scds.PROJECTS_QUERY);
...


fetch: function(store, query) {
        console.log('fetch called');

        if (query === Scds.PROJECTS_QUERY) {
          console.log('fetch projects');
          SC.Request.getUrl('scds/project/list').json().
              notify(this, '_projectsLoaded', store, query).
              send();

        } else if (query === Scds.TASKS_QUERY) {
          console.log('tasks query');
        }

        return YES; // return YES if you handled the query
      },

      _projectsLoaded: function(response, store, query) {
        console.log('projects loaded....');

        if (SC.ok(response)) {
          var recordType = query.get('recordType'),
              records = response.get('body');

          store.loadRecords(recordType, records);
          store.dataSourceDidFetchQuery(query);

          Scds.Statechart.sendEvent('projectsLoaded')
        } else {
          console.log('oops...error loading projects');
          // Tell the store that your server returned an error
          store.dataSourceDidErrorQuery(query, response);
        }
      }

This will get the Projects, but not the tasks. Sproutcore knows that as soon as I access the tasks array on a Project, it needs to get them. What it does is call retrieveRecords in the datasource. That method in turn calls retrieveRecord for every id in the tasks array. My retrieveRecord method looks like

retrieveRecord: function(store, storeKey) {
        var id = Scds.store.idFor(storeKey);
        console.log('retrieveRecord called with [storeKey, id] [%@, %@]'.fmt(storeKey, id));

        SC.Request.getUrl('scds/task/get/%@'.fmt(id)).json().
            notify(this, "_didRetrieveRecord", store, storeKey).
            send();

        return YES;
      },

      _didRetrieveRecord: function(response, store, storeKey) {
        if (SC.ok(response)) {
          console.log('succesfully loaded task %@'.fmt(response.get('body')));
          var dataHash = response.get('body');
          store.dataSourceDidComplete(storeKey, dataHash);

        } ...
      },

Note that you should use sc-gen to generate your datasource, because it provides a fairly well flushed out stub that guides you towards the methods you need to implement. It does not provide a retrieveMethods implementation, but you can provide your own if you don't want to do a single request for each child record you are loading.

Note that you always have options. If I wanted to, I could have created a Tasks query and loaded all the tasks data up front, that way I wouldn't need to go to my server when I clicked a project. So in answer to your second question, it depends. You can either load the brands when you click on the client, or you can load all the data up front, which is probably a good idea if there isn't that much data.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜