Angular: Weird if statement behaviour in app.module.ts - javascript

we recently experienced weird behaviour in our app.module.ts. We were using environment.ts and environemnt.prod.ts file to include/exclude specific library in imports array of the module(library should be excluded for production build). It looked something like this:
import { environment } from '../environments/environment'
//
#NgModule({
//
imports: [
CommonModule,
BrowserModule,
environment.production ? [] : libraryXXX,
HttpClientModule,
FormsModule
],
we found out after days of barren investigation that libraryXXX is included in build every time, even with --prod flag. We tested what's the problem - the value from environment.ts/prod.ts file was read correctly, but for whatever reason comparision always returned false. What I mean by always: replacing above fragment with:
environment.production == true ? [] : libraryXXX
or
environment.production == false ? [] : libraryXXX
did not change a thing - library WAS ALWAYS included. Like the if statement was broken(?). Extracting it to variable didn't change a thing. We ended up doing semi-hacky solution like:
environment.ts
import { libraryXXX } from 'whatever'
export const environment = {
production: false,
libraryXXX: libraryXXX
}
environment.prod.ts
export const environment = {
production: true,
libraryXXX: []
}
and simply referening to environment.libraryXXX in app.module.ts. But out of the curiosity - can anyone explain to me why simple if statement failed? Since it was app.module.ts I could not debug it, or even see console output, to know what's going on. Anyone has an idea?

This will work.
import { environment } from '../environments/environment'
const targetModules = [
CommonModule,
BrowserModule,
HttpClientModule,
FormsModule
];
if (environment.production) {
targetModules.push(libraryXXX);
}
//
#NgModule({
//
imports: targetModules,
//

What you can do:
You can import both libraries, the prod and the mock version.
Then you can decide (environment.production) which of both your will provide:
providers: [
{
provide: DbTrassenanfrageStatusService,
useClass: environment.production ? XyzMockService : XyzService
}
]
If you are talking about modules:
Give this a try:
const exports = [];
if (environment.production === true) {
exports.push(ModuleXyyyz);
console.info('FactoryModule: ModuleXyyyz;');
} else {
console.info('FactoryModule: nothing;');
}
#NgModule({
imports: [
ModuleXyyyz
],
exports
})
export class FactoryModule {
}
Then import FactoryModule in your app module.
To 'choose' the environment.prod.ts config, just build or start it via --prod flag:
ng serve --prod
ng build --prod
See the angular cli doc for more information (--prod , --configuration, ...)
If you have an environment.*.ts like this:
export const environment = {
production: true,
env: 'prod',
version: '{BUILD_VERSION}',
commitHash: '{COMMIT_HASH}',
showVersionIndicator: false,
routerTracing: false,
...then you can print out a field like version at startup (in your app component):
console.info('Version: ', environment.version);

Related

Enable CORS in Angular frontend

I have a microfrontend running on port 4200, and the other one on 4201. I tried splitting up the translation files and would like to load both of the applications translations, so I wrote a custom loader that is making an http request to port 4201:
export const scope = function(http: HttpClient) {
const loader = ['en', 'de'].reduce((acc: any, lang: string) => {
acc[lang] = () => firstValueFrom(http.get(`http://localhost:4201/assets/i18n/fields/${lang}.json`));
return acc;
}, {});
return {scope: 'fields', loader: loader}
};
#NgModule({
declarations: [RootComponent],
imports: [
CommonModule,
RootRoutingModule,
FormsModule,
SharedModule.forRemote(environment),
TranslocoModule
],
providers: [
{
provide: TRANSLOCO_SCOPE,
deps: [HttpClient],
useFactory: scope
}
]
})
export class RootModule { }
Both applications are javacript frontends, hence CORS blocks it. How can I ignore cores in the frontend?
https://levelup.gitconnected.com/fixing-cors-errors-with-angular-cli-proxy-e5e0ef143f85
This article helped me. It is a workaround to enable CORS in the frontend. But be warned this is just a workaround and in my case it was neccessary because I was loading translation files from one microfrontend to the app shell.

Is it possible to use Realm with Angular 2?

