Blog Archives

April 27th, 2017
4:15 pm
Angular 2 – Reactive Programming/Observables with multiple HTTP API calls

Posted under Angular
Tags , ,

A common pattern used in service layers is to make calls to an underlying repository or API, and then map the results to domain layer objects via mappers/convertors. Often, it may be necessary to make multiple repository calls and then combine them in the mapping phase to create the resulting domain object graph which is then returned. With traditional synchronous calls, e.g. to an underlying JPA repository, this is a straightforward exercise.

However, Angular 2 by default makes HTTP api call results available asynchronously as Observables, which poses an immediate issue with how to combine the results. I certainly did not want to embed Observables as properties within my domain objects, as I wanted the domain objects as POTOs (Plain Old Typescript Objects) and did not want to couple them/pollute them with Observables.

Handling the above pattern is in fact straightforward, but does involve a paradigm shift compared with imperative OO style programming as you need to get your head around reactive programming, in this case ReactiveX/RxJS which Angular uses.

  • In this case we are using RxJs Observables for which the api is documented here.
  • The available operators for use with Observables are documented here.
  • ReactiveX/RxJs makes extensive use of Marble diagrams an example of which may be found here.
  • Another useful introduction site to RxJS may be found here.

Note that it is possible to convert the observables to promises and use promise chaining to achieve similar results. However, I decided to stick with observables as the majority of comments online such as this post indicate that observables can do all that promises can, and more, and they are the default/chosen direction for Angular 2. It felt counter intuitive to have to convert the observables to promises all the time when there was no real need – whilst  and to do so appeared to go against the chosen way forward for Angular 2.

My first prototype needed to combine 2 API calls, and for this I used the combineLatest instance method. (This is called on one Observable and allows one or more other observables to be combined with it. There is also a static method available on the Observable class.) I found this solution in this StackOverflow post. These methods do do the following :-

  1. Combine 2 or more observables and emits a single output observable
  2. Before the output observable emits an item, all of the input observables must have emitted at least one item
  3. Once all the observables have emitted something, the output emits an output item.
  4. It continues to emit items whenever any of the input observables emit another item.
  5. Note that in our case, multiple emits for an observable do not occur. Whilst we are using observables, each one will only emit a single output item in our case, when the HTTP call completes.
  6. Note that per the example code below, I use lambdas (=> operator) routinely. The lambda used on the return of combineLatest will be called when the output observable emits an item (i.e. when both the HTTP calls have completed). The other lambdas are just used for standard synchronous filter/map/reduce operations on the arrays – these are standard in Javascript and hence Typescript, so they are not asynchronous/using observables.

For the requirement of my original pattern, i.e. make a number of repository calls, then map them all to a domain object graph, the above approach works fine. Note that there are a number of other operators on observables which are quite nuanced and this example just scratches the surface of Reactive programming, but in my experience the above is a very common pattern. Some of the other operators/methods distinguish between emits on an observable and the observable actually closing. As in the above case we just get one emit from each, we don’t need to get into that issue.

An outline example from my prototype code follows (imports and domain objects omitted for clarity). Note that it is just a prototype and a number of issues have not been properly addressed yet (such as externalisation of configuration data such as API urls):-

places-service.ts

@Injectable()
export class PlacesService {

static apiBase: string = 'http://localhost:5984/places-guide/';
static apiGetByLocationName: string = `${PlacesService.apiBase}_design/places/_view/placesByLocationName?include_docs=true`;
static apiGetFeatures: string = `${PlacesService.apiBase}_design/places/_view/featuresByOrdinal?include_docs=true`;

constructor(private http: Http, private featureMapper: FeatureMapper, private placeMapper: PlaceMapper) {}

fetch(): Observable<Place[]> {
return this.http.get(PlacesService.apiGetByLocationName)
 
       //The lambda here is called when the output observable emits an item (which only happens once when both HTTP calls complete).
.combineLatest(this.http.get(PlacesService.apiGetFeatures), (placesRows, featuresRows) => {
let features: Feature[] = this.featureMapper.mapAll(featuresRows.json().rows);
let places: Place[] = this.placeMapper.mapAll(placesRows.json().rows, features, PlacesService.apiBase);
return places;
});
}
}
 
