Sproutcore 数据源和模型关系

发布于 2024-11-17 09:31:14 字数 871 浏览 2 评论 0原文

我目前有一个 Sproutcore 应用程序设置,在我的模型上具有以下关系:

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'})
});

当我使用固定装置时,我的客户固定装置看起来像这样:

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

我的品牌固定装置看起来像这样:

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

对于我获得客户品牌来说,这一切都很好并获得品牌客户。 我的问题是关于数据源如何适应这一点以及服务器响应应如何格式化。

  1. 从服务器返回的数据是否应该完全反映灯具文件的格式?那么客户端应该始终包含一个包含一系列品牌 ID 的 Brands 属性吗?反之亦然。

  2. 如果我有一个源列表视图,其中显示其下方品牌分组的客户。我将如何使用我的数据源加载源视图的数据?我是否应该调用服务器来获取所有客户端,然后再调用以获取所有品牌?

谢谢马克

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 to 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

笑梦风尘 2024-11-24 09:31:14

您返回的 json 将主要反映灯具。我最近有和你几乎一样的问题,所以我在 Grails 中构建了一个后端,在 SC 中构建了一个前端,只是为了探索存储和数据源。我的模型是:

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
          })

    });

为项目返回的 json 为

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

,为任务返回的 json ,当我选择一个项目时,

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

显然,这只是 1 个任务的 json 。如果你查看项目 json,你会发现我在其中放置了一个带有 id 的“任务”数组——这就是内部如何知道要获取哪些任务的方式。因此,要回答您的第一个问题,您不需要从子级到父级的 id,您需要父级加载所有子级,因此 json 与装置不完全匹配。

现在,事情变得有点棘手。当我加载应用程序时,我会执行查询来获取所有项目。存储在数据源上调用 fetch 方法。这是我的实现。

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);
        }
      }

这将获得项目,但不会获得任务。 Sproutcore 知道,一旦我访问项目上的任务数组,它就需要获取它们。它的作用是在数据源中调用retrieveRecords。该方法依次为任务数组中的每个 id 调用retrieveRecord。我的retrieveRecord 方法看起来像

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);

        } ...
      },

注意,您应该使用 sc-gen 来生成数据源,因为它提供了一个相当好的刷新存根,可以指导您实现需要实现的方法。它不提供 retrieveMethods 实现,但如果您不想对正在加载的每个子记录执行单个请求,则可以提供自己的实现。

请注意,您始终有选择。如果我愿意,我可以创建一个任务查询并预先加载所有任务数据,这样当我单击项目时就不需要访问我的服务器。因此,在回答你的第二个问题时,这取决于情况。您可以在单击客户端时加载品牌,也可以预先加载所有数据,如果没有那么多数据,这可能是一个好主意。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文