I wanted to try out Realm Mobile Platform with an Angular 2 app but it looks like I'm not able to use the Javascript version of Realm in Angular 2. If I were able to include Realm in my Angular 2 app, how would I go about setting that up?
So far I've run npm install --save realm successfully and I've got my app module that attempts to import that package and initialize everything.
import * as Realm from 'realm';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
Realm.Sync.User.login('http://local:9080', 'someUser', 'somePass', (e, user) => {
if(e) {
console.log(e);
return;
}
});
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I'm getting the following error and warnings:
WARNING in ./~/realm/lib/index.js
23:11-26 Critical dependency: the request of a dependency is an expression
WARNING in ./~/realm/lib/index.js
77:27-48 Critical dependency: the request of a dependency is an expression
WARNING in ./~/realm/lib/user-methods.js
24:11-26 Critical dependency: the request of a dependency is an expression
ERROR in ./~/realm/lib/browser/index.js
Module not found: Error: Can't resolve 'react-native' in '/vagrant/angular-realm-test/node_modules/realm/lib/browser'
# ./~/realm/lib/browser/index.js 21:0-45
# ./~/realm/lib/index.js
# ./src/app/app.module.ts
# ./src/main.ts
# multi webpack-dev-server/client?http://localhost:4200 ./src/main.ts
I'm confused because I've included other third party libraries, such as Firebase, in Angular 2 apps without an issue. I'm not entirely sure what's going on here so a detailed explanation would be greatly appreciated.
I've learned that the sync option isn't available without the professional or enterprise versions. I've since tried to do the following in the component:
import * as Realm from 'realm';
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
constructor() {
const CarSchema = {
name: 'Car',
properties: {
make: 'string',
model: 'string',
miles: {type: 'int', default: 0},
}
};
const PersonSchema = {
name: 'Person',
properties: {
name: 'string',
birthday: 'date',
cars: {type: 'list', objectType: 'Car'},
picture: {type: 'data', optional: true}, // optional property
}
};
// Initialize a Realm with Car and Person models
let realm = new Realm.Realm({schema: [CarSchema, PersonSchema]});
}
}
The above code gives me the same errors.
At the moment realm-js only works in a node environment.
The only way to get realm working with front-end frameworks is with Node.JS, which is luckily available in electron!
You can try to clone my repo of realm working with electron here if you're curious on such an implementation:
https://github.com/mbalex99/realm-electron
Note:
I've used it fine on MacOSX, Linux, and Windows
The sync feature only works on MacOSX. You can get it working on Linux if unless you have the professional or enterprise edition. You can download a trial of it here

Angular 2 Snackbar - Global Duration Config

I can set the duration of a snackbar message like so
let config = new MdSnackBarConfig();
config.duration = 5000;
this.snackBar.open(element.text, 'OK', config);
However I need to set the duration for multiple snackbars and would rather not have to pass in the config each time.
Can I somehow set a global duration config?
Thanks!
I know this post is from a few years ago but for future reference i'm going to answer this anyway. Hoping I help someone who comes across this post like I did.
You can now inject the MatSnackBar with default options for modules by using the providers in your #NgModule:
import { MatSnackBarModule, MAT_SNACK_BAR_DEFAULT_OPTIONS } from '#angular/material';
#NgModule({
declarations: [],
imports: [
MatSnackBarModule
],
exports: [
MatSnackBarModule
],
providers: [
{ provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 2500 } }
]
})
Source: Material Angular doc's
What we've done is included an external app.config.ts file at the module level and include it where we need it. This is an example of what's in the file.
export class Config {
static apiUrl = "api/";
static token = "";
static snackBarDurration = 5000;
......
}
Then, all you have to do is declare it in your module and then import it in the component or service in which you want to use it. Here is an example.
import { Injectable } from "#angular/core";
import { Config } from "../../app.config";
#Injectable()
export class SnackBarService {
.... // declare your snackbar here
constructor() { }
showSnackBar(elementText: string){
let config = new MdSnackBarConfig();
config.duration = Config.snackBarDurration; // Here is your change
this.snackBar.open(elementText, 'OK', config);
}
}
I know it's not solution for a global configuration, but to
make invocation more compact I declared config in params:
this.snackBar.open(elementText, 'OK', { duration: 3000 });

When/how does #NgModule get evaluated?

