How to check for isBrowser in Angular 4 - javascript

In the past you could use isBrowser from Angular Universal to check if your page was rendering in a browser (and therefore you can use things like localStorage) or if it was doing server side pre-rendering.
But it seems that angular2-universal was dissolved into #angular/core, #angular/platform-server and #angular/platform-browser.
I've looked for similar functionality in the API documentation for Angular 4 and also tried to find it somewhere in the source code, but without luck.
Am I missing something or what is the Angular 4 way of checking if the rendering is running in a browser? Or should I simply just check if window is defined?

import { PLATFORM_ID, Inject } from '#angular/core';
import { isPlatformBrowser} from '#angular/common';
...
export class MyComponent {
...
testBrowser: boolean;
constructor(
#Inject(PLATFORM_ID) platformId: string) {
this.testBrowser = isPlatformBrowser(platformId);
if (this.testBrowser) {
//this is only executed on the browser
}
}
...

You can import isPlatformBrowser(<platform id>) as so:
import { isPlatformBrowser } from '#angular/common';
and that will allow you to check for whether it is rendering in browser or not.
As a note, there is also a isPlatformServer in #angular/common as well.

Do the Following steps:
import { isPlatformBrowser } from '#angular/common';
import { Inject, PLATFORM_ID } from '#angular/core;
constructor(#Inject(PLATFORM_ID) platformId: Object){
const isBrowser = isPlatformBrowser(this.platformId);
}
I hope it helps:

Related

NestJS: Enforce implementation of a service at *compile-time*

To keep it brief:
I have a service - let's call it CatsService...
import { Injectable } from '#nestjs/common';
#Injectable()
export class CatsService {
GetCat(id: number){
return id + 12345;
}
}
Later on, a module wishes to use this CatsService in order to inject it into a constructor. It also dynamically resolves this CatsService
import { Module } from '#nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import * as ProductionCatsService from "./production"
import * as DevelopmentCatsService from "./development"
const catsServiceProvider = {
provide: CatsService,
useValue: process.env.NODE_ENV === "development" ?
DevelopmentCatsService :
ProductionCatsService
}
#Module({
controllers: [CatsController],
providers: [catsServiceProvider],
exports:[CatsService]
})
export class CatsModule {}
Both of these service implementations are strictly empty. This means that the /cats/:id route returns:
{"statusCode":500,"message":"Internal server error"}
I have had the idea since beginning writing this to make everything implement a ICatsService interface.
After a lot of fiddling about, I have build a sort-of solution:
I have a "service_interface" file, which has an abstract class called ICatsInterface with one abstract method (GetCat)
I have made every one of these implementations implement this service_interface.
I have made the controller take ICatsService instead of CatsService
My problem is that now, the thing compiles, but I get the below error:
TypeError: this.catsService.GetCat is not a function
at CatsController.get (/home/a/learning-nest/src/cats/cats.controller.ts:12:33)
at /home/a/learning-nest/node_modules/#nestjs/core/router/router-execution-context.js:38:29
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at /home/a/learning-nest/node_modules/#nestjs/core/router/router-execution-context.js:46:28
at /home/a/learning-nest/node_modules/#nestjs/core/router/router-proxy.js:9:17
I simply don't know what to do anymore. Even though I have made it compile, I cannot make it actually be forced to implement the interface/abstract class that I want. I have made sure that both DevelopmentCatsService and ProductionCatsService have the implementation of GetCat. They are both marked with #Injectable. I don't truthfully know where to go from here.
Should I abandon my dream of having an compile-time error message for when someone does not implement the methods that I want implemented in my service?
I have solved my own issue. It was very simple.
import { Module } from '#nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import * as ProductionCatsService from "./production"
import * as DevelopmentCatsService from "./development"
const catsServiceProvider = {
provide: CatsService,
useValue: process.env.NODE_ENV === "development" ?
DevelopmentCatsService :
ProductionCatsService
}
#Module({
controllers: [CatsController],
providers: [catsServiceProvider],
exports:[CatsService]
})
export class CatsModule {}
In the above snippet, I am importing "* as ProductionCatsService". This caused my ProductionCatsService to actually be an object wrapping around the real service implementation that I wanted!
Sorry for the bother, all.

