Expand All

  Getting Started

  Reference

  Features

  Row Models

  Themes

  Components

  Examples

Misc

Github stars make projects look great. Please help, donate a star, it's free.
Read about ag-Grid's Partnership with webpack.
Get informed on releases and other ag-Grid news only - never spam.
Follow on Twitter

Angular - Building with @ngtools/webpack

We document the main steps required when using ag-Grid, Angular, Typescript and @ngtools/Webpack below, but please refer to ag-grid-angular-example on GitHub for a full working example of this.

Initialise Project

mkdir ag-grid-ngtools cd ag-grid-ngtools npm init // accept defaults

Install Dependencies

npm i --save ag-grid ag-grid-angular npm i --save @angular/common @angular/compiler @angular/compiler-cli @angular/core @angular/platform-browser @angular/platform-browser-dynamic @angular/router typescript rxjs core-js zone.js npm i --save-dev webpack webpack-dev-server @ngtools/webpack angular2-template-loader awesome-typescript-loader extract-text-webpack-plugin file-loader canonical-path @types/node npm i --save-dev css-loader style-loader html-loader html-webpack-plugin raw-loader url-loader // optional - only necessary if you're using any of the Enterprise features npm i --save ag-grid-enterprise

Create Application

Our application will be a very simple one, consisting of a single Module, a single Component and a bootstrap file, as well a few utility files for vendor & polyfills.

You can either create the project by hand, or check it out from our Angular Seed Repo in GitHub.

The resulting project structure will look like this:

ag-grid-ngtools ├── app │   ├── app.component.html │   ├── app.component.ts │   ├── app.module.ts │   ├── boot-aot.ts │   ├── boot.ts │   ├── polyfills.ts │   ├── vendor-aot.ts │   └── vendor.ts ├── config │   ├── helpers.js │   ├── index.html │   ├── webpack.dev.js │   └── webpack.prod.js ├── dist ├── node_modules ├── package.json ├── tsconfig.aot.json └── tsconfig.json // app/app.module.ts import {NgModule} from "@angular/core"; import {BrowserModule} from "@angular/platform-browser"; // ag-grid import {AgGridModule} from "ag-grid-angular/main"; // application import {AppComponent} from "./app.component"; @NgModule({ imports: [ BrowserModule, AgGridModule.withComponents([] ) ], declarations: [ AppComponent ], bootstrap: [AppComponent] }) export class AppModule { } // app/app.component.ts import {Component} from "@angular/core"; import {GridOptions} from "ag-grid/main"; @Component({ selector: 'my-app', templateUrl: 'app.component.html' }) export class AppComponent { public gridOptions:GridOptions; public rowData:any[]; public columnDefs:any[]; constructor() { // we pass an empty gridOptions in, so we can grab the api out this.gridOptions = <GridOptions>{ onGridReady: () => { this.gridOptions.api.sizeColumnsToFit(); } }; this.columnDefs = [ {headerName: "Make", field: "make"}, {headerName: "Model", field: "model"}, {headerName: "Price", field: "price"} ]; this.rowData = [ {make: "Toyota", model: "Celica", price: 35000}, {make: "Ford", model: "Mondeo", price: 32000}, {make: "Porsche", model: "Boxter", price: 72000} ]; } } // app/app.component.html <ag-grid-angular #agGrid style="width: 500px; height: 150px;" class="ag-theme-fresh" [gridOptions]="gridOptions" [columnDefs]="columnDefs" [rowData]="rowData"> </ag-grid-angular> // app/boot.ts import {platformBrowserDynamic} from "@angular/platform-browser-dynamic"; import {AppModule} from "./app.module"; // for enterprise customers // import {LicenseManager} from "ag-grid-enterprise/main"; // LicenseManager.setLicenseKey("your license key"); platformBrowserDynamic().bootstrapModule(AppModule); // app/polyfills.ts import "core-js/es6"; import "core-js/es7/reflect"; require('zone.js/dist/zone'); if (process.env.ENV === 'production') { // Production } else { // Development Error['stackTraceLimit'] = Infinity; require('zone.js/dist/long-stack-trace-zone'); }

Configuration

We have 2 Webpack Configurations in the example project - a dev configuration and a production configuration (with AOT support). In both of these configurations we make use of an html file where our generated bundle(s) will be inserted and will serve as our application starting point, as well as a helper file for within use of the webpack configurations:

// config/helpers.js var path = require('path'); var _root = path.resolve(__dirname, '..'); function root(args) { args = Array.prototype.slice.call(arguments, 0); return path.join.apply(path, [_root].concat(args)); } exports.root = root; <!-- config/index.html --> <!DOCTYPE html> <html> <head> <base href="/"> <title>ag-Grid & Angular With Webpack</title> </head> <body> <my-app>Loading...</my-app> </body> </html>

