Reputation: 11825
I am attempting to create an NgModuleRef<AppModule>
in order to render some of my components into non-html formats.
I was able to create a PlatformRef
object, however when I am calling bootstrapModule(AppModule)
on this object I get an error:
"Error: No ResourceLoader implementation has been provided. Can't read the url "app.component.html""
Probably, I should be adding a DI registration of ResourceLoader
in a place different from extraProviders
, but I don't know where exactly...
import { ResourceLoader } from '@angular/compiler';
import { Compiler, CompilerFactory, Injectable, StaticProvider, Type } from '@angular/core';
import { platformDynamicServer } from '@angular/platform-server';
import { AppComponent } from './app/app.component';
import { AppModule } from './app/app.module';
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
async function generate<M>(moduleType: Type<M>) {
try {
const extraProviders: StaticProvider[] = [
{ provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: [] },
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] },
];
const platformRef = platformDynamicServer(extraProviders);
const moduleRef = await platformRef.bootstrapModule(moduleType); // <<< BREAKS HERE
const appComponent = moduleRef.injector.get(AppComponent);
console.info(appComponent.title.toString());
} catch (error) {
throw new Error(error.toString());
}
}
generate(AppModule)
.then(message => console.info({ message }))
.catch(error => console.error({ error }));
The ResourceLoaderImpl
class code is copied to the letter from @angular/platform-browser-dynamic
(src\resource_loader\resource_loader_impl.ts
):
@Injectable()
export class ResourceLoaderImpl extends ResourceLoader {
get(url: string): Promise<string> {
let resolve: (result: any) => void;
let reject: (error: any) => void;
const promise = new Promise<string>((res, rej) => {
resolve = res;
reject = rej;
});
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'text';
xhr.onload = function() {
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in ResourceLoader Level2 spec (supported
// by IE10)
const response = xhr.response || xhr.responseText;
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
let status = xhr.status === 1223 ? 204 : xhr.status;
// fix status code when it is 0 (0 status is undocumented).
// Occurs when accessing file resources or on Android 4.1 stock browser
// while retrieving files from application cache.
if (status === 0) {
status = response ? 200 : 0;
}
if (200 <= status && status <= 300) {
resolve(response);
} else {
reject(`Failed to load ${url}`);
}
};
xhr.onerror = function() { reject(`Failed to load ${url}`); };
xhr.send();
return promise;
}
}
Upvotes: 2
Views: 1507
Reputation: 214095
ResourceLoader
is a part of CompilerOptions therefore you should provide them.
It could be done eiher via platformRef.bootstrapModule as second CompilerOptions
parameter:
platformRef.bootstrapModule(moduleType, {
providers: [
{
provide: ResourceLoader,
useClass: ResourceLoaderImpl, deps: []
}
]
});
or you can try to provide COMPILER_OPTIONS
token as multi provider(here's an example from source code):
const extraProviders: StaticProvider[] = [
{
provide: COMPILER_OPTIONS,
useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]},
multi: true
},
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] },
];
Upvotes: 2