Archive for the 'Web' Category

July 22nd, 2017
10:52 pm
AngularJS – Controller Lifecycle and using $scope lifecycle hooks

Posted under AngularJS
Tags ,

This post here answers a basic question I had as to what is the AngularJS controller lifecycle.

The post links to a plnker which demonstrates some of the lifecycle hooks – $viewContentLoaded and $destroy.

These posts relate to the events used/hooked in the example – but more digging is needed to expand on this area:

https://docs.angularjs.org/api/ngRoute/directive/ngView

https://stackoverflow.com/questions/28800426/what-is-on-in-angularjs

https://docs.angularjs.org/api/ng/type/$rootScope.Scope

Unfortunately my first attempt at trying this in an angularjs-up-and-running example failed with $scope being undefined.

I initially used this code pattern to pass $scope into the controller:

angular.module('notesApp', [])

.controller('SubCtrl', [function($scope) {

console.log('SubCtrl has been constructed');
$scope.$on('$viewContentLoaded', function() {
console.log('SubCtrl has loaded');
});
$scope.$on('$destroy', function() {
console.log('SubCtrl is no longer necessary');
})

var self = this;
self.list = [
{id: 1, label: 'Item 0'},
{id: 2, label: 'Item 1'}
];

self.add = function() {
self.list.push({
id: self.list.length + 1,
label: 'Item ' + self.list.length
});
};
}]);

The problem above is that the controller function is passed as an array (to avoid problems during minification) and so $scope has to be passed differently as per this post here.

angular.module('notesApp', [])
.controller('SubCtrl', ['$scope', function(scope) {

console.log('SubCtrl has been constructed');
scope.$on('$viewContentLoaded', function() {
console.log('SubCtrl has loaded');
});
scope.$on('$destroy', function() {
console.log('SubCtrl is no longer necessary');
})

var self = this;
self.list = [
{id: 1, label: 'Item 0'},
{id: 2, label: 'Item 1'}
];

self.add = function() {
self.list.push({
id: self.list.length + 1,
label: 'Item ' + self.list.length
});
};
}]);

The complete working example follows:

app.js

// File: chapter5/need-for-service/app.js
angular.module('notesApp', [])
.controller('MainCtrl', [function() {
var self = this;
self.tab = 'first';
self.open = function(tab) {
self.tab = tab;
};
}])
.controller('SubCtrl', ['$scope', function(scope) {

console.log('SubCtrl has been constructed');
scope.$on('$viewContentLoaded', function() {
console.log('SubCtrl has loaded');
});
scope.$on('$destroy', function() {
console.log('SubCtrl is no longer necessary');
})

var self = this;
self.list = [
{id: 1, label: 'Item 0'},
{id: 2, label: 'Item 1'}
];

self.add = function() {
self.list.push({
id: self.list.length + 1,
label: 'Item ' + self.list.length
});
};
}]);

index.html

<!-- File: chapter5/need-for-service/index.html -->
<html ng-app="notesApp">

<head>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.js">
</script>
<script src="app.js"></script>
</head>

<body ng-controller="MainCtrl as mainCtrl">
<h1>Hello Controllers!</h1>
<button ng-click="mainCtrl.open('first')">Open First</button>
<button ng-click="mainCtrl.open('second')">Open Second</button>
<div ng-switch on="mainCtrl.tab">

<div ng-switch-when="first">
<div ng-controller="SubCtrl as ctrl">
<h3>First tab</h3>
<ul>
<li ng-repeat="item in ctrl.list">
<span ng-bind="item.label"></span>
</li>
</ul>

<button ng-click="ctrl.add()">Add More Items</button>
</div>

</div>
<div ng-switch-when="second">
<div ng-controller="SubCtrl as ctrl">
<h3>Second tab</h3>
<ul>
<li ng-repeat="item in ctrl.list">
<span ng-bind="item.label"></span>
</li>
</ul>

<button ng-click="ctrl.add()">Add More Items</button>
</div>
</div>
</div>
</body>

</html>

No Comments »

June 7th, 2017
10:45 am
Using lite-server to run an angular application

Posted under Angular
Tags ,

lite-server is useful when it is desired to serve an application using actual files, in contrast with ng serve which serves everything from memory. A comparison follows:-

ng serve

This uses webpack to build and serve the application entirely from memory. The consequence of this is that any files (such as external configuration files) which are not specifically part of the angular-cli/webpack bundles/built by webpack will not be available/visible when the application is served. Additional script files can be included in the build bundles by adding to the scripts property in .angular-cli.json, but of course if this is done they are not then external to the build.

lite-server used in development mode