Call Angular 5 service method from Vanilla JS JSONP callback

I'm calling the Flickr API from a service method. Flickr expects a global function expression to be available called jsonFlickrFeed, which I've placed in my app root index.html file immediately after the root component and before the closing body tag. I need to pass the response from the Vanilla JS callback back to the originating service.
It reaches the callback fine, but I'm really struggling to find any information how to get back to the service that's specifically relevant to Angular 5.
Here's my service
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import 'rxjs/add/operator/map';
import * as validResponse from './mock/valid.json';
import {FlickrItem} from "./flickr-item";
import {FlickrResponse} from "./flickr-response";
#Injectable()
export class FlickrService {
private url: string = 'https://api.flickr.com/services/feeds/photos_public.gne?format=json';
private items: FlickrItem[];
private response: FlickrResponse;
constructor(private http: HttpClient) { }
getValid() {
// Return mock response
// For development / debug use only
// return validResponse;
this.http.jsonp(this.url, 'jsonFlickrFeed').subscribe();
};
}
As said here: Communication between angular 2 and pure javascript
You can define the callback inside the service using window:
#Injectable()
export class FlickrService {
...
constructor(...) {
window.jsonFlickrFeed = data => {
// do something
}
}
}

Where in angular/ngrx application to put logic for saving state to local storage

I have an angular application which uses #ngrx for state management. Within this application I have a piece of logic which subscribes to certain views of the state and saves them to local storage when they change. The code works as expected but I currently have it placed in my app component which seems like the wrong place for it. Can somebody suggest what the best place to put this logic would be?
import { Component } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/combineLatest';
import { Store } from '#ngrx/store';
import * as fromRoot from './state-management/reducers';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
constructor(
private store: Store<fromRoot.State>,
){
// Persist the user, with things to local storage
Observable.combineLatest(
store.select(fromRoot.getAuthUser),
store.select(fromRoot.getThings),
(user, things) => {
return {...user, things: things};
}
).subscribe(user => {
let oldUser = JSON.parse(localStorage.getItem('currentUser'));
localStorage.setItem('currentUser', JSON.stringify({...oldUser, user: user}));
});
}
}
I would suggest you simply to use an existing package: https://www.npmjs.com/package/ngrx-store-localstorage
His author definitely already found a suitable place.
If you want still to use your own implementation, then I would suggest to check his source code to understand triggers he uses for updates: https://github.com/btroncone/ngrx-store-localstorage/blob/master/src/index.ts#L289

Ionic3 / Ionic2+ settings.json file for environment variable configuration [duplicate]

