Angular2 Ansychronous bootstrapping with external json configuration file - json

I have upgraded to angular2 RC6 and want to load an external JSON config file before bootstrapping my AppModule. I had this working before RC5 but am now having trouble finding an an equivalent way of injecting this data.
/** Create dummy XSRF Strategy for Http. */
const XRSF_MOCK = provide(XSRFStrategy, { provide: XSRFStrategy, useValue: new FakeXSRFStrategyService() });
/** Create new DI. */
var injector = ReflectiveInjector.resolveAndCreate([ConfigService, HTTP_PROVIDERS, XRSF_MOCK]);
/** Get Http via DI. */
var http = injector.get(Http);
/** Http load config file before bootstrapping app. */
http.get('./config.json').map(res => res.json())
.subscribe(data => {
/** Load JSON response into ConfigService. */
let jsonConfig: ConfigService = new ConfigService();
jsonConfig.fromJson(data);
/** Bootstrap AppCOmponent. */
bootstrap(AppComponent, [..., provide(ConfigService, { useValue: jsonConfig })
])
.catch(err => console.error(err));
});
This worked just fine but struggling to change to work with RC6.
I'm experimenting the following approach but struggling to modify my predefined AppModule with loaded data:
const platform = platformBrowserDynamic();
if (XMLHttpRequest) { // Mozilla, Safari, ...
request = new XMLHttpRequest();
} else if (ActiveXObject) { // IE
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
console.log(e);
}
}
}
request.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
var json = JSON.parse(this.responseText);
let jsonConfig: ConfigService = new ConfigService();
jsonConfig.fromJson(json);
/**** How do I pass jsConfig object into my AppModule here?? ****/
platform.bootstrapModule(AppModule);
}
};
// Open, send.
request.open('GET', './config.json', true);
request.send(null);

I had the same problem. Looks like you came across my Gist :-)
As far as the RC 6 update, you should check out the HttpModule source. It shows all the providers that were originally in the now removed HTTP_PROVIDERS. I just checked that out and came up with the following
function getHttp(): Http {
let providers = [
{
provide: Http, useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new Http(backend, options);
},
deps: [XHRBackend, RequestOptions]
},
BrowserXhr,
{ provide: RequestOptions, useClass: BaseRequestOptions },
{ provide: ResponseOptions, useClass: BaseResponseOptions },
XHRBackend,
{ provide: XSRFStrategy, useValue: new NoopCookieXSRFStrategy() },
];
return ReflectiveInjector.resolveAndCreate(providers).get(Http);
}
As far as the
/**** How do I pass jsConfig object into my AppModule here?? ****/
platform.bootstrapModule(AppModule);
It's not the prettiest (it's really not that bad), but I found something I didn't even know was possible, from this post. Looks like you can declare the module inside the function.
function getAppModule(conf) {
#NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ],
providers: [
{ provide: Configuration, useValue: conf }
]
})
class AppModule {
}
return AppModule;
}
Below is what I just used to test right now
import { ReflectiveInjector, Injectable, OpaqueToken, Injector } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
import {
Http, CookieXSRFStrategy, XSRFStrategy, RequestOptions, BaseRequestOptions,
ResponseOptions, BaseResponseOptions, XHRBackend, BrowserXhr, Response
} from '#angular/http';
import { AppComponent } from './app.component';
import { Configuration } from './configuration';
class NoopCookieXSRFStrategy extends CookieXSRFStrategy {
configureRequest(request) {
// noop
}
}
function getHttp(): Http {
let providers = [
{
provide: Http, useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new Http(backend, options);
},
deps: [XHRBackend, RequestOptions]
},
BrowserXhr,
{ provide: RequestOptions, useClass: BaseRequestOptions },
{ provide: ResponseOptions, useClass: BaseResponseOptions },
XHRBackend,
{ provide: XSRFStrategy, useValue: new NoopCookieXSRFStrategy() },
];
return ReflectiveInjector.resolveAndCreate(providers).get(Http);
}
function getAppModule(conf) {
#NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ],
providers: [
{ provide: Configuration, useValue: conf }
]
})
class AppModule {
}
return AppModule;
}
getHttp().get('/app/config.json').toPromise()
.then((res: Response) => {
let conf = res.json();
platformBrowserDynamic().bootstrapModule(getAppModule(conf));
})
.catch(error => { console.error(error) });

