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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With