helpers.js helps us to resolve path easily, and index.html will be used by the HtmlWebpackPlugin plugin to ensure the generated bundles are inserted dynamically, instead of us needing to manage this ourselves.

Development Configuration

tsconfig Configuration

For development purposes we have a simple configuration:

// tsconfig.json { "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false, "lib": ["dom","es2015"] }, "compileOnSave": true, "exclude": [ "node_modules/*", "app/boot-aot.ts" ] }

Note that here we exclude the AOT bootstrap file as the AOT bootstrap file will have references to Angular Factories that won't exist yet.

Vendor Entry File

Here we add any vendor (or third-party) related libraries - note that we've included the ag-Grid CSS and chosen theme ("Fresh" in this case), as well as included the ag-grid-enterprise dependency.

The ag-grid-enterprise inclusion is only necessary it you're using Enterprise features - it can be ommitted if not.

Note too that we've included @angular/platform-browser-dynamic - this is necessary for JIT (Just-In-Time)/Development mode, but can be dropped for production/AOT builds (see later for more on this).

// app/vendor.ts // Angular import '@angular/platform-browser'; import '@angular/platform-browser-dynamic'; import '@angular/core'; import '@angular/common'; // RxJS import 'rxjs'; // ag-grid import 'ag-grid/dist/styles/ag-grid.css'; import 'ag-grid/dist/styles/ag-theme-fresh.css'; import 'ag-grid-angular/main' // for ag-grid-enterprise users only //import 'ag-grid-enterprise/main';

Webpack Development Configuration

// config/webpack.dev.js var webpack = require('webpack'); var helpers = require('./helpers'); var path = require('path'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { devtool: 'cheap-module-eval-source-map', entry: { 'polyfills': './app/polyfills.ts', 'vendor': './app/vendor.ts', 'app': './app/boot.ts' }, output: { path: helpers.root('dist'), publicPath: 'http://localhost:8080/', filename: '[name].js', chunkFilename: '[id].chunk.js' }, resolve: { extensions: ['.ts', '.js'] }, module: { loaders: [ { test: /\.ts$/, exclude: path.resolve(__dirname, "node_modules"), loaders: ['awesome-typescript-loader', 'angular2-template-loader'] }, { test: /\.html$/, loader: 'html-loader', query: { minimize: false // workaround for ng2 } }, { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader?name=[path]/[name].[ext]' }, { // site wide css (excluding all css under the app dir) test: /\.css$/, exclude: helpers.root('app'), loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader?sourceMap'}) }, { // included styles under the app directory - these are for styles included // with styleUrls test: /\.css$/, include: helpers.root('app'), loader: 'raw-loader' } ] }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['app', 'vendor', 'polyfills'] }), new ExtractTextPlugin({filename: '[name].css'}), new HtmlWebpackPlugin({ template: 'config/index.html' }) ], devServer: { historyApiFallback: true, stats: 'minimal' }, };

entry

We could generate one large bundle, but it's better to break the bundle up into the fairly "static" dependencies and the more fluid application code. Using the entry property we can specify the entry points we want to use - we have specified 3 here:

  • polyfills: polyfills we require to run Angular / ES6 applications in current browsers.
  • vendor: the vendor (or 3rd party) libraries we need - ag-Grid, Angular etc.
  • app: our application code.

resolve

As our imports done specify what file extension to use, we need to specify what file types we want to match on - in this case we're looking at TypeScript and JavaScript files, but you could also add CSS & HTML files too.

module.loaders

Loaders tell Webpack how & what to do with certain types of file - we have specified a few here to deal with Typescript, HTML, CSS and Images:

  • awesome-typescript-loader: transpile Typescript to ES5
  • angular2-template-loader: processes Angular components' template/styles
  • html
  • images & fonts
  • css: the first phe pattern matches application-wide styles, and the second handles component-scoped styles (ie with styleUrls)

plugins

  • CommonsChunkPlugin: separates our entry points into distinct files (one each for polyfills, vendor and application)
  • HtmlWebpackPlugin: takes our supplied template index.html and inserts the generates JS & CSS files for us

The dev configuration doesn't generate any files - it keeps all bundles in memory, so you won't find any artifacts in the dist directory (from this configuration).

Production Configuration

tsconfig Configuration

// tsconfig.aot.json { "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false, "lib": ["dom","es2015"] }, "compileOnSave": true, "exclude": [ "node_modules/*", "aot/", "app/boot-aot.ts" ], "angularCompilerOptions": { "genDir": "aot/", "skipMetadataEmit": true }, "atom": { "rewriteTsconfig": false } }

