Using ngrx-data how to configure the app to fetch data of specific entities from specific urls - angular-ngrx-data

If we follow ngrx-data example and look at the Entity DataService, we can fetch the Hero data that we have in-memory (hard-coded) without any configuration. The default will work the same as if we configured:
const defaultDataServiceConfig: DefaultDataServiceConfig = {
root: 'api', // or a running server url, e.g: 'http://localhost:4000/api'
timeout: 3000, // request timeout
}
and in e.g: EntityStoreModule
#NgModule({
providers: [{ provide: DefaultDataServiceConfig, useValue: defaultDataServiceConfig }]
})
Question:
How will we configure our app to fetch data for entity "Heros" from the default source:
root: 'api'
and data for entity "Villans" from a URL:
root: 'http://localhost:4000/villans'
and data for other entities from their (other/various) respective URLs ...?

After reviewing the docs specifically:
Custom EntityDataService and
Replace the HttpUrlGenerator
I came up with this solution. Anyone feel free to comment.
Define/review your data types - entity metadata - entity names;
Create mapping to plurals for non-default plural entity names (default is: name + 's');
For entities with the non-default root URL create a mapping of entity names to specific URL;
File: ../entity-metadata.ts
// Step 1:
const entityMetadata: EntityMetadataMap = {
Hero: {},
Villan: {},
Creature: {},
DataA01: {}
// etc.
}
// Step 2:
const pluralNames = {
Hero: 'heroes',
DataA01: 'data-a01'
}
export const entityConfig = {
entityMetadata,
pluralNames
};
// Step 3:
export const rootUrls = {
// Hero: - not needed here, data comes from default root
Villan: 'http://localhost:4001',
Creature: 'http://localhost:4001',
DataA01: 'http://remoteserver.net:80/publicdata',
}
Replace the HttpUrlGenerator (doc) with your own URL generator (DynamicHttpUrlGenerator)
File: ../http-dyn-url-generator.ts
import { Injectable } from '#angular/core';
import {
DefaultHttpUrlGenerator,
HttpResourceUrls,
normalizeRoot,
Pluralizer,
DefaultPluralizer,
} from '#ngrx/data';
import { rootUrls } from '../data/ngrx-data/db01-entity-metadata';
#Injectable()
export class DynamicHttpUrlGenerator extends DefaultHttpUrlGenerator {
constructor(private aPluralizer: Pluralizer = new DefaultPluralizer(undefined)) {
super(aPluralizer);
}
protected getResourceUrls(entityName: string, root: string): HttpResourceUrls {
let resourceUrls = this.knownHttpResourceUrls[entityName];
if ( ! resourceUrls) {
// rootUrls contains
// mapping of individual ngrx data entities
// to the root URLs of their respective data sources.
// It contains only entities which do not have
// the default root URL.
if (rootUrls.hasOwnProperty(entityName)) {
root = rootUrls[entityName];
}
const nRoot = normalizeRoot(root);
const url = `${nRoot}/${this.aPluralizer.pluralize(entityName)}/`.toLowerCase();
// remove after testing
console.log('-- entityName: ' + entityName + ', URL: ' + url)
resourceUrls = {
entityResourceUrl: url,
collectionResourceUrl: url
};
this.registerHttpResourceUrls({ [entityName]: resourceUrls });
}
return resourceUrls;
}
}
For each of your data entity create a custom EntityDataService
HeroDataService
VillanDataService
CreatureDataService
DataA01DataService
etc.
(doc and code is here) - the code example is under
// store/entity/hero-data-service.ts
Register your DynamicHttpUrlGenerator and your custom EntityDataServices in your app's module, in my case:
File: ../ngrx-data-store.module.ts
(in a simple app, directly in file: app.module.ts)
#NgModule({
imports: [ ... ],
providers: [ { provide: HttpUrlGenerator, useClass: DynamicHttpUrlGenerator },
HeroDataService,
VillanDataService,
CreatureDataService,
DataA01DataService
]
})
Use your custom EntityDataServices in your components for each given entity the same way as all standard or default EntityDataServices to fetch data. The data will be pulled from the respective URLs you set in the const: rootUrls.
Don't forget to get your URLs' data server(s) configured and started.
A few important considerations:
on your server you may need to enable CORS handling. E.g: on nestjs use:
app.enableCors();
if your client app uses: Angular in-memory-web-api you need to enable access to remote server as follows:
File: ../in-mem-data.module.ts (or as you named it)
import { NgModule } from '#angular/core';
import { HttpClientModule } from '#angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemDataService } from '../../services/data/in-mem-data/in-mem-data.service';
#NgModule({
imports: [
HttpClientModule,
HttpClientInMemoryWebApiModule.forRoot(InMemDataService, {
passThruUnknownUrl: true // <--- IMPORTANT for remote data access
}),
]
})
export class InMemDataModule {}

