Reputation: 1880
In a ticket entry page, I have a main ticketEntry.html page, which contains one grid for the lines and one for the payments.
When ticketEntry.html is loaded, it must first retrieve the ticket view model (via ajax calls to Web API). The line and payment grid cannot retrieve their data until the ticket view model has been received.
In my current solution, I have to use $timeout in the controller for ticketEntry.html for this to work. I am looking for a cleaner way.
Extracts from ticketEntry.html:
<div ng-controller="ticketLineController">
<div id="ticketLineGridDiv" kendo-grid="ticketLineGrid" k-options="ticketLineGridOptions"></div>
</div>
...
<div ng-controller="ticketPaymentController">
<div id="ticketPaymentGridDiv" kendo-grid="ticketPaymentGrid" k-options="ticketPaymentGridOptions"></div>
</div>
In the controller for ticketEntry.html, I have this:
$timeout(function () {
ticketService.getTicket(ticketId).then(
function(ticket) {
$scope.initPos(ticket);
},
...);
}, 500);
$scope.initPos = function(ticket) {
$scope.ticket = ticket; <-- $scope.ticket is used by the line and payment grid
$scope.$broadcast('PosReady'); <-- Tell the payment and line controllers to load their grids
}
As you can see, I am using $timeout to delay for 500ms, then I get the ticket view model and broadcast to the line and payment controller that they now can load their grids.
Here is the listener in the line controller:
$scope.$on('PosReady', function (event) {
$scope.ticketLineGrid.setDataSource(getGridDataSource());
$scope.ticketLineGrid.dataSource.read();
});
The problem is that if I do not use $timeout in the ticket entry controller, $scope.ticketLineGrid is sometimes undefined here (same thing with the payments controller).
I have tried using angular.element(document).ready(function () {...} instead of $timeout in the ticket entry controller, but that did not handle the issue.
How do I know when $scope.ticketLineGrid (for example) has been created/defined?
What is the proper way of handling this kind of scenario?
Update 9/27/2014, to provide more data on how the ticket line grid gets initialized:
In the AngularJs directive in ticketEntry.html, the k-options specifies the definition object for the grid:
<div id="ticketLineGridDiv" kendo-grid="ticketLineGrid" k-options="ticketLineGridOptions"></div>
ticketPaymentGridOptions is just an object with properties that defines the grid:
$scope.ticketPaymentGridOptions = {
autoBind: false,
height: 143,
columns: [
{
field: "payCode", title: "PayCode",
},
{
field: "amount", title: "Amount", format: "{0:n2}", attributes: { style: "text-align:right" },
},
],
pageable: false,
...
};
Update 9/29/2014: This is the solution I went with, based on suggestion by Valentin
I use two watches - one in the child scope where the ticketLineGrid lives:
$scope.$watch('ticketLineGrid', function (newVal) {
if (angular.isDefined(newVal)) {
$scope.ticketControl.lineGridReady = true;
}
});
This watch sets the parent property $scope.ticketControl.lineGridReady = true once the grid has been initialized.
The parent (ticketEntryController) has watches for lineGridReady:
$scope.$watch('ticketControl.lineGridReady', function (gridReady) {
if (gridReady) {
$scope.loadPage();
}
});
$scope.loadPage = function () {
ticketService.getTicket(ticketId).then(
function (ticket) {
$scope.initPos(ticket);
}
...
}
Not as clean as I would have liked it, but certainly better than using $timeout...
Upvotes: 1
Views: 1741
Reputation: 6061
How do I know when $scope.ticketLineGrid (for example) has been created/defined?
You could use a $scope.$watch
statement :
$scope.$watch('ticketLineGrid', function (newVal, oldVal) {
if(angular.isDefined(newVal)){
// do something with it
}
})
However, in my view the good way to do this is to retrieve the data not from a scope property, but from a promise. I would use only promises and no events at all for this :
var ticketPromise = ticketService.getTicket(ticketId);
ticketPromise.then(function (ticket) {
$scope.ticket = ticket;
});
// you know that part better than I do
var ticketLineGridPromise = ...;
$q.all([ticketPromise, ticketLineGridPromise])
.then(function (realizations) {
var ticket = realizations[0], ticketLineGrid = realizations[1];
$scope.ticketLineGrid.setDataSource(getGridDataSource());
$scope.ticketLineGrid.dataSource.read();
})
I can't be more precise because it's not clear from your code what initializes ticketLineGrid
.
Finally, in many cases it's very handy to use resolve
clauses in your route declaration.
Upvotes: 1