Having installed lite-server globally, it can be used in the following way such that file changes are monitored and updated dynamically in the build :-

under linux:-

ng build –watch && cd dist && lite-server

under windows:-

ng build –watch
rem then under a separate command session:-
cd dist
lite-server

 

lite-server used in production mode

This will do a production build with AOT enabled by default:-

ng build –prod
cd dist
lite-server

No Comments »

June 6th, 2017
5:58 pm
Angular/Ionic–externalising application configuration

Posted under Angular & Ionic & TypeScript
Tags , , , ,

Key Goals

  1. The primary goal is to completely decouple configuration data from the build deployment. This is a key requirement in an enterprise setting, not least because it is vital that the very same build can be tested and signed off in a number of different development and test environments pointing at different server infrastructure.  If a different build was needed for each environment, there is always the risk of an incorrect build, so technically the final production build has never been tested and could break. A good discussion on this may be found on The Twelve Factor App site here.
  2. Upon investigating a pattern for this in Ionic and Angular, I found numerous posts citing different ways around the problem, some appearing quite complex. I was left feeling that this need was not something that had been baked in to the Framework design as a first class citizen – it was certainly not an up front and clear pattern in the documentation. Therefore I wanted a reusable pattern to use as a standard for all my Angular development.
  3. I am using angular-cli with webpack, which is the recommended approach. This produces simple static deployment bundles which are web server agnostic – just an html file which loads one or more js bundles, plus assets such as fonts and images. The pattern for externalising configuration aims to fit in with this and makes no assumptions about the ability to build configuration data dynamically server-side or inject configuration related information into the web server. For example, a common pattern when using Apache Tomcat with Java applications is to inject a configuration location as a JVM parameter into tomcat, which is used later server-side to access the configuration dynamically. We make no assumptions here about the ability to do this kind of trick – any such mechanisms (discussed further below) would be an extension to the pattern for a particular server.
  4. An important goal of the pattern is that it should be testable, i.e. we use dependency injection to inject configuration into the classes that use it, (in our case via a service). This allows unit tests to inject test configuration as required.
  5. The pattern also supports offline use e.g. in an Ionic application. If, as some solutions advocate, the configuration was loaded via an HTTP service in javascript code, this would not work offline with Ionic. The challenge that remains is how and where the configuration would be located in an Ionic deployment, but that is a question for another day and further invesigation.

 

Solution Overview

  1. After a fair amount of investigation I settled on a fairly simple pattern as per this post here. This stackoverflow comment refers to the same technique.  I adapted the technique for my situation – the key points are as follows, and a code sample is shown below.
  2. The basic idea is to load configuration data as a Json object literal from a .js file. This is done by a <script> tag in index.html, prior to the loading of the webpack bundles, which loads the configuration typically as a file from a known location, where the src of the <script> tag is deemed not part of the build.
  3. Whilst this would typically be a location on the same server such as the root directory where index.html is located, it does not have to be, and could equally well be loaded from another server over http. The key point is that the precise src for the script tag is a contract between index.html and any deployment/configuration management software. For example, the configuration file might be dynamically created by Ansible or similar, or the configuration might not be a file at all, and could be dynamically created server side via a web service. These possibilities are all extensions to the basic pattern and depend on the particular web servers and deployment techniques in play.
  4. When running in development mode using ng serve, the bundles are created in memory and no files exist. Therefore this mechanism will not work as the <script> tag will not load anything. We therefore provide a means of having default configuration in the code which will be overridden by the above configuration file if present. If the configuration file is not present (ng serve in dev mode) you will get a 404 for the script tag on the console, which can be ignored.
  5. When running in development mode using files (e.g. via lite-server using  ng build –watch and running lite-server from the dist subdirectory), a config file can be used in the normal way to override the default config if desired.
  6. The default configuration object is exported as a constant from the appropriate environment file, e.g. environment.ts. Whilst there could be default settings in environment.prod.ts, the default is typically set to null in this case so that a configuration file is always used.
  7. The structure of the configuration data is typed via an AppConfig interface which is used everywhere the data is loaded or referenced. In simple cases the configuration data may be just properties of the root AppConfig object. However in more complex cases the object graph could be extensive.
  8. The configuration file loads the configuration object into a global variable which is then read by the AppConfigService, and if present overrides the default configuration. Object.freeze() is used (along with readonly properties on the AppConfig interface) to make the configuration data immutable. Whilst in theory it may be possible to inject the resulting configuration as a constant using a value provider, when I tried this (with the latest Angular 4) I hit a variety of intermittent compile/build errors citing ERROR in Error encountered resolving symbol values statically. In the end I went with an injected configuration service, which consuming classes set up as a getter to allow convenient access.
  9. The AppConfigService loads the global configuration variable via a casted reference to the window object. I tried to get this to work via a declare to reference it as an external object but again I hit compile errors when doing so.

 