Related

reuse *.resx(AngularJS) translation files in hybrid AngularJS/Angular 5 application

Hello I have AngularJS application which is using for internalization $translateProvider and WebResources.resx files :
angular.module('app')
.config(['$translateProvider', 'sysSettings', 'ngDialogProvider',
function($translateProvider, sysSettings, ngDialogProvider) {
ngDialogProvider.setDefaults({
closeByDocument: false
});
sysSettings.device = window['device'];
if (window['device'] && ktmvPreference && ktmvPreference.preference) {
sysSettings.webServiceURL = ktmvPreference.preference.webServiceURL;
sysSettings.reportServiceURL = ktmvPreference.preference.reportServiceURL;
sysSettings.onlineHelpURL = ktmvPreference.preference.onlineHelpURL;
}
$translateProvider.useSanitizeValueStrategy(null);
$translateProvider.useLocalStorage();
var savedLanguage = localStorage.language;
if (savedLanguage)
$translateProvider.fallbackLanguage([savedLanguage]);
else
$translateProvider.fallbackLanguage(['en', 'fr', 'es']);
var url = sysSettings.webServiceURL + 'api/GlobalResources';
$translateProvider.useUrlLoader(url);
$translateProvider.useMissingTranslationHandlerLog();
$translateProvider.useMissingTranslationHandler('ktmvTranslationFail');
}
]);
Now I am doing AngularJS/Angular5 Hybrid application. As mentioned in documentation Angular5 is using "i18n" for internationalization. "i18n" is using "*.xlf" files to keep translations.
So only way during AngularJS/Angular5 application keep both WebResources.resx and messages.xlf files with the same context ?
Is there any way to reuse WebResources.resx translation from AngularJS in AngularJS/Angular application?
Maybe it will be usefull for someone...
To be able to reuse */resx files in my AngularJS/Angular internalization I started to use ngx-translate library.
This is how I implemented it :
I created custom-translate-loader.ts
import { Injectable } from '#angular/core';
import { TranslateLoader } from '#ngx-translate/core';
import { Observable } from 'rxjs/Observable';
import {HttpClient} from "#angular/common/http";
#Injectable()
export class CustomTranslateLoader implements TranslateLoader {
constructor(private http: HttpClient) {}
getTranslation(lang: string): Observable<any>{
var apiAddress = "http://localhost:26264/api/GlobalResources/?lang=" + lang;
return Observable.create(observer => {
this.http.get(apiAddress, ).subscribe(res => {
observer.next(res);
observer.complete();
},
error => {
console.log("cannot retrieve Global Resources");
}
);
});
}
}
then in my app.module.ts I imported
import {TranslateModule, TranslateLoader} from '#ngx-translate/core';
import {HttpClient, HttpClientModule} from '#angular/common/http';
import {CustomTranslateLoader} from "./common/loader/custom-translate-loader";
and in my NgModule i injected TranslateModule :
#NgModule({
imports: [
BrowserModule,
UpgradeModule,
FormsModule,
routingModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: CustomTranslateLoader,
deps: [HttpClient]
}
})
],
declarations: [
AppComponent,
SignInComponent,
ActivationComponent
],
providers: [authServiceProvider,
commonSvcProvider,
BackgroundImageFactoryProvider,
LanguageSvcProvider
// { provide: UrlHandlingStrategy, useClass: CustomHandlingStrategy }
],
bootstrap: [AppComponent]
})
After this in my components (sign-in.components.ts for example) i am able to set up the language:
import {TranslateService} from "#ngx-translate/core";
export class SignInComponent implements OnInit{
constructor(
private translate: TranslateService) {
translate.setDefaultLang('en');
}

Create unique instance of provider ionic 3

I am creating an Ionic app. I have 3 providers - database provider, portfolio provider and user provider. All 3 are Injectable. I have created it this way because several other pages need to use their function calls (i.e. they should not share the same data, they all should create new instances)
Both the portfolio and user provider import the database provider, as the need to make the same database calls to retrieve data.
I have 1 page - ViewPortfolio. The ViewPortfolio page imports the user provider (to know who the user is) and portfolio provider (to get the users portfolio data). For some reason, these 2 providers seem to be sharing the same instance for database provider. This is how I use them:
PORTFOLIO PROVIDER
import { DatabaseProvider } from '../providers/database-provider';
#Injectable()
#Component({
providers: [DatabaseProvider]
})
export class PortfolioProvider {
public portfolio_list: any = new BehaviorSubject<Array<string>>([]);
constructor(private dbProvider: DatabaseProvider) {
this.dbProvider.enableDataListener(this.protfolio_path); // set path
this.dbProvider.db_listener.subscribe(value => { // subscribe to data in the path
// do stuff
});
}
}
The user portfolio is the same, the only difference is the path its listening to is different.
However, when I change data in the portfolio path, the subscribe call is also triggered in the user path (and vice versa). Thus, even though I added DatabaseProvider in the components providers, its not creating unique instances. Why is this?
I figured it might be because I am importing them both on the same page but I am not convinced that's why it is not working. How do I make the 2 providers use unique instances on databaseprovider, while calling them both on the same page?
This is what my app.moudle.ts file looks like (please note that DatabaseProvider is not included in my app.module.ts file)
// ... more imports
import { PortfolioProvider } from '../providers/portfolio-provider';
import { UserProvider } from '../providers/user-provider';
#NgModule({
declarations: [
MyApp,
// ... more
],
imports: [
// ... more
IonicModule.forRoot(MyApp, {
backButtonText: '',
tabsPlacement: 'bottom'
}),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
// ... more
],
providers: [
// ... more
PortfolioProvider,
UserProvider
]
})
export class AppModule {}
Thanks,
Did you remove the provider from app.module.ts (root AppModule)?
From the Angular Documentation:
Scenario: service isolation
While you could provide VillainsService in the root AppModule (that's where you'll find the HeroesService), that would make the VillainsService available everywhere in the application, including the Hero workflows.
If you generated the provider using ionic-cli, it'll automatically add it to the root AppModule.