place-mapper.ts
@Injectable()
export class PlaceMapper {

constructor(private placeFeaturesMapper: PlaceFeaturesMapper) {};

public map(row: any, placeFeatures: PlaceFeatures, apiBase: string): Place {
let place: Place = Place.create(
row.id,
row.doc.name,
row.doc.strapline,
row.doc.description,
Address.create(
row.doc.address.address1,
row.doc.address.locality,
row.doc.address.postTown,
row.doc.address.postCode),
row.doc.lat,
row.doc.long,
row.doc.website,
row.doc.phone,
row.doc.email,
placeFeatures,
apiBase + row.doc._id + "/thumbnail.jpg",
apiBase + row.doc._id + "/detail.jpg"
);
return place;
}

public mapAll(rows: any[], features: Feature[], apiBase: string){
let placesPlaceFeaturesMap: {[key: string]: PlaceFeatures } = this.createPlacesPlaceFeaturesMap(rows, features);
return rows
 
      //this is standard array filtering and mapping with lambdas
.filter(row => row.doc.type == 'place')
.map(row => this.map(row, placesPlaceFeaturesMap[row.doc.placeFeaturesId], apiBase));
}

createPlacesPlaceFeaturesMap(rows: any[], features: Feature[]): {[key: string]: PlaceFeatures } {
let placesPlaceFeaturesMap: {[key: string]: PlaceFeatures } =
rows.filter(row => row.doc.type == 'placeFeatures')
        //This is a standard array lambda. Reduce is used as we are populating a single return object.
        .reduce((map, row) => {
map[row.doc._id] = this.placeFeaturesMapper.map(row, features);
return map;
 
        //Note that the {} is the seed/initial value for the reduce, where we initialise the empty output object
}, {}
      );
return placesPlaceFeaturesMap;
}
}
place-features-mapper.ts
 
@Injectable()
export cl

ass PlaceFeaturesMapper {

public map(row: any, features: Feature[]): PlaceFeatures {
let list: PlaceFeature[] = this.buildList(row.doc._id, row.doc.placeFeatures, features );
let map: {[key: string]: PlaceFeature} = this.buildMap(list);
return PlaceFeatures.create(row.doc._id, list, map, features);
}

buildList(placeFeaturesId: string,
rawPlaceFeatures: {[key: string]: {rating: number} },
features: Feature[]): PlaceFeature[] {
let placeFeatures: PlaceFeature[] = features
.filter(feature => rawPlaceFeatures[feature.id])
.map(feature => PlaceFeature.create(
placeFeaturesId,
feature.id,
feature.ordinal,
feature.name,
feature.description,
feature.searchable,
feature.rateable,
rawPlaceFeatures[feature.id].rating)
);
return placeFeatures;
}

buildMap(placeFeatures: PlaceFeature[]): {[key: string]: PlaceFeature} {
let placeFeaturesMap: {[key: string]: PlaceFeature} =
placeFeatures.reduce((map, placeFeature) => {
map[placeFeature.featureId] = placeFeature;
return map;
}, {});
return placeFeaturesMap;
}
}

 
feature-mapper.ts
@Injectable()
export class FeatureMapper extends AbstractMapper<any, Feature> {

public map(row: any): Feature {
let feature: Feature = Feature.create(
row.id,
row.doc.ordinal,
row.doc.name,
row.doc.description,
row.doc.searchable,
row.doc.rateable);
return feature;
}
}
 
abstract-mapper.ts
export abstract class AbstractMapper<R,T> {
public abstract map(row: R): T;

public mapAll(rows: R[]): T[] {
return rows.map(row => { return this.map(row);});
}
}
 

No Comments »

April 26th, 2017
8:21 am
Ionic 2 Super Starter template application – Cannot find module "ionic-native"