Related

Unit Testing Angular component with service : Cannot read property 'diagonisticData' of undefi

I am new to angular testing. I have a component, nested json and a service. The app works fine but during testing values are not being populated into the component. Please help.I have attached the service, json object,component and spec file.
I am not sure if I am following the right approach in spec file.
App component -Hub-Details-component.ts
export class HubDetailsComponent implements OnInit {
ngOnInit(): void {}
public jsonData:any = []
public diagnosticsData:any = [];
public dummy:any = [];
public hubData:any;
constructor(private dataService: DataService) {}
handleData()
{
this.dataService.getData()
.subscribe(response =>{
if(response!=null)
{
this.jsonData=response;
console.log(this.jsonData);
this.dummy=this.jsonData.result;
console.log(this.dummy);
this.diagnosticsData=this.dummy.diagnosticData;
const DataArray = [];
for(const element in this.diagnosticsData)
{
DataArray.push({
id:element,
name:this.diagnosticsData[element]
});
}
console.log(DataArray);
this.hubData=DataArray;
}
});
}
}
DataService
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'})
export class DataService {
public url = '/assets/Data/Data.json'
constructor(private http: HttpClient ) { }
getData = () => {
const url = 'assets/Data/Data.json';
return this.http.get(url);
}}
json file
{
"result"
{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}
spec.ts
it('should get data from dataservice',fakeAsync(()=>{
const fixture =
TestBed.createComponent(HubDetailsComponent);
const component =
fixture.debugElement.componentInstance;
const service =
fixture.debugElement.injector.get(DataService);
let spy_getPosts =
spyOn(service,'getData').and.callFake(() => {
return of([{"result"{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}]).pipe(delay(2000));});
fixture.detectChanges();
component.handleData();
tick(2000);
expect(component.jsonData).toEqual([{
{"result"{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}
}]);
}));
Thanks in advance.
Try this:
// In your spec file, mock the service;
#Injectable()
class MockDataService extends DataService {
getData() {
const mockData = {
result: {
diagnosticData: [
{ mock1: 'value1' },
{ mock2: 'value2' }
]
}
}
return of(mockData);
}
}
describe('Your Component Name you are testing', () => {
let dataService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [...yourImports],
schemas: [NO_ERRORS_SCHEMA],
providers: [
{
provide: DataService,
useClass: MockDataService
}
]
}).compileComponents();
dataService = TestBed.get(DataService);
}));
// Now your test case:
it('should call #handleData() method', () => {
spyOn(component, 'handleData').and.callThrough();
component.handleData();
expect(component.handleData).toHaveBeenCalled();
});
// Thats it. You do not need to do anything else;
})

#ngrx/store-devtools - effects called twice