Example Code Listings

 

index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Places Admin</title>

<base href=”/”>

<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link rel=”icon” type=”image/x-icon” href=”favicon.ico”>

<script type=”text/javascript” src=”appConfig.js”></script>

</head>
<body>
<app-root>Loading…</app-root>
</body>
</html>

appConfig.js – external configuration file.

// Application Configuration
var salientSoft_appConfig = {
  appTitle: "The Places Guide",
  apiBase: "http://localhost:5984/places-guide/",
  messageTimeout: number = 3000
};

environment.ts

// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

import {AppConfig} from ‘../config/app-config’;

export const environment = {
production: false,
};

export const defaultAppConfig: AppConfig = {
appTitle: ‘The Places Guide’,
apiBase: ‘http://localhost:5984/places-guide/’,
messageTimeout: 3000
};

environment.prod.ts

import {AppConfig} from '../config/app-config';

export const environment = {
production: true
};

export const defaultAppConfig: AppConfig = null;

appConfig.ts – config file interface

/* Root application configuration object
   This encapsulates the global config json properties.
   These must match what is in the actual config file, normally /appConfig.js.
 */
export interface AppConfig {
    readonly appTitle: string;
    readonly apiBase: string;
    readonly messageTimeout: number;
}

app-config.service.ts

import {Injectable} from '@angular/core';
import {AppConfig} from './app-config';
import {defaultAppConfig} from '../environments/environment';

@Injectable()
export class AppConfigService {
private _appConfig: AppConfig;
buildConfig(): AppConfig {
const appConfig: AppConfig = (<any>window).salientSoft_appConfig || defaultAppConfig;
return Object.freeze(appConfig);
}
constructor() {
this._appConfig = this.buildConfig();
}
get(): AppConfig {
return this._appConfig;
}
}

app.component.ts – example configuration consumer

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

constructor(
private appConfigService: AppConfigService
) {}

public get appConfig(): AppConfig {return this.appConfigService.get(); }

ngOnInit(): void {
document.title = this.appConfig.appTitle;
}

No Comments »

May 29th, 2017
1:18 pm
CouchDB View emitting the same document twice

Posted under CouchDB
Tags , , ,

I had a bug whereby my view was emitting the same place document twice. This was strange as the database clearly did not contain 2 copies of the document. I had seen what appeared to be a similar issue before bud at the time did not track down the cause.

It turned out to be a bug in the view definition. The original (buggy) version follows:-

function (doc) {
  if (doc.type == ‘place’) {
      emit([doc.address.postTown, doc.address.locality, doc.name, doc._id, 0], null);
      emit([doc.address.postTown, doc.address.locality, doc.name, doc._id, 1], {_id: doc.placeFeaturesId});
  }
}

The problem with this is that when doc.placeFeaturesId is null/undefined, the second emit emits its key plus an “_id” with an undefined/null value in the generated json. The result of this is that when using ?include_docs=true to include the documents, CouchDB applies a default behaviour due to the null, and emits the place document again a second time. The following fix prevents the second emit when there are no placeFeatures present, which prevents the place document being emitted a second time. This solves the problem:-

function (doc) {
  if (doc.type == ‘place’) {
      emit([doc.address.postTown, doc.address.locality, doc.name, doc._id, 0], null);
      if (doc.placeFeaturesId) {
        emit([doc.address.postTown, doc.address.locality, doc.name, doc._id, 1], {_id: doc.placeFeaturesId});
      }
  }
}

The lesson here is to be careful to handle undefined or null values in the view code when emitting entries in a view definition as this can cause strange behaviour such as the duplicates above.

No Comments »

May 26th, 2017
12:55 pm
Dynamically updating css classes on child components from a parent property

Posted under Angular & CSS & PrimeNG
Tags , , ,

I was using a button component to encapsulate the primeNG one, to allow it to responsively change to an icon only button at smaller screen widths. Whilst in theory I could have dynamically modified the internal primeNG button CSS, this was complex and I did not want  to become tightly coupled to it. My button component therefore simply selected one of 2 buttons via a media query, setting the unwanted one to display:none.

I wanted any classes added to the top level ss-button to be set on the underlying primeNG buttons. One use of these buttons was in a home brewed tab component, and  I was dynamically changing the button colour via ui-button-primary, ui-button-secondary so that the active tab had a ‘primary’ coloured button and the inactive ones were ‘secondary’ which caused them to become uncoloured ghost buttons – ideal for my requirement.