I'm working on an ionic2 project and I need to create a new custom JSON config file. I found some tutorials to create one and access it through http.get but I think it's weird to call it through a get request. I want it in the root folder (where all the config JSONs are) and I open/read the file directly.
I don't know if it's possible, or even recommended ? This is why I'm posting here to have some opinions and solutions :)
Thanks
Personally I don't like the read the config.json file by using the http.get way to handle configuration information, and even though there must be another way to just include and read the json file in your code, since we're using Angular2 and Typescript, why not using classes, interfaces and doing it in a more fancy way?
What I'll show you next may seem more complicated than it should at first (although after reading it you will find it very straightforward and easy to understand), but when I started learning Angular2 I saw an example of how they handled config files in the Dependency Injection guide and I followed that in the apps I've worked on to handle config information (like API endpoints, default values, and so on).
According the docs:
Non-class dependencies
[...]
Applications often define configuration objects with lots of small
facts (like the title of the application or the address of a web API
endpoint) but these configuration objects aren't always instances of a
class.
One solution to choosing a provider token for non-class dependencies
is to define and use an OpaqueToken
So you would need to define a config object with the urls and so on, and then an OpaqueToken to be able to use it when injecting the object with your configuration.
I included all my configuration in the app-config.ts file
// Although the ApplicationConfig interface plays no role in dependency injection,
// it supports typing of the configuration object within the class.
export interface ApplicationConfig {
appName: string;
apiEndpoint: string;
}
// Configuration values for our app
export const MY_CONFIG: ApplicationConfig = {
appName: 'My new App',
apiEndpoint: 'http://www...'
};
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new OpaqueToken('config');
What OpaqueToken is may be confusing at first, but it just a string that will avoid naming conflicts when injecting this object. You can find an amazing post about this here.
Then, you just need to include it in the page you need it like this:
import { NavController } from 'ionic-angular/index';
import { Component, OpaqueToken, Injectable, Inject } from "#angular/core";
// Import the config-related things
import { MY_CONFIG_TOKEN, MY_CONFIG, ApplicationConfig } from 'app-config.ts';
#Component({
templateUrl:"home.html",
providers: [{ provide: MY_CONFIG_TOKEN, useValue: MY_CONFIG }]
})
export class HomePage {
private appName: string;
private endPoint: string;
constructor(#Inject(MY_CONFIG_TOKEN) private config: ApplicationConfig) {
this.appName = config.appName;
this.endPoint = config.apiEndpoint;
}
}
Please notice how to include it in the providers array
providers: [{ provide: MY_CONFIG_TOKEN, useValue: MY_CONFIG }]
And how to tell the injector how it should obtain the instance of the config object
#Inject(MY_CONFIG_TOKEN) config: ApplicationConfig
UPDATE
OpaqueToken has been deprecated since v4.0.0 because it does not support type information, use InjectionToken<?> instead.
So instead of these lines:
import { OpaqueToken } from '#angular/core';
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new OpaqueToken('config');
Now we should use
import { InjectionToken } from '#angular/core';
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new InjectionToken<ApplicationConfig>('config');
After reading and reading different solutions I ended up using this hacky implementation. Hopefully there will be a nice and native solution available soon:
import { NgModule } from '#angular/core';
import { environment as devVariables } from './environment.dev';
import { environment as testVariables } from './environment.test';
import { environment as prodVariables } from './environment.prod';
export function environmentFactory() {
const location = window.location.host;
switch (location) {
case 'www.example.org': {
return prodVariables;
}
case 'test.example.org': {
return testVariables;
}
default: {
return devVariables;
}
}
}
#NgModule({
providers: [
{
provide: 'configuration',
useFactory: environmentFactory
}
]
})
export class EnvironmentsModule {}
and then where ever needed, e.g.:
import { Injectable, Injector, Inject } from '#angular/core';
import { AuthenticationService } from '../authentication';
#Injectable()
export class APIService {
private http: Http;
private apiURL: string;
protected authentication: AuthenticationService;
constructor(
public injector: Injector,
#Inject('configuration') public configuration: any
) {
this.http = injector.get(Http);
this.authentication = injector.get(AuthenticationService);
this.apiURL = configuration.apiURL;
};
...

Angular2 dependency not injecting correct path

I am getting the following errors in my browser console, from trying to use localStorage with Angular2. It seems that the path it is generating isn't referring to the node_modules, but rather assuming that there is a localstorage.js in my site root (which there isn't). I am just referring to it normally (see my user.service below), so how do I get around this? All my other dependencies are working fine.
Error loading http://localhost:3000/localStorage.js as "localStorage" from http://localhost:3000/client/dev/user/services/user.service.js
import { Injectable } from 'angular2/core';
import { Headers } from 'angular2/http';
import { loalStorage } from 'localStorage';
#Injectable()
export class UserService {
private loggedIn = false;
constructor(private http: Http) {
this.loggedIn = !!localStorage.getItem('auth_token');
}
}
NB: I am fairly sure there isn't an actual problem with the localStorage installation, as if I run npm list localStorage, then it tells me I have localStorage#1.0.3 instaled.
If you want to use localStorage from an import, you need to configure it within SystemJS as described below:
System.config({
map: {
localStorage: 'node_modules/localStorage/lib/localStorage.js'
},
(...)
});
This way, you will be able to use the following import:
import loalStorage from 'localStorage';
See this question for more details since it's similar to the way to configure Lodash:
Lodash in angular2, declare var_:any not working

Categories

Resources