CONNECTING BREEZEJS TO A LIGHTSWITCH BACKEND, YEAH IT WORKS (PART 2/3)

Introduction

In this article we’ll elaborate how we can get BreezeJS working in lightSwitch.

Let’s first refactor our code

IN this first step we’ll first include some progressive insight I acquired yesterday night and introduce an angular factory to do the whole setup of low level configuration of BreezeJS.

This factory approach allows a better level of encapsulation and put code where it belongs.  That’s nice about AngularJS, a seasoned c# developer feels soon at home.

Voilà:

<script type="text/javascript">
       var app = angular.module("app", ['breeze.angular']);

       angular.module('app')
      .factory('lightSwitchEntityManagerFactory', ['breeze', entityManagerFactory]);

       function entityManagerFactory(breeze) {
           breeze.NamingConvention.camelCase.setAsDefault();
           breeze.config.initializeAdapterInstance('dataService', 'OData', true);

           var serviceName = "/ApplicationData.svc/";

           var factory = {
               Manager: function () {
                   return new breeze.EntityManager(serviceName);
               },
               serviceName: serviceName
           };

           return factory;
       }
      
       app.run(['breeze', function (breeze) {
          
       }]);

      
       app.controller("MainCtr", function ($scope, lightSwitchEntityManagerFactory) {
           $scope.customers = [];
           var em = lightSwitchEntityManagerFactory.Manager();
           var query = breeze.EntityQuery.from("Customers");//.where("lastName", "contains", "bladel");
           em.executeQuery(query)
                   .then(function querySucceeded(data) {
                       $scope.customers = data.results;
                   });
       });
   </script>

 

What’s going wrong?

The above refactoring didn’t add any functionality. The factory approach is just more clear, that’s all. You can learn more about angular Factories on the Angular website.

Let’s use our browser developer tools and sniff what’s happening in the browser:

515

 

When BreezeJS requests the Odata service metadata we get back a 415 error response meaning “Unsupported Media type”.

 

unsupportedmediatype

Nowadays, odata can deliver data both in xml and Json format, but the metadata are always in xml format.

Nonetheless, requireJS seems to add an accept header to request for json data:

accept

 

Let’s use now fiddler so that we can construct our own request for the metadata and tweak the Accept header:

In order to be 100% sure, let’s first reproduce the problem in Fiddler. Luckily fiddler gives the same  result:

image

 

image

 

Let’s apply our fix now and change the Accept header in Application/xml:

image

 

Bingo ! This gives a 200 response and we get the metadata.

image

 

How doing the same in code?

The above looks challenging, but how can we achieve the same via code. An extra challenge will be that the accept header should only be patched when the metadata are requested. For other requests,(so request for normal data, the Accept header should indicate Json data are needed, because that’s the format BreezeJs needs !

var oldClient = OData.defaultHttpClient;

            var myClient = {
                 request: function (request, success, error) {
                     if (request.requestUri.indexOf("$metadata", request.requestUri.length - "$metadata".length) !== -1) {
                         request.headers.Accept = "application/xml";
                     }

                    return oldClient.request(request, success, error);
                 }
             };

            OData.defaultHttpClient = myClient;

first the request url is examined and in case it ends with “$metadata’”, the request header is updated to “application/xml”. In all other cases, the Accept header stays as it is.The above code must be included in the factory. Basically, it patches the BreezeJs proxy client.

And this works, we receive now a 200 response on the metadata and, even more important, we retrieve also correctly actual application data afterwards:

image

 

Our first angular based table in LightSwitch

Let’s enjoy now the result of all this: a simple table completely rendered with AngularJS in LightSwitch:

image

Currently the table is seasoned with some Twitter Bootstrap, but e..g. paging is missing.

I’m planning a whole series of articles on using angular in LightSwitch, so be prepared

 

The full code

As I mentioned earlier in part 1 of this article, our code and markup is now condensed in one html page, which is good or this smoke test demo.

In further articles on angular I’ll stick to better practices.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
    <title></title>
    <link href="content/bootstrap.css" rel="stylesheet" />
</head>
<body ng-app="app">

    <div class="container" ng-controller="MainCtr">
        <div class="row">
            <h2>Customers</h2>
        </div>
        <div class="row">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <td>Last Name</td>
                        <td>First Name</td>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-repeat="customer in customers">
                        <td>{{customer.lastName}}</td>
                        <td>{{customer.firstName}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <script src="Scripts/datajs-1.1.3.js"></script>
    <script src="Scripts/jquery-1.9.0.js"></script>
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/breeze.min.js"></script>
    <script src="Scripts/breeze.angular.js"></script>


    <script type="text/javascript">
        var app = angular.module("app", ['breeze.angular']);

        angular.module('app')
       .factory('lightSwitchEntityManagerFactory', ['breeze', entityManagerFactory]);

        function entityManagerFactory(breeze) {
            breeze.NamingConvention.camelCase.setAsDefault();
            breeze.config.initializeAdapterInstance('dataService', 'OData', true);

            var oldClient = OData.defaultHttpClient;

            var myClient = {
                request: function (request, success, error) {
                    if (request.requestUri.indexOf("$metadata", request.requestUri.length - "$metadata".length) !== -1) {
                        request.headers.Accept = "application/xml";
                    }

                    return oldClient.request(request, success, error);
                }
            };

            OData.defaultHttpClient = myClient;

            var serviceName = "/ApplicationData.svc/";

            var factory = {
                Manager: function () {
                    return new breeze.EntityManager(serviceName);
                },
                serviceName: serviceName
            };

            return factory;
        }

        app.run(['breeze', function (breeze) {

        }]);



        app.controller("MainCtr", function ($scope, lightSwitchEntityManagerFactory) {
            $scope.customers = [];
            var em = lightSwitchEntityManagerFactory.Manager();
            var query = breeze.EntityQuery.from("Customers");//.where("lastName", "contains", "bladel");
            em.executeQuery(query)
                    .then(function querySucceeded(data) {
                        $scope.customers = data.results;
                    });
        });
    </script>
</body>
</html>

 

Conclusion

So far, I’m happy with this result and I hope I didn’t miss something here. My first tests reveal that BreezeJS is correctly functioning, I tried queries but also save operations. Drop me a line in case it turns out differently.

Anyhow, this can be the start of enriching LightSwitch applications with a full desktop variant based on really strong foundations: AngularJS, BreezeJS and twitter bootstrap.

More in my next article series.