During refactoring to replace the ‘full size only’ primeNG buttons with the resizing ss-buttons I introduced a bug and the colour change failed to work. The initial (incorrect) refactored code was as follows:-

ss-tab-toolbar.component.html

<ng-container *ngFor="let tab of tabs">
<app-ss-button [label]="tab.label" [icon]="tab.icon" (click)="setActiveTab(tab)"
[ngClass]="{'ui-button-primary': isActiveTab(tab), 'ui-button-secondary': ! isActiveTab(tab)}"></app-ss-button>
</ng-container>

ss-button.component.html

<button pButton type="button" [title]="label" [label]="label" [icon]="icon" class="ss-button icon-text"
[ngClass]="class" ></button>
<button pButton type="button" [title]="label" [icon]="icon" class="ss-button icon-only"
[ngClass]="class" ></button>

ss-button.component.ts

@Component({
selector: 'app-ss-button',
templateUrl: './ss-button.component.html',
styleUrls: ['./ss-button.component.scss']
})
export class SSTabButtonComponent {

@Input() label: string;
@Input() icon: string;
@Input() class: string;

constructor() { }
}

The issue was that I was using [ngClass] dynamically to update the classes on the <app-ss-button> in ss-tab-toolbar.component. html. This used to work for the PrimeNG buttons, but now would have no effect as the classes need to be changed on the child buttons not the encapsulating parent. A simple change of [ngClass] to [class] fixed the problem, as the parent style change then updated the property on the parent component which was then picked up by the children correctly.

modified ss-tab-toolbar.component.html

<ng-container *ngFor="let tab of tabs">
<app-ss-button [label]="tab.label" [icon]="tab.icon" (click)="setActiveTab(tab)"
[class]="{'ui-button-primary': isActiveTab(tab), 'ui-button-secondary': ! isActiveTab(tab)}"></app-ss-button>
</ng-container>

Happily the dynamic nature of angular meant that this worked perfectly for tab changes – the change on the parent class was propagated correctly to the child and the button colours changed correctly.

I was wondering initially whether I would need to involve observables to make this work but this was not the case – this kind of dynamic propagation pattern is worth remembering in future.

No Comments »

May 18th, 2017
6:30 pm
Tricks with the PrimeNG Grid CSS Component

Posted under Angular & CSS & HTML & PrimeNG
Tags ,

Update 19/5/2017

I noticed that my original attempt below, whilst causing the text div to flow under the image div at smaller screen sizes, did not cause the text div to be full width when it was sitting underneath. To do this properly I needed to use a combination of 2 specific non-default (i.e. not ui-g-*) responsive style classes on the text div, in this case ui-md-9 and ui-sm-12. This causes the following:-

  1. At the u-md-* screen size boundary, and higher, the ui-md-9 kicks in and the image and text are on the same line.
  2. Below the ui-md-9 boundary, the ui-sm-12 kicks in and allows the text div to be full width. Without this, it would sit underneath but the default ui-g-9 would still be in effect so it would not switch to full width.

The modified code fragment for the item-text div is as follows:-

<div class="ui-g-9 ui-sm-12 ui-md-9 item-text">

Original Post

I did this quite by accident, due to adding an unwanted “>” on the end of a ui-g-3 class for the div around the left image in a data list – it caused the class to be ignored.

The code (with the offending character and the ui-g-3 class removed) is as follows:-

<ng-template let-place pTemplate="item">
<div class="ui-g list-item item-image">
<div class="item-image">
<img src={{place.thumbUrl}}>
</div>
<div class="ui-g-9 item-text">
<h4>{{place.shortLocation}}</h4>
<h3>{{place.name}}</h3>
<p>{{place.strapline}}</p>
</div>
</div>
</ng-template>

The scss for the component is here (note this is from the component scss file so is encapsulated by Angular from bleeding out):-

h3 {
font-size:1.1em;
font-weight: normal;
margin:.2em 0;
}
h4 {
font-size:1em;
font-weight: normal;
margin: 0;
}
p {
font-size:1em;
margin:.2em 0;
}
.list-item {
padding:.5em;
border-bottom:1px solid #D5D5D5;
display:flex;
align-items: center;
}
.list-item .item-image {
padding-right:1em;
}
.list-item .item-text {
padding-left:0;
}

The happy effect of this was that the image div did not have a ui-g-* class to size it, so had a fixed size determined by the thumbnail, and was not a floating div. When I used a ui-g-3 class for it by removing the offending “>”, then the gap between the image and the text increased as the width changed, which is not something I wanted – the fixed padding there due to the bug was fine for all screen widths.