Importing json2csv module in Angular 4

I am trying to use this library in my application to convert JSON data to CSV file format. I installed the lib into my project as it mentions https://www.npmjs.com/package/json2csv
npm install json2csv --save.
I also see the module in my node_module folder. Then in my component class i am calling it like so
import { json2csv } from 'json2csv';
But then I get this error
[ts] Module '"c:/dev/angularworkspace/tntzweb/node_modules/json2csv/index"' has no exported member 'json2csv'.
Can someone please help me!!
Change the import to:
import * as json2csv from 'json2csv';
Then implement as:
let fields = ['field1', 'field2', 'field3'];
  let result = json2csv({ data:[{ field1: 'a', field2: 'b', field3: 'c' }], fields: fields });
  console.log(result);
The other answers are now outdated. For json2csv version 5, first:
npm install --save json2csv #types/json2csv
Then at the top of your Angular component/service/etc:
import { parse } from 'json2csv';
Then to generate the csv in your method:
const csv = parse(json);
There are, of course, all kinds of options you can pass to parse() and json2csv exposes other classes and functions you can import and use as well. There are useful examples in the tests from #types/json2csv.
Here is a complete CSV download implementation:
<a [download]="csvFileName" [href]="getCSVDownloadLink()">CSV export</a>
import { Component } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
import * as json2csv from 'json2csv';
#Component({
selector: 'csv-download',
templateUrl: './csv-download.component.html',
styleUrls: ['./csv-download.component.scss']
})
export class CsvDownloadComponent {
public csvFileName = `test.csv`;
private SOME_DATA: any[] = [{id: 1, name: 'Peter'}, {id: 2, name: 'Sarah'}];
constructor(
private domSanitizer: DomSanitizer,
) { }
getCSVDownloadLink() {
return this.generateCSVDownloadLink({
filename: this.csvFileName,
data: this.SOME_DATA,
columns: [
'id',
'name',
],
});
}
// you can move this method to a service
public generateCSVDownloadLink(options: { filename: string, data: any[], columns: string[] }): SafeUrl {
const fields = options.columns;
const opts = { fields, output: options.filename };
const csv = json2csv.parse(options.data, opts);
return this.domSanitizer.bypassSecurityTrustUrl('data:text/csv,' + encodeURIComponent(csv));
}
}
You can use the angular 2 version of the library. The link to the same is: https://github.com/aqeel-legalinc/angular2-json2csv

