简单的JSON和Breeze导致错误,首先调用fetchMetadata"

时间:2015-02-17 19:16:17

标签: knockout.js breeze

我有一个API端点,可以像这样吐出简单的JSON:

{
  "num_results": 2,
  "objects": [
    {
      "creator": null,
      "id": 1,
      "image": "www.test.com",
      "title": "test",
      "user_id": null
    }
}

我想使用Breeze将其放入Knockout observables。我认为我需要一个自定义jsonResultsAdapter。我试图跟随埃德蒙兹的例子,但我承认我并没有真正得到它。

var jsonResultsAdapter = new breeze.JsonResultsAdapter({
    name: "test",

    extractResults: function (data) {
        var results = data.results;
        if (!results) throw new Error("Unable to resolve 'results' property");
        return results;
    },

    visitNode: function (node, parseContext, nodeContext) {
        // Make parser
        if (node.objects) {
            return { entityType: "Pin"  }
        }
      }
    });

var dataService = new breeze.DataService( {
        serviceName: "api/",
        hasServerMetadata: false,
        jsonResultsAdapter: jsonResultsAdapter,
});    

var manager = new breeze.EntityManager( {
  dataService: dataService
});

var query = new breeze.EntityQuery.from("pin");

manager.executeQuery(query).then(function(data){
    ko.applyBindings(data);
}).fail(function(e) {
    alert(e);  
});

但是这在executeQuery函数中失败并带有消息:

  

错误:无法通过名称找到“类型”:'Pin'。请务必先执行查询或调用fetchMetadata。

我以为我刚刚那样做了。我在这做错了什么?

修改

  function initialize(metadataStore) {
    var DT = breeze.DataType; // alias

    metadataStore.addEntityType({
        shortName: "Pin",
        namespace: "test",
        dataProperties: {


            creator:    { dataType: DT.String },
            id:         { dataType: DT.Int64, isPartOfKey: true },
            image:      { dataType: DT.String },
            title:      { dataType: DT.String },
            modelLinks: { dataType: DT.Int64 }
        },
    });
  }

部分弄清楚了。需要添加metadataStore来描述返回的数据类型。但现在我得到了

  

错误:您无法多次将绑定应用于同一元素。

1 个答案:

答案 0 :(得分:2)

我怀疑你的JsonResultsAdapter没有做你认为应该做的事情。您应该调试它以查看正在进行的操作。

同时检查返回到查询成功回调的数据。 data.results中的对象看起来对你好吗?他们有entityAspect财产吗?一个entityType财产?如果没有,数据不会成为实体,我也不认为他们会有可观察的属性。

我相信您会将问题追溯到visitNode方法。当您在node.objects上获得匹配时,我预测节点本身将是这样的:

{
  "num_results": 2,
  "objects": [
    {
      "creator": null,
      "id": 1,
      "image": "www.test.com",
      "title": "test",
      "user_id": null
    }, 
    ...
  ]
}

您的回复

return { entityType: "Pin"  }

似乎告诉Breeze, 整件事 应被视为Pin类型的数据。

显然不是这样。 Pin数据 objects数组。

您确实希望匹配这些数据。你可以通过像

这样的测试来做到这一点
if (node.id && node.title) {
    return { entityType: ...  } // see below
}

或类似的东西。

风险在于此类测试与Pin以外的某些类型的数据匹配。如果您的服务器可以使用数据序列化类型指示器,那么它将更容易

  

我们在our rails sample中完成了此操作。我们added a $type property将每个实体对象序列化为Breeze客户端并将其设置为该类型的名称。这使得识别节点变得容易。

您必须返回EntityType而不是类型名称

查看"webApi" DataServiceAdapter的实现。

您会看到未返回类型的名称。您返回匹配的 EntityType

您需要这样的代码:

var type = mappingContext.entityManager.metadataStore.getEntityType('Pin', true);
return { entityType: type  }

我确信你可以想象一种更有效的方法来缓存这种类型的对象,这样你就不必每次访问节点时都要努力工作。

为什么要指定类型名称空间

您使用" test"命名空间定义了Pin类型。为什么?那是服务器上的命名空间吗?或者你刚刚弥补了吗?如果你没有,你就不需要一个。它只是增加了不必要的复杂性。

你可能会逃避这个:

var DT = breeze.DataType; // alias

metadataStore.addEntityType({
    shortName: "Pin",
    dataProperties: {
        creator:    { }, // string is the default
        id:         { dataType: DT.Int64, isPartOfKey: true },
        image:      { },
        title:      { },
        modelLinks: { dataType: DT.Int64 }
    },
});

更好的 extractResults 功能?

你也可以考虑改进extractResults方法,以便它立刻摆脱无关紧要的东西。为什么不直接进入这样的objects级别?

extractResults: function (data) {
    var results = data.results && data.results.objects;
    if (!results) throw new Error("Unable to resolve 'results.objects' property");
    return results;
}