Upgrading to ng-grid 3.0 (ui-grid)

In this tutorial segment I upgrade the league application to use the new ui-grid (ng-grid 3.0).  Note that ui-grid is at this time still early beta, but offers the likelihood of better performance, a cleaner interface, easier edit in place, and removes the previous dependency on jQuery.

This code is built on the rails4 tutorial that I have previously written, and in particular uses as a starting point step 10 of that tutorial, which is available at github: PaulL : tutorial_10.

We start by uninstalling our previous angular-grid:

  bower uninstall angular-grid --save-dev

We then obtain the beta files for the new ui-grid. These aren’t installable with bower at the moment, so we download them directly following the links in the ng-grid github. Create a directory vendor/ui-grid, and in that directory:

  wget http://ui-grid.info/release/ui-grid-unstable.js
  wget http://ui-grid.info/release/ui-grid-unstable.css
  wget http://ui-grid.info/release/ui-grid.eot
  wget http://ui-grid.info/release/ui-grid.svg
  wget http://ui-grid.info/release/ui-grid.ttf
  wget http://ui-grid.info/release/ui-grid.woff

You can also use curl to retrieve the files, or right click on them in your browser and select save as.

As before, we also need to symlink the css file as a .less file so that we can use it within ngbp:

  ln -s ui-grid-unstable.css ui-grid-unstable.less

We update build.config.js to use ui-grid instead of angular-grid:

  vendor_files: {
    js: [
      'vendor/angular/angular.js',
      'vendor/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'vendor/placeholders/angular-placeholders-0.0.1-SNAPSHOT.min.js',
      'vendor/angular-ui-router/release/angular-ui-router.js',
      'vendor/angular-ui-utils/modules/route/route.js',
      'vendor/angular-resource/angular-resource.js',
      'vendor/jquery/jquery.js',
      'vendor/ui-grid/ui-grid-unstable.js'
    ],
    css: [
    ],
    assets: [
      'vendor/ui-grid/ui-grid.eot',
      'vendor/ui-grid/ui-grid.svg',
      'vendor/ui-grid/ui-grid.ttf',
      'vendor/ui-grid/ui-grid.woff'
    ]
  },

And we update src/less/main.less to import the less file:

@import '../../vendor/ui-grid/ui-grid-unstable.less';

We then follow the upgrade instructions to change our code to use ui-grid properly.  First, we change our controllers in src/app/team/team.js and src/app/club/club.js to include ui.grid instead of ngGrid:

angular.module( 'league.team', [
  'ui.state',
  'league.club',
  'ngResource',
  'ui.grid'
])

We then change src/app/team/teams.tpl.html and src/app/club/clubs.tpl.html to reference ui-grid instead of ng-grid:

  <div class="gridStyle" ui-grid="gridOptions"></div>

Finally, we need to give each column definition a name or field within our controllers – currently our button columns have no name against them:

  $scope.gridOptions = {
    data: 'teams',
    columnDefs: [
      {field: 'id', displayName: 'Id'},
      {field: 'club_id', displayName: 'Club Id', visible: false},
      {field: 'club_name', displayName: 'Club Name'},
      {field: 'name', displayName: 'Team Name'},
      {field: 'captain', displayName: 'Captain'},
      {field: 'date_created', displayName: 'Date Created', cellFilter: "date:mediumDate"},
      {name: 'edit', displayName: 'Edit', cellTemplate: '<button id="editBtn" type="button" class="btn-small" ng-click="$parent.$parent.editTeam(row.entity)" >Edit</button> '},
      {name: 'delete', displayName: 'Delete', cellTemplate: '<button id="deleteBtn" type="button" class="btn-small" ng-click="$parent.$parent.deleteTeam(row.entity)" >Delete</button> '}
    ],
    multiSelect: false,
    filterOptions: $scope.filterOptions,
    showColumnMenu: true    
  };

Finally, our buttons aren’t working correctly. The new version no longer gives direct access to the parent scope – we can get at our edit function by calling getExternalScopes().editTeam(row.entity):

      {name: 'edit', displayName: 'Edit', cellTemplate: '<button id="editBtn" type="button" class="btn-small" ng-click="getExternalScopes().editClub(row.entity)" >Edit</button> '},
      {name: 'delete', displayName: 'Delete', cellTemplate: '<button id="deleteBtn" type="button" class="btn-small" ng-click="getExternalScopes().deleteClub(row.entity)" >Delete</button> '},
      {name: 'show_teams', displayName: 'Show Teams', cellTemplate: '<button id="showBtn" type="button" class="btn-small" ng-click="getExternalScopes().showTeams(row.entity)" >Show Teams</button> '}

We also need to set the external scope, which we do by storing $scope in a variable within the controller:

.controller( 'ClubsCtrl', function ClubsController( $scope, ClubRes, $state ) {
  $scope.$scope = $scope;

And setting it on the grid in the tpl.html:

  <div class="gridStyle" ui-grid="gridOptions" external-scopes="$scope" ></div>

Next, the ui-grid has split out many of the functions into standalone modules, including ui.grid.edit and ui.grid.resizeColumns.  The full API can be found in the docs.  We’d like to provide edit in place functionality, and to make our columns resizable.

In the controllers, include these two functions:

angular.module( 'league.club', [
  'ui.state',
  'ngResource',
  'ui.grid',
  'ui.grid.resizeColumns',
  'ui.grid.edit'
])

And in the tpl.html, include the relevant directives:

  <div class="gridStyle" ui-grid="gridOptions" external-scopes="$scope" ui-grid-edit ui-grid-resize-columns ></div>

This should give you an editor when you double click on a cell, and should also let you resize the grid columns.

To complete the edit in place functionality, we need a server call when you finish editing. To do this we register a callback in each controller, which is called whenever a cell finishes editing:

  $scope.gridOptions.onRegisterApi = function( gridApi ) {
    gridApi.edit.on.afterCellEdit( $scope, function( rowEntity, colDef ) {
      rowEntity.$update( function( response ) {
        $scope.error = null;
      }, function( error ) {
        $scope.error = error;
      });
    });
  };

This works, but it calls update after each field edit. It would be preferable for this to be called after the user clicks off the row. This can potentially be achieved through using ui.grid.selection, and the rowSelectionChanged event, although I haven’t implemented that as yet.

The filter functionality also requires modification to continue working.  Referring to the tutorial, and guessing a bit from looking at the code, we can make the following changes to the teams controller. We’re turning on filtering on the grid, and if the clubId is present, we’re setting a filter term on the club id column:

.controller( 'TeamsCtrl', function TeamsController( $scope, TeamRes, $state, $stateParams ) {
  $scope.$scope = $scope;
  $scope.teams = TeamRes.query();

  $scope.clubId = $stateParams.clubId;

  $scope.gridOptions = {
    data: 'teams',
    columnDefs: [
      {field: 'id', displayName: 'Id'},
      {field: 'club_id', displayName: 'Club Id', visible: false,
      {field: 'club_name', displayName: 'Club Name'},
      {field: 'name', displayName: 'Team Name'},
      {field: 'captain', displayName: 'Captain'},
      {field: 'date_created', displayName: 'Date Created', cellFilter: "date:mediumDate"},
      {name: 'edit', displayName: 'Edit', cellTemplate: '<button id="editBtn" type="button" class="btn-small" ng-click="getExternalScopes().editTeam(row.entity)" >Edit</button> '},
      {name: 'delete', displayName: 'Delete', cellTemplate: '<button id="deleteBtn" type="button" class="btn-small" ng-click="getExternalScopes().deleteTeam(row.entity)" >Delete</button> '}
    ],
    multiSelect: false,
    enableFiltering: true,
    showColumnMenu: true    
  };

  if($scope.clubId) {
    $scope.gridOptions.columnDefs[1].filter = { term: $scope.clubId };
  }

  $scope.gridOptions.onRegisterApi = function( gridApi ) {
  ....

And we can remove the filter box from team.tpl.html, as they’re now column by column filters, rather than a general filter box:

<div>
  <div class="gridStyle" ui-grid="gridOptions" external-scopes="$scope" ui-grid-edit ui-grid-resize-columns ></div>
  <button ng-click="newTeam()" class="btn btn-primary" >New</button>  
</div>

 

The code for this tutorial can be found at github: PaulL : ui-grid-3.0

Advertisements

22 thoughts on “Upgrading to ng-grid 3.0 (ui-grid)

  1. Pingback: Editable ngGrid, with both dropdowns and selects | technpol

  2. Pingback: AngularJS and Rails 4 CRUD application using ng-boilerplate and twitter bootstrap 3: Tutorial Index | technpol

  3. Pingback: Using protractor to e2e test ui-grid 3.0 (ng-grid) | technpol

  4. The code is in github, although obviously if you didn’t want to run rails that doesn’t help you much. The application won’t run without a backend, so cannot go into a plunker. I can do a screenshot, but it’s not all that exciting, it looks like the screenshots in the previous sections of the tutorial, but just with a 3.0 grid (which still looks like a grid). The UI is not all that pretty, so not much to look at.

  5. How to better implement calculated fields for ui-grid 3?

    old method (for ng-grid ) with attaching function to data object don’t work .

    i.e.

    ………………
    var data = [{name:”john”},{name:”lana”}];

    angular.forEach (data,function(row, key) {
    row.getNewName = function() {
    return “human: ” + this.name;
    };
    });

    ……..

    columnDefs: [
    {field: ‘getNewName()’, displayName: ‘New Name’ }
    ]

  6. @Paul thank you for your reply, it is working now, I was not including latest source files, when included everything works fine as per documentation..

    thanks for time..

  7. Update: I already fixed in the plunker above Must have appScopeProvider: $scope in subGridOptions

  8. Hi PaulL,

    When do you think ui-grid will be ready for production? It would be a risk to use ui-grid in a production project? At this time it’s a better option to use ng-grid?

    Thank you very much.

  9. Amazon are using ui-grid in production on the AWS console, if that helps. I’d say ng-grid is an equal risk in production at the moment, because there’s almost no support for it.

  10. Hi PaulL, a question? there is any method for get a list of renderedRows of ui-grid? getVisibleRows return all rows in the grid and I need only the rows rendered in the screen. I need to update some data in the rows rendered but I don´t know what event to subscribe and I don´t find a method to call.

    Thanks in advance.

  11. PaulL,

    for this I’m listening core.on.scrollEnd and add something like this to ui-grid.js

    Grid.prototype.getRenderedRows = function getRenderedRows() {
    return this.renderContainers.body.renderedRows;
    };

    this.registerMethod(‘core’, ‘getRenderedRows’, this.grid.getRenderedRows);

    😦

  12. Hi Paul – off topic – didn’t know how to contact you — in your GitHub work for ui grid, you mentioned that you added setRowsClean(dataRows). Is there a way to set a single row to be clean, such as in a cancel edit scenario on the row?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s