TypeScript generics with Angular2 injector

I'm trying to use dependancy injection in angular using an injector. I want to me able to instantiate types at runtime depending on what this component is sent.
#Injectable()
export class ServiceInjectionManager {
private _injector: ReflectiveInjector;
constructor() {
this._injector = ReflectiveInjector.resolveAndCreate([
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backendInstance, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
AppSettings,
HierarchyService
]);
}
public resolve<T extends HierarchyService>(type:any): T {
return this._injector.get(type);
}
}
I can't seem to find a way to pass a type. I have taken multiple approaches including:
public resolve<T extends HierarchyService>(T): T {
return this._injector.get(T);
}
It seems that generics in TypeScript are not the same as in .NET.

Ionic 2 File Plugin usage examples

Does anyone have complete examples about how to use the Cordova Native File Plugin in a Ionic 2/Angular 2 project?
I installed this plugin but the documentation don't seems to make much sense to me due the fact it is fragmented and lacks of a complete example, including all needed imports.
For example, the following example don't shows where objects like LocalFileSystem or window came from.
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
console.log('file system open: ' + fs.name);
fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {
console.log("fileEntry is file?" + fileEntry.isFile.toString());
// fileEntry.name == 'someFile.txt'
// fileEntry.fullPath == '/someFile.txt'
writeFile(fileEntry, null);
}, onErrorCreateFile);
}, onErrorLoadFs);
For example, I need to crate a property file. First I need to check if a file exists on app sandbox storage area, if don't exists I must create it. Then I must open the file write data and save it . How could I do that?
Ionic 2 comes with a Cordova file plugin wrapper:
http://ionicframework.com/docs/v2/native/file/.
The necessary file system paths (e.g. cordova.file.applicationDirectory) you can find here at the documentation of the original plugin:
https://github.com/apache/cordova-plugin-file#where-to-store-files. Note that not all platforms support the same storage paths.
I even managed to build a file browser with it. Use it like so:
import {Component} from '#angular/core';
import {File} from 'ionic-native';
...
File.listDir(cordova.file.applicationDirectory, 'mySubFolder/mySubSubFolder').then(
(files) => {
// do something
}
).catch(
(err) => {
// do something
}
);
Here is an example using IonicNative for an app I am working on where I want
to send an email with a csv file attachment.
import {EmailComposer} from '#ionic-native/email-composer';
import {File} from '#ionic-native/file';
class MyComponent {
constructor(private emailComposer: EmailComposer, private file: File) {
}
testEmail() {
this.file.writeFile(this.file.dataDirectory, 'test.csv', 'hello,world,', {replace: true})
.then(() => {
let email = {
to: 'email#email',
attachments: [
this.file.dataDirectory + 'test.csv'
],
subject: 'subject',
body: 'body text...',
isHtml: true
};
this.emailComposer.open(email);
})
.catch((err) => {
console.error(err);
});
}
}
This was tested with ionic 3.7.0 on IOS.

Resources