JSON file in "src/assets" folder not found

Viewed 2928

I'm trying to do dynamic app configuration following this article: https://devblogs.microsoft.com/premier-developer/angular-how-to-editable-config-files/. Browser returns 404 not found on the json file, although it can be accessed directly.

I put the configuration in assets folder:

src/assets/config/config.json

{
    "api": {
        "baseUrl": "http://xx.com"
    }
}

I have a service that loads the config.json:

src/app/services/app.config.service.ts

import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { IAppConfig } from '../models/appConfig'
import { environment } from 'environments/environment'

@Injectable()
export class AppConfigService {
    static settings: IAppConfig = {} as IAppConfig

    constructor(private http: HttpClient) {}

    load() {
        const jsonFile = `assets/config/config.json`
        return new Promise<void>((resolve, reject) => { 
            this.http
                .get(jsonFile)
                .toPromise()
                .then((response: any) => {
                    console.log(response, response.json())
                    AppConfigService.settings = <IAppConfig>{
                        ...environment,
                        ...response,
                    }
                    resolve()
                })
                .catch((response: any) => {
                    console.log(response)
                    reject(
                        `Could not load app configuration file '${jsonFile}': ${JSON.stringify(
                            response
                        )}`
                    )
                })
        })
    }
}

The service is utilized on APP_INITIALIZER:

src/app/app.module.ts

import { AppConfigService } from './services/app.config.service'

...
providers: [
        { provide: LOCALE_ID, useValue: 'da' },
        AppConfigService,
        {
            provide: APP_INITIALIZER,
            useFactory: initializeApp,
            deps: [AppConfigService],
            multi: true,
        },
    ],

...

I serve the app with ng serve. I can access the it directly with http://localhost:4200/assets/config/config.json. But the browser console complains:

ERROR Could not load app configuration file 'assets/config/config.json': {"body":{"error":"Collection 'config' not found"},"url":"assets/config/config.json","headers":{"normalizedNames":{},"lazyUpdate":null},"status":404,"statusText":"Not Found"}
defaultErrorLogger @ core.js:15724
setTimeout (async)
scheduleTask @ zone.js:2302
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:410
onScheduleTask @ zone.js:301
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:404
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:238
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMacroTask @ zone.js:261
scheduleMacroTaskWithCurrentZone @ zone.js:1245
(anonymous) @ zone.js:2317
proto.<computed> @ zone.js:1569
(anonymous) @ delay-response.js:16
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:196
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.error @ Subscriber.js:147
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._error @ Subscriber.js:80
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.error @ Subscriber.js:60
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._error @ Subscriber.js:80
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.error @ Subscriber.js:60
(anonymous) @ backend.service.js:278
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe @ Observable.js:43
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:29
push../node_modules/rxjs/_esm5/internal/operators/map.js.MapOperator.call @ map.js:18
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
(anonymous) @ delay-response.js:8
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe @ Observable.js:43
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:29
(anonymous) @ subscribeTo.js:21
subscribeToResult @ subscribeToResult.js:11
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._innerSub @ mergeMap.js:74
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._tryNext @ mergeMap.js:68
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._next @ mergeMap.js:51
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
push../node_modules/rxjs/_esm5/internal/operators/tap.js.TapSubscriber._next @ tap.js:51
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
push../node_modules/rxjs/_esm5/internal/operators/take.js.TakeSubscriber._next @ take.js:40
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next @ filter.js:38
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
push../node_modules/rxjs/_esm5/internal/BehaviorSubject.js.BehaviorSubject._subscribe @ BehaviorSubject.js:22
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe @ Observable.js:43
push../node_modules/rxjs/_esm5/internal/Subject.js.Subject._trySubscribe @ Subject.js:89
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:29
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._subscribe @ Observable.js:78
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:28
push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterOperator.call @ filter.js:15
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
push../node_modules/rxjs/_esm5/internal/operators/take.js.TakeOperator.call @ take.js:24
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
push../node_modules/rxjs/_esm5/internal/operators/tap.js.DoOperator.call @ tap.js:18
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapOperator.call @ mergeMap.js:29
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
(anonymous) @ subscribeTo.js:21
subscribeToResult @ subscribeToResult.js:11
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._innerSub @ mergeMap.js:74
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._tryNext @ mergeMap.js:68
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._next @ mergeMap.js:51
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:54
(anonymous) @ scalar.js:5
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe @ Observable.js:43
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:29
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapOperator.call @ mergeMap.js:29
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterOperator.call @ filter.js:15
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
push../node_modules/rxjs/_esm5/internal/operators/map.js.MapOperator.call @ map.js:18
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:24
(anonymous) @ Observable.js:98
ZoneAwarePromise @ zone.js:910
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.toPromise @ Observable.js:96
(anonymous) @ app.config.service.ts:17
ZoneAwarePromise @ zone.js:910
push../src/app/services/app.config.service.ts.AppConfigService.load @ app.config.service.ts:14
(anonymous) @ app.module.ts:27
push../node_modules/@angular/core/fesm5/core.js.ApplicationInitStatus.runInitializers @ core.js:16739
(anonymous) @ core.js:17774
_callAndReportToErrorHandler @ core.js:17867
(anonymous) @ core.js:17772
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
onInvoke @ core.js:17299
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:390
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:150
push../node_modules/@angular/core/fesm5/core.js.NgZone.run @ core.js:17213
push../node_modules/@angular/core/fesm5/core.js.PlatformRef.bootstrapModuleFactory @ core.js:17763
(anonymous) @ core.js:17803
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:150
(anonymous) @ zone.js:889
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:423
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:195
drainMicroTaskQueue @ zone.js:601
Promise.then (async)
scheduleMicroTask @ zone.js:584
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:413
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:238
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:258
scheduleResolveOrReject @ zone.js:879
ZoneAwarePromise.then @ zone.js:1012
push../node_modules/@angular/core/fesm5/core.js.PlatformRef.bootstrapModule @ core.js:17803
./src/main.ts @ main.ts:12
__webpack_require__ @ bootstrap:83
0 @ main.ts:13
__webpack_require__ @ bootstrap:83
checkDeferredModules @ bootstrap:45
webpackJsonpCallback @ bootstrap:32
(anonymous) @ main.js:1
Show 60 more frames

Hints or help appreciated.

-- UPDATE --

I found my own solution for the problem which I posted below.

2 Answers

Try this

 load() {
        const jsonFile = `assets/config/config.json`
        return new Promise<void>((resolve, reject) => { 
            this.http
                .get(jsonFile)
                .toPromise()
                .then((response: any) => {
                   var responseData =  response.json() | response;
                    AppConfigService.settings = <IAppConfig> responseData;
                    resolve();
                })
                .catch((response: any) => {
                    console.log(response)
                    reject(
                        `Could not load app configuration file '${jsonFile}': ${JSON.stringify(
                            response
                        )}`
                    )
                })
        })
    }

My own solution:

The problem was rather trivial. I am using the angular In Memory API plugin to mock all api requests until the real API is ready. That means that all http requests are caught by the service and instead returning the mock data that I defined locally. So the error message Collection 'config' not found makes sense, as I have no collection of mock data called 'config'.

So the solution is to use the setting passThruUnknownUrl to the In Memory API:

src/app/app.module.ts

imports: [
    ...
    HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {
            passThruUnknownUrl: true,
        }),
    ...

Now the collections not defined locally is passed thru externally.

Related