1

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...

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I agree that a watch statement is not a great approach. I am already using a promise to get the ticket view model, and then a broadcast to let the child controllers know when done. I have updated my post with data on how the grid gets initialized; it is all declarative and handled automagically by the kendo grid directive (using the options object specified). I could of course resort to jQuery and just create the grid when the ticketEntry promise has been resolved (instead of using a broadcast), but I prefer a more Angular way.
Broadcast or promises are 2 ways to trigger an "event"; to me the promises way seems cleaner, but why not. But it seems to me what you have here is a synchronisation problem, correct? You're "waiting" from both the ticket data to be loaded and ticketLineGrid to be ready. That's why I suggest the use of $q.all.
I agree that promises are great, but how do I get the Kendo UI grid to give me a promise that lets me know when it has been initialized?
I don't know Kendo, but if there is no direct way, that's where I'd use a $scope.$watch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.