The ui-g-9 class happily floated next to it and flowed underneath as required. Different ui-g-* classes such as ui-g-6 and ui-g-8 just caused the flow to happen at a different width point.

I could probably have achieved a similar effect by using a ui-g-3 class for the image as I had intended, and then explicitly fixing the width for that div (or e.g. setting a min-width and a max-width).

I may need to do this or re-jig it if I have to cater for different thumbnail sizes, but my preferences is to scale all thumbnails to the same size as it gives a much better layout.

Whether this trick technically breaks or violates the ui-g-* grid css rules I am not sure, the documentation on them is not hugely comprehensive, but I will ponder this one in due course.

For now it works fine!

No Comments »

May 18th, 2017
5:56 pm
Vertically Centering items in a div–2017 version!

Posted under Angular & CSS & PrimeNG & Web
Tags ,

I posted about this kind of issue back in 2010 here.

Nowadays for my own project work I am happy to assume recent browsers, in particularly IE11 at least (noting that even this has been out a while now – it was released 4 years ago now so a reasonable start point).

This means that I am happy to use css Flex which makes this insanely simple compared to the old days. You just need display:flex;align-items:center in the container div and its content items will centre vertically.

I’ve just used this on a PrimeNG data list component to centre left thumbnails correctly in a list item with no issues.

Quite why we have had to suffer for so many years with arcane tricks (or risk ridicule by making it simple using tables for layout) is beyond me, but there we are.

No Comments »

May 18th, 2017
11:22 am
Webstorm IDE–Oragnising Imports Tips/Gotchas

Posted under TypeScript & WebStorm
Tags ,

1/ Organising imports does not seem to happen automatically when it should. To organise imports for a given folder or the project, select the appropriate level in the navigator then hit Code/Optimise Imports or ctrl+alt+0 as detailed here.

2/ TSLint gives errors about double quotes in imports as it prefers single quotes in all the code (double quotes for html), but Webstorm uses double quotes by default when organising imports. You can change Webstorm to use single quotes as per this post here.

No Comments »

May 15th, 2017
4:07 pm
Angular–using parent component references

Posted under Angular & TypeScript
Tags ,

I had a need to do this initially when implementing my own tab container component.

I wanted each tab to hold a parent reference to the container so that it could get the container’s active tab, to see if ‘it’ was the active tab.

There is significant discussion about this online. The parent can be injected directly via its class name, but this introduces tight coupling which is undesirable.

Another option discussed was the use of @Host in conjunction with injection, as per the angular docs here. In this case, the traversal of the injector  tree stops at the parent otherwise another compatible component could conceivably be injected from elsewhere.

My solution initially was to define parent and child interfaces which defined the interaction between them, as one would in Java. This decoupled them from each other. However, in Angular you cannot inject interfaces directly as they are not available at runtime. As I was already iterating the content children (the tabs) in the parent container, in the ngAfterContentInit() lifecycle hook (as this is the earliest point at which the content children are available), it was straightforward to manually assign the parent reference to each child via a method in the child interface. In this way the parent and children were decoupled – any parent supporting the container interface (used by the child) could host any children which used the child interface (used by the parent).

In the end I dumped the idea as it ended up a tad unwieldy – I only needed the child to detect if it was the active one and it was having to call a parent method to compare itself with the currently active tab to see if it was the one. This child element property was then used to ensure that only the content of the active tab was actually rendered. I finally just added a boolean active property on the child interface, and in the parent, when switching tabs, I cleared the active flag on the old active tab (if there was one) and set the active flag on the new active tab, as well as assigning a parent property for the currently active tab. Whilst this meant I was having to track the active flags correctly, I no longer had to handle parent references and parent method calls. This seemed less messy but it was a close call and either pattern in reality would be ok.

One point of note is that in Typescript, an interface can contain properties (as in fields in Java) as well as methods, so for example my interface for the child could contain active and default properties directly rather than having to additionally define isActive() and isDefault() methods.

No Comments »

May 15th, 2017
1:46 pm
Angular–dynamic building of parent component markup based on content children

Posted under Angular
Tags ,

I had a use case where I was rolling my own simple tab container.

An interesting challenge of this use case was the fact that the tabs needed rendering in their own right, but also needed iterating separately in order to build the tab header bar.

This is an example use case where content markup might influence parent markup dynamically and in multiple areas, and that attributes of the content markup are used in parent markup.

To do this I used the @ContentChildren decorator to inject an array of the tabs into the parent tab container. I could then easily iterate the array in the parent markup to build the header bar with labels and icons declared in the content children. In addition, the content children were rendered in their own right with their own content in different ways using <ng-content select=”…”></ng-content>.

No Comments »