2

I am trying to make Breeze.JS to make use of angular's http service for ajax calls. I followed the the docs (http://www.breezejs.com/documentation/customizing-ajax) and applied it. However it doesn't work.

Further more when I checked breeze source code I saw the following:

fn.executeQuery = function (mappingContext) {

    var deferred = Q.defer();
    var url = mappingContext.getUrl();

    OData.read({
            requestUri: url,
            headers: { "DataServiceVersion": "2.0" }
        },
        function (data, response) {
            var inlineCount;
            if (data.__count) {
                // OData can return data.__count as a string
                inlineCount = parseInt(data.__count, 10);
            }
            return deferred.resolve({ results: data.results, inlineCount: inlineCount });
        },
        function (error) {
            return deferred.reject(createError(error, url));
        }
    );
    return deferred.promise;
};

It simply calls OData.read without doing anything about http service. Thus OData makes use of builtin ajax. I don't understand with above code, how it is possible to customize ajax of Breeeze.JS

Ward
  • 17,793
  • 4
  • 37
  • 53
Onur Gumus
  • 1,389
  • 11
  • 27

2 Answers2

3

The problem is that the Breeze OData path does NOT use the Breeze Ajax adapter. Changing the Breeze Ajax Adapter (as the "Breeze Angular Service" does) won't help.

At the moment, both the "OData" and "webApiOData" DataService Adapters delegate to the 3rd party datajs library for AJAX services (and for other OData-related support).

You could replace its odata.defaultHttpClient with a version of your own based on $http. That's not a trivial task. Look here for the source code; it's roughly 160 lines.

I suppose we could write one. It hasn't been a priority.

Until somebody does it or we abandon datajs (not soon if ever), you're stuck with the datajs ajax.

Sorry about that.

p.s. Just about everyone who talks to OData data sources uses the datajs library. Maybe you can talk to the authors of that library and try to get them to support$http.

Ward
  • 17,793
  • 4
  • 37
  • 53
  • well I reverted to WebAPI provider. It Works fine but then there are two things wrong. 1-) It asks url for /Metadata rather than /$metadata. 2-) It asks for json document whereas $metadata returns an xml. I couldn't find anything to return json as metadata. I worked it around by saying server doesn't have metadata. I wonder if this is a safe approach though – Onur Gumus Mar 17 '14 at 08:52
  • How are you producing a $metadata endpoint? Are you using Web API OData? Why? Why not straight Web API? If you persist in using Web API OData, what you are doing won't work ... and I fear for how you have setup your Web API OData. Did you pay **close attention** to the recent [**OData sample**](http://www.breezejs.com/samples/breeze-web-api-odata); there are many "gotchas". They really don't do OData correctly yet. – Ward Mar 17 '14 at 15:16
  • Yes I do use Web API OData. Because I wanted to use standart OData and I wanted my server to be Breeze Independent. In your link it says:" A Breeze client can ask an OData source for metadata In our example, the client sends a GET request to http://localhost:55802/odata/$metadata. "How it will send the request to $metadata if I use "WebAPI" ? If I don't use WebAPI then I can't use my own httpclient, back to square one ? – Onur Gumus Mar 18 '14 at 04:50
  • In addition to that your link is geared towards EF. I'd use nhibernate. But again I don't want to introduce anything to server about breeze. – Onur Gumus Mar 18 '14 at 16:41
  • NH/EF is irrelevant. The sample is from Microsoft and they always use EF. But if you know NH, then you know how to implement their controllers with NH. If you look again you will see that THERE IS NO BREEZE ON THE SERVER. All we do is compensate for the fact that they screwed up metadata generation. We tell you to replace their `ODataConventionModelBuilder` with the `EdmBuilder` which IS NOT A BREEZE component and knows nothing at all about breeze. Since you are not using EF, that won't work ... so you'll have to create the metadata by hand. Sorry. – Ward Mar 19 '14 at 08:33
  • FWIW, the Web API OData query is standard and Breeze independent. You can write standard Web API controllers if you don't like Breeze controller (although you're wasting your time if what you care about is OData standards). Let Breeze hit the `metadata` endpoint. And use Web API to route a $metadata request to the same metadata endpoint so you can serve both Breeze and arbitrary OData clients. THEY WILL NOT KNOW THE DIFFERENCE when it comes to reading. As for saving ... well that can be done too. – Ward Mar 19 '14 at 08:38
  • You're also not hearing me when I tell you that the Web API OData implementation is incorrect. This has nothing to do with Breeze. You will be sorry if you try to use $batch save to update your database with either NH or EF. This is NOT a Breeze issue either. This is a failing of their OData stack no matter how you hit it. Breeze is not your problem. Web API OData is your problem. I wish you luck. – Ward Mar 19 '14 at 08:40
  • Yes I do understand Web API OData metadata generation is problematic and it may get a fix in future solutions and what you provide is an EF specific workaround. That is context.GetEdm() extension method in your tutorial. I suppose no such thing exists for non-EF users. In otherwords I have to write it from scratch, maybe client side bindings by hand. For hitting metadata endpoint, I don't have any clue how to serve the metadata as json, since builtin provider is generated as XML, if you can provide a sample (at least how the json looks like) that might be useful – Onur Gumus Mar 19 '14 at 09:45
  • Also one more question, am I safe, if I implement metadata on client side by hand manually, and use standart web api controllers ? – Onur Gumus Mar 19 '14 at 09:46
  • The "Metadata" section of the documentation describes how to create metadata on the server and by hand on the client. The "DocCode" sample shows one way to generate a JavaScript file to download to client as script file. Some other samples do that too. – Ward Apr 10 '14 at 22:39
1

Quick and dirty hack to simulate $http service

I ran into this issue today. Since the external datajs AJAX methods are used rather than Angular's $http service (as explained by Ward), Breeze queries do not trigger a digest and the models do not get updated.

As with any external-to-angular changes, the simple solution is to wrap any assignments from your queries in a $scope.$apply() function. However, this will quickly clutter up your app so it's a bad idea.

I came up with a quick and dirty hack that so far seems to work well:

  1. I have a dataContextservice which encapsulates all my Breeze queries and exposes methods like getCustomers(), getProducts() etc (inspired by the example on the Breeze site).
  2. When any of these data-access methods completes (ie the promise resolves), I call a triggerAngularDigest() method.
  3. This method simple calls $rootScope.$apply() inside a $timeout().
  4. The $timeout() causes Angular to run the digest on the next tick, i.e. after the data from your Breeze query has been assigned to your models.
  5. All your models update just like when you use $http, no need to call $apply() in your controllers.

Simplified version:

function dataContext($rootScope, $timeout, breeze) {

    // config of entity manager etc snipped

    return {
        getCustomers: function () {
            return breeze.EntityQuery.from('Customers')
                .using(manager)
                .execute()
                .then(function(data) {
                    triggerAngularDigest(); // <-- this is the key
                    return data;
                });
        }
    };

    function triggerAngularDigest() {
        $timeout(function() {
            $rootScope.$apply();
        }, 0);
    }
}

myApp.factory('dataContext', dataContext);

Then:

// some controller in your app
dataContext.getCustomers().then(function(data) {
    scope.customers = data;
}); 
Michael Bromley
  • 4,792
  • 4
  • 35
  • 57