Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Breezejs, How to fetch metadata at start with shared EntityManager

I have an Angular application using Breeze and that has a shared EntityManager for my different controllers. A few of my controllers can be reached without executing a query to pre-populate the EntityManager's MetadataStore. I have found somewhat of a starting direction here saying to fetch the metadata at the start of the application. My project is based on the Angular-Breezejs template and when i try doing the following I get errors because the promise isn't fully resolved before something uses the datacontext.

app.factory('datacontext',
    ['breeze', 'Q', 'model', 'logger', '$timeout',
        function (breeze, Q, model, logger, $timeout) {
            logger.log("creating datacontext");

            configureBreeze();
            var manager = new breeze.EntityManager("/api/app");            
            manager.enableSaveQueuing(true);

            var datacontext = {
                metadataStore: manager.metadataStore,
                saveEntity: saveEntity,
                getUsers: getUsers,
                getUser: getUser,
                createUser: createUser,
                deleteUser: deleteUser
            };

            return manager.fetchMetadata()
                    .then(function () {
                        model.initialize(datacontext);
                        return datacontext;
                    })
                    .fail(function (error) {
                        console.log(error);
                        return error;
                    });
            //Function definitions

What is the proper way of blocking until the metadata fetch is complete? Since it seems unnecessary to have to check if the metadata exists before each non-query function (including entity creation) like the original poster of the linked question above ended up doing.

like image 763
cobywhite Avatar asked Apr 17 '13 19:04

cobywhite


1 Answers

I see your problem.

When Angular invokes your factory function to create the DataContext service, it expects to get back immediately (synchronously) a DataContext object that is ready to use. But you are returning a promise to return that DataContext at some time in the future ... and Angular just isn't built for that.

I like the idea though. You might want to propose it to the Angular team :-).

So what you're trying here just won't work. You have to return a DataContext immediately. Until the metadata arrive, you have to either block the entire UI or block the specific functionality that relies on metadata (e.g., createUser). It's kind of like waiting for the DOM to settle down before manipulating it with jQuery.

This situation is not Angular specific. You face the same quandary in a Knockout app. The resolution is similar.

Start by exposing some kind of "whenReady" hook on the DataContext. A promise might be a good idea. Something like this:

function (breeze, Q, model, logger, $timeout) {
    logger.log("creating datacontext");
    ...
    var readyDeferred = Q.defer(), whenReady = readyDeferred.promise;

    var datacontext = {
            whenReady: whenReady,
            ...
        };

    initializeDatacontext();

    return datacontext; // now Angular is happy because it has a datacontext

    function initializeDatacontext() {
        manager.fetchMetadata()
               .then(function () {
                   readyDeferred.resolve();
                   // do success stuff;
               })
               .fail(function (error) {
                   readyDeferred.reject(error);
                   // do error stuff;
               });
    }

    //Function definitions
}

Elsewhere in the bootstrapping of your app, you tie into the datacontext.whenReady promise.

    // somewhere inside your main controller
    $scope.isReady = false;
    datacontext.whenReady.then(function() {
           $scope.isReady = true;
           $scope.$apply();
       })
    .fail(function() { alert("Uh oh!"); });
    ...

Now bind the scope's isReady to the HTML such that you get the desired behavior. You could use it to block the entire UI or just seal off functionality (e.g, "Create User") until the datacontext is ready.

Pleae don't use this pseudo-code literally. Use it for inspiration.

like image 107
Ward Avatar answered Sep 28 '22 08:09

Ward