Posted under Ionic
Tags , ,

This template is noteworthy as it contains a number of simple ready made use cases/patterns such as swipe gesture support, master/detail pages, searching, and simple entry forms.
As per this post, a tutorial starter app with the above template should be created as follows:-

ionic start my-super-starter-app super –v2
npm install

When I tried to run this I got the following error trace:-

Runtime Error

Cannot find module "ionic-native"

Stack

Error: Cannot find module "ionic-native"
at g (http://localhost:8100/build/polyfills.js:3:7133)
at Object.<anonymous> (http://localhost:8100/build/main.js:108352:7)
at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
at Object.<anonymous> (http://localhost:8100/build/main.js:82204:73)
at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
at Object.<anonymous> (http://localhost:8100/build/main.js:120594:70)
at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
at http://localhost:8100/build/main.js:66:18 at http://localhost:8100/build/main.js:69:10

Ionic Framework: 3.0.1
Ionic Native: ^3.5.0
Ionic App Scripts: 1.3.0
Angular Core: 4.0.0
Angular Compiler CLI: 4.0.0
Node: 7.5.0
OS Platform: Windows 7
Navigator Platform: Win3
User Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36

This Ionic forum post documents the problem, which appears to be because the starter template expects A version of ionic-native lower than 3 (mine was 3.5.0 as above). As per the post, I tried the following:-

  1. Edited my package.json and changed the ionic-native version to 2.9.0
  2. Entered npm install ionic-native –save

This did not work, it failed with a similar error.

Fortunately the post links to an updated template as the solution, in github here.

This can be cloned and used instead of entering ionic start my-super-starter-app super –v2

I tried the fixed version and it worked correctly. Note ref the comments in the various posts that it was not necessary to install ionic-native explicitly via npm install ionic-native –save

No Comments »

April 25th, 2017
6:52 pm
Ionic V2–starter app creation, folder structure, and menu toggle issues

Posted under Ionic
Tags , , ,

A new Ionic 2 application should be created via ionic-cli as follows:-

ionic start my-new-app tabs –v2

Note that the v2 switch is mandatory, as without it (and running ionic 2.2.1) I got a different folder structure which did not put the application code under a top level src/ directory – whilst this tutorial does not mention the switch, it is mentioned here.

Note also that the tabs keyword is an app template which creates a tabbed app. There are other choices as noted here .

The super starter template is noteworthy as it has simple examples of master/detail lists, searching, and simple forms. I tried this and had some issues getting it working but there is a fixed version which worked. I have documented this here.

Note also that this post here mentions using an earlier version of node (4.x) to avoid a number of issues. I have not tried that, as the current node version I have installed as I type is v7.5.0, which makes 4.x a pretty old version.

I may try the older one though, to see if the issues I hit with the menu toggle on the Nav bar menu might be solved. The issue I have hit is that the menu icon on the nav bar intermittently does not open the side menu. Typically clicking the same tab icon again or visiting another tab makes it work, such that it alternately fails and works. No solution has yet been found for this and I could not find any reference to it online.

Note that the default generated folder structure for —   v2 creates an app directory under /src, which is used for core app configuration stuff. I did attempt (mistakenly I now feel) putting the component and service layers under /src/app, but have now reverted that. Whilst this worked, when I then tried to move (with a webstorm refactor) /src/pages/ to /src/app/pages/ I hit numerous issues with  template errors/ html files not found when running ionic serve. I checked all the import and related configuration, and whilst some was incorrect, I eventually went through and corrected every .ts file individually and still had issues. This appears to be an issue – moving the /src/pages/ directory (for an app with the tabs template, as per the above ionic command) appears to break some hidden configuration that I could not find.

No Comments »

April 22nd, 2017
3:54 pm
Typescript typing for unknown/arbitrary types– any vs Object vs {}?

Posted under TypeScript
Tags ,

I was looking into this in order to find the best type for an arbitrary stream of Json.

This comment post here has an interesting discussion on this, and concludes that any is the most flexible, and there aren’t strong reasons for using the Object or {} (which are similar, as {} extends Object).

This post describes how to use TypeScript interfaces to describe the Json.

http://json2ts.com is a site that automatically creates the interfaces from the json for you, however in my case it did nothing when I gave it a returned CouchDB Json stream.

This stackoverflow post also discusses the issue and mentions json2ts.com – the poster evidently had more luck with it than I did.

No Comments »

April 22nd, 2017
2:54 pm
Declaring Providers for Dependency Injection

Posted under Angular
Tags ,

Providers need to be declared as they ‘provide’ the objects to be injected – in many cases this will be a singleton reused whenever injected (much like a normal Spring bean in Java).

To be visible everywhere in the entire application, they should be declared in the Providers list in the root @NgModule  (e.g. app.module.ts).

However if not used globally, they can be for example registered in a component whereupon they will be visible to that component and its children only.

This angular doc here discusses dependency injection and where to register providers.

This angular doc here is the NgModule faq and also discusses the issue. It points out that registering the providers in the root AppModule (@NgModule as above) is the normal use case, and other cases are rare. In particular it makes the following points:-

Should I add application-wide providers to the root AppModule or the root AppComponent?

Register application-wide providers in the root AppModule, not in the AppComponent.

Lazy-loaded modules and their components can inject AppModule services; they can’t inject AppComponent services.

Register a service in AppComponent providers only if the service must be hidden from components outside the AppComponent tree. This is a rare use case.

More generally, prefer registering providers in modules to registering in components.

Discussion

Angular registers all startup module providers with the application root injector. The services created from root injector providers are available to the entire application. They are application-scoped.

Certain services (such as the Router) only work when registered in the application root injector.

By contrast, Angular registers AppComponent providers with the AppComponent‘s own injector. AppComponent services are available only to that component and its component tree. They are component-scoped.

The AppComponent‘s injector is a child of the root injector, one down in the injector hierarchy. For applications that don’t use the router, that’s almost the entire application. But for routed applications, "almost" isn’t good enough.

AppComponent services don’t exist at the root level where routing operates. Lazy-loaded modules can’t reach them. In the NgModule page sample applications, if you had registered UserService in the AppComponent, the HeroComponent couldn’t inject it. The application would fail the moment a user navigated to "Heroes".

Should I add other providers to a module or a component?

In general, prefer registering feature-specific providers in modules (@NgModule.providers) to registering in components (@Component.providers).

Register a provider with a component when you must limit the scope of a service instance to that component and its component tree. Apply the same reasoning to registering a provider with a directive.

For example, a hero editing component that needs a private copy of a caching hero service should register the HeroService with the HeroEditorComponent. Then each new instance of the HeroEditorComponent gets its own cached service instance. The changes that editor makes to heroes in its service don’t touch the hero instances elsewhere in the application.

Always register application-wide services with the root AppModule, not the root AppComponent.

No Comments »

March 20th, 2017
6:44 pm
CouchDb access issues from local network

Posted under CouchDB & Kaspersky
Tags ,

I was unable to access a couchDb from another computer on the local network.

There were 2 issues that needed resolving to address this:-

1/ By default, CouchDb binds to 127.0.0.1:5984, and therefore is only accessible if you use this address (i.e. the loopback adapter) explicitly. Other adapters, such as the lan adapter which has the local ip address, will not work, so you can’t enter the server machine’s own IP address as this won’t work. To fix this, edit local.ini in <couchdb home dir>/etc, and bind to 0.0.0.0 instead of 127.0.0.1, as  follows:-

 

[chttpd]
port = 5984
; Options for the MochiWeb HTTP server.
;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
; For more socket options, consult Erlang’s module ‘inet’ man page.
;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
bind_address = 0.0.0.0

Note the use of the [chttpd] section in the ini file, not [httpd]

Editing local.ini will override settings in default.ini, and these settings will also be preserved across upgrades, whereas edits to default.ini will not.

This is described in this post here. Note that you can also edit configuration parameters in fauxton in the configuration section. Clicking on the value of a parameter will expose an edit box to allow changes.

Once changes have been made you need to bounce the service.

2/ My other issue was to do with Kaspersky firewall settings. To ensure that you can access a machine from elsewhere on the lan, make sure your network type for the lan adapter is set to trusted and not public. Public will force you to enter explicit rules for every address/application/port etc. that you use, which is a complete pain on a lan. Trusted just allows all machines on the lan to interoperate freely. This is detailed here under how to change the network status.

No Comments »

March 17th, 2017
12:36 pm
Ionic Styling issues with Menu button on an ion-navbar

Posted under Ionic
Tags , ,

When adding a menu button to an ion-navbar, there is a styling issue whereby the menu toggle gets the wrong styling.
This depends on whether the menuToggle attribute is present.

If the attribute is present and the toggle is placed inside an ion-buttons group, it gets the wrong styling via a special case for menu toggles (flex rather than inline-block like the rest). This (incorrectly) puts it on a separate line above the others. Putting it separately above the ion-buttons group, with an explicit left on the button, fixes the problem.

If the menuToggle attribute is NOT present, then the button needs to go inside the ion-buttons group as otherwise for example for a left group the other left buttons go to its left. The rules for all this styling seem a bit quirky and not clearly defined.

This post discusses this issue.

No Comments »

March 13th, 2017
3:27 pm
Using Sass Maps e.g. to get colors for custom components

Posted under Ionic
Tags

This is described in the theming docs here.

The above mentions using the color function to apply a color to an element.

The $colors defines a sass map of the color variants for Ionic. You can use the sass map-get() function to get colors from the map, as posted here:-

ion-menu ion-item span {       
 color: map-get($colors, primary);
}

However the correct way uses the color function as per the above theming docs and the comment in the above post:-

ion-menu ion-item span {
  color: color($colors, primary, base);
}

No Comments »

March 11th, 2017
12:36 pm
Showing title text in the Ionic NavBar

Posted under Ionic
Tags ,

I wanted to try showing a centered title with a back button on the left, in the NavBar.

I tried various ways of doing this with css hacks, using floated <h1> and <div> combinations, but could not get it all to line up correctly.

The correct way is just to include an <ion-title> in the NavBar, as follows. This can be centred via the text-center attribute.

<ion-header>
  <ion-navbar>
    <ion-title text-wrap>
      {{place.localisedName}}
    </ion-title>
  </ion-navbar>
</ion-header>

 

Whilst text-center worked correctly in the title, note that text-wrap did not and was ignored.

As I did not take this pattern any further (I decided not to use titles in the toolbars but to put titles immediately underneath) I did not investigate this further.

This highlights an important issue. Too much CSS hacking breaks the encapsulation of the CSS model used by Ionic/Angular which is a bad thing – other things are in danger of breaking even if it does work initially.

it is obviously better to go with the tools/tags provided and investigate this way thoroughly first for a solution – in this case I did not and initially went down a blind alley which took time as well as being a suboptimal approach which did not work and broke encapsulation.

No Comments »

March 11th, 2017
12:08 pm
WebStorm/IntelliJ–Performance Problems/tweaking the IDE

Posted under IntelliJ & WebStorm
Tags ,

I hit issues with Webstorm, pregnant pauses and hanging for a few seconds.

The following points/posts offer help on tweaking:-

  1. Firstly it helps to see current VM usage. This is not on by default but if you tick Show Memory Indicator in Settings/Preference/Appearance it will then show at the bottom right of the status bar.
  2. VM can be tuned by editing the VM options as per this post here, but note that bigger is not necessarily better. Some of the hanging can be caused by excessive Garbage Collection when too much memory has to be GCd in one go. This post here goes into this. The tuning process can be informed by checking the memory indicator to see what you actually use.
  3. Excluding unwanted directories from indexing is important – see this post for details on doing this.

No Comments »