All actions I dispatch in effects are called twice. I already figured out, that this is caused by StoreDevtoolsModule. When I kick StoreDevtoolsModuleout of the app.module.ts my action will be called only once => everything is fine.
Here's my setup:
Angular 6.0.1
"#ngrx/effects": "6.0.0-beta.1",
"#ngrx/entity": "5.2.0",
"#ngrx/router-store": "6.0.0-beta.1",
"#ngrx/store": "6.0.0-beta.1",
"#ngrx/store-devtools": "6.0.0-beta.1",
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { StoreModule, Store, combineReducers, ActionReducerMap, MetaReducer }
from '#ngrx/store';
import { StoreDevtoolsModule } from '#ngrx/store-devtools';
import { EffectsModule } from '#ngrx/effects';
import { EquipmentModule } from './modules/equipment/equipment.module';
const reducers: ActionReducerMap<IAppState> = {
equipments: EquipmentReducer
};
#NgModule({
declarations: [
AppComponent
],
imports : [
BrowserModule,
StoreModule.forRoot(reducers),
StoreDevtoolsModule.instrument({
name: 'EQUIPMENT',
maxAge: 10,
logOnly: true,
}),
EquipmentModule,
EffectsModule.forRoot([]),
]
})
export class AppModule
{}
equipment.module.ts
import { EffectsModule } from '#ngrx/effects';
import { EquipmentEffects } from './equipment.effect';
import { EquipmentMockService } from './equipment.mock';
#NgModule({
imports: [
EffectsModule.forFeature([EquipmentEffects])
],
providers: [
{
provide: 'IEquipmentService',
useClass: EquipmentMockService
},
]
})
export class EquipmentModule {
}
equipment.effect.ts
import { CustomAction } from './../../custom.action';
import { Injectable, Inject } from '#angular/core';
import { Http } from '#angular/http';
import { Actions, Effect, ofType } from '#ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { EquipmentActions } from './equipment.action';
import { HttpClient } from '#angular/common/http';
import { Action, State } from '#ngrx/store';
import { of } from 'rxjs/internal/observable/of';
import { EquipmentService } from './equipment.service';
import { IAppState } from '../../app.state';
import { ofTypeWithPayload } from '../../customActionHelper';
#Injectable()
export class EquipmentEffects {
constructor(
private http: HttpClient,
private actions$: Actions,
private readonly state: State<IAppState>,
#Inject('IEquipmentService')
private equipmentService: EquipmentService
) { }
#Effect()
getAllEquipments$: Observable<Action> = this.actions$.pipe(
ofType(EquipmentActions.LOAD_ALL_EQUIPMENTS),
mergeMap(action => {
console.log('here'); <-- This console.log gets called twice!
return this.equipmentService.getAllEquipments().pipe(
map(equipments => ({ type: 'LOAD_ALL_EQUIPMENTS_SUCCESS', payload: equipments })),
catchError(() => of({ type: 'LOAD_ALL_EQUIPMENTS_FAIL' }))
);
}
)
);
Does anyone have an idea?
Thanks

Ionic app getting data from url json

I don't know how to do this. I tried few examples from internet. What is the best solution?
import { Component } from '#angular/core';
#Component({
selector: 'page-hello-ionic',
templateUrl: 'hello-ionic.html'
})
export class HelloIonicPage {
constructor() {
}
}
to do this
Modify src/app/app.module.ts and import
import { HttpModule } from '#angular/http';
And Add to the Imports
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
HttpModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
After that you can use the HTTP Module from angular.
In your Page.ts
import the module and use it like this
import { Http } from '#angular/http';
constructor(public navCtrl: NavController, public http: Http) {
let url = "https://www.reddit.com/r/gifs/new/.json?limit=10";
this.http.get(url).map(res => res.json()).subscribe(data => {
console.log(data);
});
}
Additionally you can convert to returned JSON string to JSON Object
var jsonobject = JSON.parse(data)
And Alternatively you can use the IONIC NATIVE HTTP PLUGIN
Cheers :D
To do this just use the below code, no need to import any module
fetch('https://url.com').then(res => res.json())
.then(json => {
console.log(json)
});

Can't access subscribe method in unit-test