I've 3 applications which share a common framework so there's currently a bunch of duplicated code (basic ui layout, common code such as loggers etc, security/keycloak auth wrapper). I've extracted all the duplicate module code but also want to simplify the app module declaration itself. I've created an "Application" class containing a static method which takes a couple of params and builds an NgModule definition based on common modules, concatenating the passed in ones...
public static Define({modules, states, defaultState}) {
return {
imports: [
BrowserModule,
FormsModule,
HttpModule,
TreeModule,
NgbModule.forRoot(),
ToastModule.forRoot(<ToastOptions>{animate: 'flyRight', positionClass: 'toast-bottom-right'}),
UIRouterModule.forRoot(<RootModule>{ states: states.concat({name: 'app', url: '/app', component: ShellComponent}), useHash: true, otherwise: defaultState }),
CommonModule,
DialogsModule,
LayoutModule,
].concat(modules),
providers: [
{
provide: Http,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new AuthHttpService(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ UIView ]
};
}
Each application can then simply call this and just list its specific modules, states and an initial/default state like this...
#NgModule(
Application.Define({
modules: [
PatientIdentityModule,
RecordViewerModule,
ResourcesModule,
],
states: [
{name: 'app.resourceList', url: '/resourceList', component: ResourcesComponent },
{name: 'app.resourceEdit', url: '/resourceEdit/:itemAction/:itemUuid', component: ResourcesComponent },
{name: 'app.patientIdentity', url: '/patientIdentity', component : PatientIdentityComponent},
{name : 'app.recordViewer', url: '/recordViewer', component : RecordViewerComponent }
],
defaultState : { state: 'app.recordViewer', params: {} }
})
)
export class AppModule {}
This works great when all the code is together but when I try extracting the Application class and building a node module library, I then get
Error: No NgModule metadata found for 'AppModule'
Any ideas or am I going about this completely the wrong way!?
Angular 2.1.0, typescript 2.0.6, webpack 1.13.2

JSON files are not loading in ng2-translate with webpack

I'm trying to implement ng2-translate i18n.
dashboard.component.ts
import { Component } from '#angular/core';
import {TranslateService} from 'ng2-translate';
#Component({
    selector: 'dashboard-page',
    template:`<div>
      <h2>{{ 'HOME.TITLE' | translate }}</h2>
      <label>
        {{ 'HOME.SELECT' | translate }}
        <select #langSelect (change)="translate.use(langSelect.value)">
          <option *ngFor="let lang of translate.getLangs()" [value]="lang" [selected]="lang === translate.currentLang">{{ lang }}</option>
        </select>
      </label>
    </div>`
    
})
export class DashboardComponent {
constructor(private translate: TranslateService) {
translate.addLangs(["en", "fr"]);
translate.setDefaultLang('en');
let browserLang = translate.getBrowserLang();
translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
}
}
Path of this file is src/main/app/dashboard/dashboard.component.ts
Path of the 2 JSON files- en.json & fr.json is src/main/app/assets/i18n.
I have included TranslateModule in app.module.ts
But when I run the app, I'm getting en.json file not found-404 error. I'm using webpack and in webpack.common.js I have preloader for JSON like this
preLoaders:[
    {
        test: /\.json$/,
        exclude: /node_modules/,
        loader: 'json-loader'
    }
]
Still I'm getting JSON file not found error.
And from the examples I was following, I din't understand in which file the path assests\i18n.json is to be mentioned.
I have encountered this same issue. Webpack only include files which are 'require'-ed, so unless there is a require('./path/to/file.json') the file is not included. Furthermore, by doing so would mean that the file will also become hashed, and thus the file will not be recognized by the ng2-translate util.
I came around this issue by using the CopyWebpackPlugin (see https://github.com/kevlened/copy-webpack-plugin) By adding the following config to my webpack.config.js file
var CopyWebpackPlugin = require('copy-webpack-plugin');
...
plugins: [
new CopyWebpackPlugin([ { from: 'src/assets/i18n', to: 'assets/i18n' } ])
]
I also configured the Translate module as following because my resources were found under the /assets/i18n folder, rather than the default /i18n folder.
In app.translate.ts (note the export of the function, this is required for AoT)
export function createTranslateLoader(http: Http) {
return new TranslateStaticLoader(http, 'assets/i18n', '.json');
}
export const AppTranslateModule: ModuleWithProviders = TranslateModule.forRoot({
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [ Http ]
});
And in my app.module.ts as follows
#NgModule({
imports: [ AppTranslateModule, ... ],
...
})
export class AppModule {
}
Note: To the time of writing, the ng2-translate webpack example is broken. In fact there is an issue open for this https://github.com/ocombe/ng2-translate/issues/325
Change
new CopyWebpackPlugin([ { from: 'src/assets/i18n', to: 'assets/i18n' } ])
to
new CopyWebpackPlugin({ patterns: [ { from: './src/assets/i18n', to: 'assets/i18n' } ] }),

Categories