Excellent Angular 2/Material Design framework, Teradata Covalent, provides a RESTService abstract class that wraps REST api calls here:
https://teradata.github.io/covalent/#/components/http
Code to incorporate the extension is easy, as follows:
export class CustomRESTService extends RESTService<any> {
constructor(private _http: Http /* or HttpInterceptorService */) {
super(_http, {
baseUrl: 'www.api.com',
path: '/path/to/endpoint',
headers: new Headers(),
dynamicHeaders: () => new Headers(),
transform: (res: Response): any => res.json(),
});
}
}
The "update" method in the RESTService abstract class is shown here:
public update(id: string | number, obj: T, transform?: IRestTransform): Observable<any> {
let requestOptions: RequestOptionsArgs = this.buildRequestOptions();
let request: Observable<Response> = this.http.patch(this.buildUrl(id), obj, requestOptions);
return request.map((res: Response) => {
if (res.status === 200) {
if (transform) {
return transform(res);
}
return this.transform(res);
} else {
return res;
}
}).catch((error: Response) => {
return new Observable<any>((subscriber: Subscriber<any>) => {
try {
subscriber.error(this.transform(error));
} catch (err) {
subscriber.error(error);
}
});
});
}
My question is: if the update method of the abstract class throws an exception, how can that be captured in the CustomRESTService class? I.e., what Typescript code might one use to display an error in the UI?
Thank you.
First thing's first - Why would you want to catch it inside the rest client and not inside the app's logic?
Assuming you have some good reason for doing that (some other infrastructure code that you're running in the CustomRESTClient class), I would override the update function and implement error handling there.
A simple example without observables:
abstract class Base {
update(n:number):number {
return n;
}
test():bool;
}
class Child extends Base {
update(n:number):number {
return super.update(n)*2;
}
test():bool {
return true;
}
}
Related
I am trying to convert the date property to string object for all http request. Currently converting individual request body as below;
modifiedOn: modifiedOn?.map(x => moment(x).format('YYYY-MM-DD HH:mm:ss')),
But I would like this to apply for all http request in the interceptor. Can anyone help how to do this. my interceptor is as below:
#Injectable()
export class VerifyAuthorisationInterceptor implements HttpInterceptor {
constructor(#Inject(ENVIRONMENT) private environment: IEnvironment) {}
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
map((event: HttpEvent<unknown>) => {
return event;
}),
catchError((error: HttpErrorResponse) => {
if (error.status === 401 && error.statusText === 'Unauthorized') {
const returnUrl = window.location.href;
window.location.href = radarWebUrl;
}
return throwError(error);
})
);
}
}
Add this to your intercept method before return. It will check if your request contains body and any Date elements and then converts those:
if(request?.body) {
Object.keys(request.body).forEach((key: any) => {
if(request.body[key] instanceof Date) {
request.body[key] = moment(request.body[key]).format('YYYY-MM-DD HH:mm:ss');
}
});
}
artistSelected(item: any) {
console.log(item.artist);
this.songsServices.getSongsListArtist(item.artist).subscribe(
result => {
this.songsServices.artistList = result;
this.route.navigate(['./artistsongs']);
},
error => {
console.log('There is an error');
}
);
}
Service
getSongsListArtist(album: string) {
const completeUrl = `${environment.API_URL}?album=${album}`;
return this.httpClient.get(encodeURI(completeUrl));
}
artistList(artistsongs: string) {
return artistsongs;
}
I am facing an error in this. Please sole me how to pass the object from one ts file to another ts file by using service.
artistList(artistsongs: string) {
return artistsongs;
}
change to:
artistList(artistsongs: any) {
return artistsongs;
}
As i understand when you call getSongsListArtist it will return an array object or an object not a string only. You can use typeof() to check this.httpClient.get(encodeURI(completeUrl)) return what kind of type.
I need to assert that an element I created was added to a list, but it is added at the end of it and it is paginated.
I'm thinking of navigating through each page by calling another task this way:
export class CheckItem implements Task {
static afterCreated(): CheckItem {
return new CheckItem();
}
performAs(actor: PerformsTasks & UsesAbilities): PromiseLike<void> {
return TakeNotes.as(actor).read('item-name')
.then((itemName: string) => actor
.attemptsTo(
NavigateThroughItemList.untilFinds(itemName),
));
}
}
I need to implement NavigateThrougItemList.
Ok, I found the solution by using a recursive call. Maybe there is a better solution for this, but this one worked for me.
export class NavigateThroughItemList implements Task {
static untilFinds(itemName: string): NavigateThroughItemList {
return new NavigateThroughItemList(itemName);
}
constructor(private itemName: string) {
}
#step('{0} navigates through the list until #itemName is found')
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
See.if(Text.ofAll(ItemList.items), include(this.itemName)),
).catch(() => actor
.attemptsTo(
See.if(
WebElement.of(ItemList.nextButton),
isEnabled(),
),
)
.then(() => actor
.attemptsTo(
Click.on(ItemList.nextButton),
))
// Looping until it finds the item
.then(() => this.performAs(actor)));
}
}
as title says, when i wanna update TextDocumentContentProvider with different query params by calling update method provideTextDocumentContent is not called...
only way i managed to get it working was with same URI as in calling
vscode.commands.executeCommand('vscode.previewHtml', URI, 2, 'Storybook');
relevant part of code:
// calculates uri based on editor state - depends on actual caret position
// all uris will start with 'storybook://preview'
function getPreviewUri(editor: vscode.TextEditor): vscode.Uri;
// transforms uri, so web server will understand
// ex: 'storybook://preview?name=fred' -> 'http://localhost:12345/preview/fred?full=1'
function transformUri(uri: vscode.Uri): vscode.Uri;
class StorybookContentProvider implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string {
var httpUri = transformUri(uri);
return `<iframe src="${httpUri}" />`;
}
onDidChange = new vscode.EventEmitter<vscode.Uri>();
update(uri: vscode.Uri) {
this.onDidChange(uri);
}
}
export function activate(context: vscode.ExtensionContext)
{
vscode.workspace.onDidChangeTextDocument(
(e: vscode.TextDocumentChangeEvent) => {
if (e.document === vscode.window.activeTextEditor.document) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
vscode.window.onDidChangeTextEditorSelection(
(e: vscode.TextEditorSelectionChangeEvent) => {
if (e.textEditor === vscode.window.activeTextEditor) {
const previewUri = getPreviewUri(vscode.window.activeTextEditor);
provider.update(previewUri);
}
}
);
const provider = new StorybookContentProvider();
context.subscriptions.push(
vscode.commands.registerCommand('extension.showStorybook', () => {
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.parse('storybook://preview'), 2, 'Storybook')
}),
vscode.workspace.registerTextDocumentContentProvider('storybook', provider)
);
}
I'm building an observable data service based on the following article: https://coryrylan.com/blog/angular-2-observable-data-services
In the article he used an array as an example, here I will use the user object since I'm developing the user service.
Here's what I got:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Events, SqlStorage, Storage } from 'ionic-angular';
import { Subject } from 'rxjs/Subject';
export interface DataStore {
user: Object
}
#Injectable()
export class UserService {
private baseUrl: string;
private storage: Storage;
private _user$: Subject<Object>;
private dataStore: DataStore;
constructor(
private http: Http
) {
this.baseUrl = 'http://localhost:3000';
this.storage = new Storage(SqlStorage);
this._user$ = <Subject<Object>>new Subject();
this.dataStore = {
user: { name: '' }
};
}
set user$(user: Object) {
this.storage.set('user', JSON.stringify(user));
this.dataStore.user = user;
this._user$.next(this.dataStore.user);
}
get user$() {
return this._user$.asObservable();
}
loadUser() {
return this.storage.get('user').then(
((user: string): Object => {
this.dataStore.user = JSON.parse(user);
this._user$.next(this.dataStore.user);
return this.dataStore.user;
})
);
}
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', { access_token: accessToken })
.retry(2)
.map((res: Response): any => res.json());
}
logout(): void {
this.storage.remove('user');
}
}
To authenticate I call the login() function and set the user data if everything ok.
this.userService.login(this.data.accessToken)
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
I feel it is better set the user data inside the service. I could do the following:
login(accessToken: string) {
return this.http
.post('http://localhost:3000/login', {
access_token: accessToken
})
.retry(2)
.map((res: Response): any => res.json())
.subscribe(
(user: Object) => {
this.userService.user$ = user;
this.nav.setRoot(EventListComponent);
},
(error: Object) => console.log(error)
);
}
But I won't be able to subscribe to the login() function in the component since it's already subscribed. How could I redirect the user if everything ok or show an alert if anything goes wrong in the component but setting the user inside the service?
In the main component I load the user data and set the rootPage:
this.userService.loadUser().then(
(user: Object) => this.rootPage = EventListComponent,
(error: Object) => this.rootPage = LoginComponent
);
I thought that calling the loadUser() function at this time I would not have to call it again, but I have to call it in all components that I need the user data:
this.user = this.userService.user$;
this.userService.loadUser();
I don't think the service is the way it should, what could I improve? Is there any better way to achieve what I want? Any example or idea?