I'm experiencing a very strange issue on a simple unit-test of a simple service get method. I'm using Angular 4 and cli so karma/jasmine for testing.
My service method:
getAccounts(): Observable<ApiData> {
return this.authHttp.get('../../assets/mock_data/productsOverview.json')
.map(
data => {
return JSON.parse(data['_body']);
},
err => {
return err;
}
);
}
And here is my test case:
import { TestBed, inject, async } from '#angular/core/testing';
import { Http, BaseRequestOptions, RequestOptions, HttpModule, ConnectionBackend } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { AuthHttp, AuthConfig } from 'angular2-jwt';
import { encodeTestToken } from 'angular2-jwt/angular2-jwt-test-helpers';
import { AppModule } from '../app.module';
import { PersonalAccountsService, ApiData } from './personal-accounts.service';
import { Observable } from 'rxjs/Observable';
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
headerName: 'Authorization',
headerPrefix: 'Bearer',
tokenName: 'token',
tokenGetter: (() => localStorage.getItem('token')),
globalHeaders: [
{ 'Content-Type': 'application/json' },
{ 'Ocp-Apim-Subscription-Key': localStorage.getItem('key') }],
}), http, options);
}
const MyMockedService = {
method: () => { }
}
describe('PersonalAccountsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PersonalAccountsService,
BaseRequestOptions,
MockBackend,
{
provide: Http,
useFactory: (mockBackend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(mockBackend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions],
},
{
provide: AuthHttp,
useFactory: (http) => {
return new AuthHttp(new AuthConfig({
tokenName: 'token',
tokenGetter: (() => encodeTestToken(this)),
globalHeaders: [{ 'Content-Type': 'application/json' }]
}), http);
},
deps: [Http],
},
],
imports: [
HttpModule
],
});
});
it('should be created', inject([PersonalAccountsService],
(service: PersonalAccountsService) => {
expect(service).toBeTruthy();
}));
it('identification', async(
inject(
[PersonalAccountsService],
(service: PersonalAccountsService) => {
console.log('ENTERS HERE');
service.getAccounts().subscribe(
data => {
console.log('NOT ENTERING HERE');
},
err => {
console.log('NOT ENTERING HERE');
},
() => {
console.log('NOT ENTERING HERE')
}
);
}
)
));
});
Why this is happening? I'm trying to figure it our for a long time and I cannot even take an error to dive deeper.
The test likely performs requests but they weren't mocked with MockBackend.
The fact that code relies on third-party services (AuthHttp) makes it integration test rather than unit test.
To keep it as isolated as possible, every unit but tested unit should be mocked or stubbed. The service can be tested in isolation (in this case DI is not tested) or with TestBed:
beforeEach(() => {
const authHttpMock = jasmine.createSpyObj('authHttp', ['get']);
TestBed.configureTestingModule({
providers: [
{ provide: AuthHttp, useValue: authHttpMock }
...
});
it('...', async(inject([PersonalAccountsService, AuthHttp], async (service, authHttpMock) => {
const responseMock = Observable.of({ _body: ... });
authHttpMock.get.and.returnValue(responseMock);
const accounts$ = service.getAccounts();
expect(authHttpMock.get).toHaveBeenCalledWith(...);
const accounts = await accounts$.toPromise();
expect(accounts)...
...

angular2-google-maps read apikey from web.config

We intergrated angular2-google-maps in our project. The project is built angularjs 2 release version and using webapi.
app.module.ts
import { LazyMapsAPILoaderConfig, AgmCoreModule } from 'angular2-google-maps/core';
import { appConfig,IAppConfig,GoogleMapApiKey } from "./app.config";
#NgModule({
imports: [
AgmCoreModule.forRoot({
apiKey: ""
})
],
declarations: [
AppComponent
],
providers: [
{ provide: LazyMapsAPILoaderConfig, useValue: GoogleMapApiKey }
],
bootstrap: [AppComponent]
})
export class AppModule {
}
app.config.ts
import { OpaqueToken } from "#angular/core";
export let appConfig = new OpaqueToken('app.config');
export interface IAppConfig {
apiKey: string
}
export const GoogleMapApiKey : IAppConfig = {
apiKey: "Key"
};
I hardcoded the google map api key in the typescript file. Is it possible to read it from .net web.config file.
Also is this correct way of displaying the map or can i make it part of component.
Any suggestions.