Archive for May, 2017

May 7th, 2017
8:05 pm
Angular 2 expressions containing observables cause null glitch on page display

Posted under Angular
Tags ,

The following expressions caused a brief glitch whereby null was displayed prior to the observable locationsCount emitting its value:-

<span text-capitalize>show all{{showAllActive ? ' ' + (locationsCount | async) : ''}}</span>

This makes sense as the observable with its async pipe, | async , which handles the observable, is part of a string expression. In other cases where the observable is e.g. a returned list, then you would just get an empty list with no entries. However in this case, Angular needed to evaluate and display the expression and so null was briefly displayed instead of the value of locationsCount.

The easiest way around this is to use a logical or which acts as a null coalescing operator in JavaScript/TypeScript, to replace the null with a suitable constant string:-

<span text-capitalize>show all{{showAllActive ? ' ' + (locationsCount | async)||' ' : ''}}</span>

The use of (locationsCount | async) || ‘ ‘  checks the first operand regardless of type, and if it is falsey, the second operand is taken. This is explained in this post here.

An alternative, a more complex solution which also worked in my case and may be interesting in other cases, is to concatenate the observable with another observable which emits a constant string using Observable.of(). The concat method emits all the values from each of the passed observables in turn, proceeding to the next one when an observable closes, as detailed in the RxJS api docs here. Therefore, the constant observable emits a value and completes/closes immediately which prevents the null glitch, followed by the locationsCount observable when it finally emits a value (which came from a mapped http api call):-

initial version without constant observable:-

this.locationsCount = this.locationsAutoCompleteService.getLocationsCount()
.map(count => {
let countString: string = this.formatLocationsCount(count);
return countString;
});

Alternative version with concatenated constant observable:-

this.locationsCount = Observable.of(' ').concat(
this.locationsAutoCompleteService.getLocationsCount()
.map(count => {
let countString: string = this.formatLocationsCount(count);
return countString;
})
);

No Comments »

May 1st, 2017
4:03 pm
Typescript duck typing error when casting a reference.

Posted under Angular & TypeScript
Tags , , ,

The following code fragment in a dao gave an error when casting the response json. The cause is not clear, but 2 code variations were tried:-

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())
      // *** This version originally failed citing that property total_rows did not exist
.map(response => {return <FeaturesSearchResult>(response.json())});
}

I then tried using the “as” operator for the cast instead of the <> syntax. This worked. Strangely, right at the end I managed to get an example with the”<>” syntax working too, but the reason and differences were not at all clear.
However, I noted that the latter is also the preferred syntax now, as unlike the “<>” syntax it is also compatible with JSX/.tsx syntax (JSX is an embeddable React xml-like syntax which transforms into JavaScript)
See here for details on JSX/.tsx, and on the React wikipedia entry. The working code fragment follows:-

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())// *** This version originally failed citing that property total_rows did not exist
      // *** This version worked by using the “as …” syntax for casting rather than <>
.map(response => {return response.json() as FeaturesSearchResult});
}

The full listing of the code for the working version (less imports for brevity) follows:-

features-dao.ts
@Injectable()
export class FeaturesDao {
constructor(private appConfig: AppConfig,
private http: Http) {}

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())
.map(response => {return response.json() as FeaturesSearchResult});
}

//API query definitions
queryFeatures(): string {
return `${this.appConfig.apiBase}_design/places/_view/featuresByOrdinal?include_docs=true`;
}

}
export interface FeaturesSearchResult extends CouchDBResult<FeaturesSearchResultRow> {}
export interface FeaturesSearchResultRow extends CouchDBResultRow<number[], any, FeatureDoc> {}
couchdb-result.ts
export interface CouchDBResult<R extends CouchDBResultRow<any,any,any>> {
total_rows: number;
offset: number;
rows: R[];
}

export interface CouchDBResultRow<K,V,D> {
id: string;
key: K;
value: V;
doc?: D;
}
feature-doc.ts
export interface FeatureDoc extends CouchDBDoc {
ordinal: number;
name: string;
description: string;
searchable: boolean;
rateable: boolean;
}

No Comments »