Friday, August 25, 2017

How to Force Refresh a Single Page

How to Force Refresh a Single Page

Before you go straight to clearing your entire browser cache, one trick you can try is something called a “force refresh”. Normally, when you refresh a page, your browser still serves up the cached version of the page, rather than downloading all of the assets again. But you can actually bypass the cache and force a complete refresh by using some simple hotkeys:
  • Windows and Linux browsers: CTRL F5
  • Apple Safari: SHIFT Reload toolbar button
  • Chrome and Firefox for MacCMD + SHIFT R
Again, this method only bypasses the cache for the specific page that you’re on. It leaves the rest of your browser cache untouched. Therefore, this option is great if you’re experiencing issues with just a single page. But if you want to completely reset your experience (say, across an entire site), you’re better off just clearing your entire browser cache.

Thursday, August 24, 2017

DataTable in an Angular

There are several data table widgets for Angular, but none of them matched our project’s needs. I’m sure that’s simply a matter of time. Angular2 is young, and the third-party libraries are even younger. They simply didn’t have enough time to accumulate features and maturity. So why don’t we use one of the seasoned JavaScript data tables?
As it turns out, Louis Lin had the same idea creating the Angular DataTables project. It looks promising, but at the time of writing, it was only 26 days old. So the feature list is pretty short. When you’re reading this article, things have probably improved. However, today I don’t want to tell you how to use a third-party data table. Instead, I’ll tell you how to do it yourself. Before we start to wallow in the source code, let’s start with what developers usually do: a short market survey. If you’re in a hurry, skip that section or jump directly to the source code of the demoon GitHub.

Short Angular data table market survey (as of Jan 21, 2016)

I’ve already mentioned Louis Lin’s project. He does exactly what we’re doing later in this article: integrating the table from DataTables.net. The demos look nice, and I guess you can pass almost every option of the JavaScript widget to the angular-datatables. If my guess is correct, compensates the lack of features the demos show.
“Lack of features” is not the word coming to mind looking at the demo of Swimlane’s ngx-datatable. The only reason we didn’t use it was because our project uses Bootstrap. ngx-datatable supports theming, so it’s possible to provide a Bootstrap theme yourself. Maybe we’ll do that later.
The ng2-table of valor-software is great because it’s extremely simple. You provide a column description, pass data to the table and that’s it. We used this table in our project for a couple of weeks. However, we needed editable cells, and we needed to select rows, so this table was too simple for us.
Another interesting table is the ng2-smart-table. Unfortunately, the fast pace of Angular updates breaks the project every once in a while. We didn’t use it simply because it wasn’t compatible with our version of Angular when we evaluated the table. Keep in mind that this is probably only a matter of time. Chances are the bug has been fixed when you read this article.
The grids of Kendo and Wijmo are almost certainly great choices, too. Too bad they are not available for free. So we chose to evaluate the other choices.
The documentation of the ngx-datatable lists several other alternatives. I didn’t examine them, so suffice it to mention them: ng2-super-tablevaadin-gridangular2-iron-data-table (which is built on a Polymer datatable) and another Material Design table, the paper-datatable.

Do it yourself (but don’t do it from scratch)

The next option is to use one of the existing data tables and integrate them in an Angular application. I chose the table from DataTables.net, which is build on jQuery. That’s a fine library we also use in the BootsFaces project. I could have chosen Chinese data table just as well. But I’m more familiar with DataTables.net.
There are several challenges to overcome: for instance, the Angular life-cycle isn’t exactly compatible with the jQuery events. Adding insult to injury, the data table is available on NPM, but it seems to support Common.js, while the Angular CLI uses another module system.
By the way, if you don’t use the Angular CLI, I recommend the article of 
Mitch Talmadge
, who integrates DataTables.net using Webpack.

Adding the dependencies to the package.json

Back to the Angular CLI approach. The first step is to install the dependencies:
1
2
3
4
5
6
7
npm install bootstrap --save
npm install datatables.net --save
npm install datatables.net-bs --save
npm install datatables.net-select --save
npm install datatables.net-select-bs --save
npm install jquery --save
npm install @types/jquery --save-dev
It’s also a good idea to add types for the DataTable widget. As far as I can see, there are several type definition files. But there’s no official type definition which is regularly maintained. This article simply casts the type to any. That’s far from ideal, but it works. If you know a good type definition file, please leave a comment so I can improve this article.

Adding the files to the angular-cli.json