We exclude the aot output directory and the AOT bootstrap file (again, as the AOT bootstrap file will have references to Angular Factories that won't exist yet).

Vendor AOT Entry File

Here we add any vendor (or third-party) related libraries - note that we've included the ag-Grid CSS and chosen theme ("Fresh" in this case), as well as included the ag-grid-enterprise dependency.

The ag-grid-enterprise inclusion is only necessary it you're using Enterprise features - it can be ommitted if not.

This time we've dropped @angular/platform-browser-dynamic as we won't be compiling anything at runtime in the browser.

// app/vendor-aot.ts // Angular import '@angular/platform-browser'; import '@angular/core'; import '@angular/common'; import '@angular/router'; // RxJS import 'rxjs'; // ag-grid import 'ag-grid/dist/styles/ag-grid.css'; import 'ag-grid/dist/styles/ag-theme-fresh.css'; import 'ag-grid-angular/main' // for ag-grid-enterprise users only //import 'ag-grid-enterprise/main';

As we'll be pre-compiling our application, we can use the generated factory to bootstrap our application, which will result in much faster load times:

// app/boot-aot.ts import {platformBrowser} from "@angular/platform-browser"; import {AppModuleNgFactory} from "../aot/app/app.module.ngfactory"; // for enterprise customers // import {LicenseManager} from "ag-grid-enterprise/main"; // LicenseManager.setLicenseKey("your license key"); platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

Webpack AOT/Production Configuration

// config/webpack.prod.js const path = require('path'); const webpack = require('webpack'); const helpers = require('./helpers'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const AotPlugin = require('@ngtools/webpack').AotPlugin; const ENV = process.env.NODE_ENV = process.env.ENV = 'production'; module.exports = { devtool: 'source-map', entry: { polyfills: './app/polyfills.ts', vendor: './app/vendor-aot.ts', app: './app/boot-aot.ts' }, output: { path: helpers.root('dist/aot'), publicPath: '/', filename: '[name].[hash].js', chunkFilename: '[id].[hash].chunk.js' }, resolve: { extensions: ['.js', '.ts'] }, module: { loaders: [ { test: /\.ts$/, loader: '@ngtools/webpack' }, { test: /\.html$/, loader: 'html-loader' }, { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader?name=[path]/[name].[ext]' }, { // site wide css (excluding all css under the app dir) test: /\.css$/, exclude: helpers.root('app'), loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader?sourceMap'}) }, { // included styles under the app directory - these are for styles included // with styleUrls test: /\.css$/, include: helpers.root('app'), loader: 'raw-loader' } ] }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['app', 'vendor', 'polyfills'] }), new AotPlugin({ tsConfigPath: './tsconfig.aot.json', entryModule: helpers.root('app/app.module#AppModule') }), new HtmlWebpackPlugin({ template: 'config/index.html' }), new webpack.optimize.UglifyJsPlugin({ beautify: false, comments: false, compress: { screw_ie8: true, warnings: false }, mangle: { keep_fnames: true, screw_i8: true } }), new ExtractTextPlugin({filename: '[name].[hash].css'}), new webpack.DefinePlugin({ 'process.env': { 'ENV': JSON.stringify(ENV) } }) ] };

We don't use a development server with this configuration - we generate the final artifacts in the dist/ folder and expect this to be deploy to a server.

We use the @ngtools/webpack plugin to transpile our Angular code, including the AOT step

Finally, we use the DefinePlugin to provide an environment variable that we can use in our application code to enableProdMode()

if (process.env.ENV === 'production') { enableProdMode(); }

With all this in place, we can now add the following npm scripts to our package.json:

"scripts": { "start": "webpack-dev-server --config config/webpack.dev.js --inline --progress --port 8080", "build": "webpack --config config/webpack.prod.js --progress --profile --bail" },

Now we can either run npm start to run the development setup, or npm run build for the production build. In the case of the production build the generated files will be under the dist/ folder.

If we now run our applicatiom with the above code we will see this:

Override ag-Grid CSS

There are many ways to override the CSS with Webpack, but if you use the configuration above then you can override ag-Grid CSS as follows:

  • Place your application-wide CSS file(s) in a directory other than ./app - for example ./css/. Remember that CSS under ./app is treated differently - it is used for component-scoped styles.
  • In a suitable component - we suggest boot.ts import the CSS you want to include:
  • import '../css/app.css';

And that's it - you can now override ag-Grid CSS with your own in ./css/app.css. For example, the following would set the cell background to green across the board.

.ag-cell { background-color: green; }