Next we add the CSS files to the angular-cli.json file:
1
2
3
4
5
6
7
"styles": [
   "styles.css",
   "../node_modules/bootstrap/dist/css/bootstrap.min.css",
   "../node_modules/bootstrap/dist/css/bootstrap-theme.min.css",
   "../node_modules/datatables.net-bs/css/dataTables.bootstrap.css",
   "../node_modules/datatables.net-select-bs/css/select.bootstrap.css"
 ],

Creating the component

Now it’s time to create the component for the data table using the command line command "ng g c datatable".
The HTML template file of the component is straight-forward, pretty much the way it’s documented on the DataTables.net page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
   <thead>
   <tr>
     <th>First name</th>
     <th>Last name</th>
   </tr>
   </thead>
    <tbody>
   <tr *ngFor="let row of data; let i = index">
     <td>{{row.name}}</td>
     <td><input [(ngModel)]="row.lastName" style="color:black"></td>
   </tr>
   </tbody>
 </table>
This generates a table with a static cell and input fields in the second column.

TypeScript imports

The component class imports both jQuery and the Datatable like so:
1
2
import * as $ from 'jquery';
import 'datatables.net'
The second import statement circumvents the module system of TypeScript. It simply registers the DataTable as a jQuery plugin, which is fine by us.

Initializing the DataTables.net widget

The component initializes the data table in the ngAfterViewInit method. This relieves us from having to write an onDocumentLoad handler we’d use without Angular. Not a big deal, but still, it improves readability.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export class DatatableComponent implements OnInit {
 public tableWidget: any;
 
 ngAfterViewInit() {
    this.initDatatable()
  }
 
  private initDatatable(): void {
    let exampleId: any = $('#example');
    this.tableWidget = exampleId.DataTable({
      select: true
    });
  }
  ...
}

Adding and deleting rows

At first glance, the table already looks great. It’s even possible to add or remove array elements, and the data table is redrawn. But there’s a catch: sorting and filtering ignores the new rows.
So adding and deleting rows requires us to redraw the table. If you’ve got a simple table without input fields, it’s possible to use the data object of the data table to add or delete rows. Unfortunately, the input field prevents that. I’m sure there’s a better way, but I solved the problem by a brute-force approach: destroying and re-initializing the table does the trick. The disadvantage is that the table flickers. If you know how to do it better, please leave a comment. Until then, I’ll show you the brute force approach:
1
2
3
4
5
6
7
8
9
10
11
12
private reInitDatatable(): void {
   if (this.tableWidget) {
     this.tableWidget.destroy()
     this.tableWidget=null
   }
   setTimeout(() => this.initDatatable(),0)
 }
 
 public deleteRow(): void {
   this.data.pop();
   this.reInitDatatable()
 }
The timeout is needed to make sure that the data table is initialized after Angular has redrawn the page. Otherwise, you may end up with the message “no data” under a long table.

jQuery

Remains the question how to deal with jQuery events. By default, modifying attributes in a jQuery event listener doesn’t trigger Angular’s change detection. We can fix this using an event emitter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Output() rowSelected: EventEmitter<number> = new EventEmitter();
 
private initDatatable(): void {
  let exampleId: any = $('#example');
  this.tableWidget = exampleId.DataTable({
    select: true
  });
  this.tableWidget.on('select',
    (e, dt, type, indexes) => this.onRowSelect(indexes))
}
 
private onRowSelect(indexes: number[]): void {
  this.rowSelected.emit(indexes[0])
}
For some reason, during my tests, the array indexes always contained a single number, no matter how many rows I’d selected. That’s why the event emitter only broadcasts a single number instead of the entire array.

Using the component

After these preparations, using the data table is straight-forward:
1
<datatable (rowSelected)="onRowSelected($event)"></datatable>
1
2
3
4
5
6
export class AppComponent {
 
  public onRowSelected(index: number) {
    console.log(index)
  }
}

Wrapping it up

This article has become a bit long, which may give you the impression integration a JavaScript data table library in an Angular application is difficult. In reality, it’s pretty simple. The solution I presented is far from ideal, but it’s good enough for most use cases, and it took me merely a couple of hours to implement it. Truth to tell, writing this article took more time than writing the actual code.
The extra value the full-blown Angular2 libraries give you is basically syntactical sugar (which is great!) and a better integration with the Angular life cycle. The trade-off is that these libraries are usually opinionated in one way or another.

Dig deeper