UPDATE [A minimalist ASPNETZERO Angular client #11164](https://support.aspnetzero.com/QA/Questions/11164/A-minimalist-ASPNETZERO-Angular-client)
That's it for now ; )
This documentation shows how to make use of ASPNETZERO's SignalR capabilities (public/non-authorized endpoint only).
Add package:
npm install --save --legacy-peer-deps @microsoft/signalr
Add assets and scripts to ./angular.json
:
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "abp.signalr-client.js",
"input": "node_modules/abp-web-resources/Abp/Framework/scripts/libs",
"output": "/assets/abp"
}
],
"scripts": [
"src/assets/abp-web-resources/abp.js",
"node_modules/@microsoft/signalr/dist/browser/signalr.min.js"
]
Adapt ./src/typings.d.ts
as follows:
///<reference path="../node_modules/abp-web-resources/Abp/Framework/scripts/abp.d.ts"/>
///<reference path="../node_modules/abp-web-resources/Abp/Framework/scripts/libs/abp.signalr.d.ts"/>
Copy ./src/shared/helpers/signalr-helper.ts
from original ASPNETZERO Angular client (original name is SignalRHelper.ts
).
Adapt the file as follows:
import { AppConsts } from '@shared/app-consts';
export class SignalRHelper {
static initSignalR(callback: () => void): void {
abp.signalr = {
autoConnect: false,
connect: undefined,
hubs: undefined,
qs: undefined,
remoteServiceBaseUrl: AppConsts.remoteServiceBaseUrl,
startConnection: undefined,
url: '/signalr-public',
};
let script = document.createElement('script');
script.src = `${AppConsts.appBaseUrl}/assets/abp/abp.signalr-client.js`;
script.onload = () => {
callback();
};
document.head.appendChild(script);
}
}
That's it! Now we can make use of ASPNETZERO's SignalR capabilities in our minimalist ASPNETZERO Angular client!
This documentation shows how to make use of ASPNETZERO's localization capabilities.
Add packages:
npm install --save --legacy-peer-deps lodash-es
npm install --save-dev --legacy-peer-deps @types/lodash-es
Edit ./tsconfig.json
to contain the following compilerOptions
:
"strict": false,
"paths": {
"@shared/*": ["./src/shared/*"]
}
Copy ./src/shared/services/locale-mappings.services.ts
from original ASPNETZERO Angular client (original location is ./src/shared/
).
Copy ./src/shared/pipes/localize.pipe.ts
from original ASPNETZERO Angular client (original location is ./src/shared/common/pipes/
).
Add ./src/shared/pipes/pipes.module.ts
with the following content:
import { NgModule } from '@angular/core';
import { LocalizePipe } from "./localize.pipe";
@NgModule({
declarations: [
LocalizePipe,
],
exports: [
LocalizePipe,
]
})
export class PipesModule {}
Add the following functions to ./src/app/app.module.ts
:
export function convertAbpLocaleToAngularLocale(locale: string): string {
return new LocaleMappingService().map('angular', locale);
}
export function getCurrentLanguage(): string {
return convertAbpLocaleToAngularLocale(abp.localization.currentLanguage.name);
}
function registerLocales(
resolve: (value?: boolean | Promise<boolean>) => void,
reject: any,
spinnerService: NgxSpinnerService
) {
if (abp.localization.currentLanguage.name && abp.localization.currentLanguage.name !== 'en-US') {
let angularLocale = convertAbpLocaleToAngularLocale(abp.localization.currentLanguage.name);
import(`/node_modules/@angular/common/locales/${angularLocale}.mjs`).then((module) => {
registerLocaleData(module.default);
spinnerService.hide();
resolve(true);
}, reject);
}
else {
spinnerService.hide();
resolve(true);
}
}
Adapt appInitializerFactory()
in ./src/app/app.module.ts
as follows:
export function appInitializerFactory(injector: Injector, platformLocation: PlatformLocation) {
return () => {
const spinnerService = injector.get(NgxSpinnerService);
spinnerService.show();
return new Promise<boolean>((resolve, reject) => {
AppConsts.appBaseHref = getBaseHref(platformLocation);
let appBaseUrl = `${getDocumentOrigin()}${AppConsts.appBaseHref}`;
AppPreBootstrap.run(
appBaseUrl,
() => {
registerLocales(resolve, reject, spinnerService);
resolve(true);
},
resolve,
reject
);
});
};
}
Adapt @NgModule
decorator in ./src/app/app.module.ts
as follows:
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
ServiceProxyModule,
HttpClientModule,
NgxSpinnerModule,
PipesModule,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{ provide: API_BASE_URL, useFactory: getRemoteServiceBaseUrl },
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps: [Injector, PlatformLocation],
multi: true
},
{
provide: LOCALE_ID,
useFactory: getCurrentLanguage
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Adapt ./src/app-pre-bootstrap.ts
as follows:
import { environment } from './environments/environment';
import { AppConsts } from './shared/app-consts';
import { XmlHttpRequestHelper } from './shared/helpers/xml-http-request-helper';
import { merge as _merge } from 'lodash-es';
import { LocaleMappingService } from '@shared/services/locale-mapping.service';
import { DateTime, Settings } from 'luxon';
export class AppPreBootstrap {
static run(appRootUrl: string, callback: () => void, resolve: any, reject: any): void {
AppPreBootstrap.getApplicationConfig(appRootUrl, () => {
AppPreBootstrap.getUserConfiguration(callback);
});
}
private static getApplicationConfig(appRootUrl: string, callback: () => void) {
let type = 'GET';
let url = appRootUrl + 'assets/' + environment.appConfig;
let customHeaders = [{
name: 'Abp.TenantId',
value: abp.multiTenancy.getTenantIdCookie() + ''
}];
XmlHttpRequestHelper.ajax(type, url, customHeaders, null, (result: any) => {
AppConsts.localeMappings = result.localeMappings;
AppConsts.appBaseUrlFormat = result.appBaseUrl;
AppConsts.remoteServiceBaseUrlFormat = result.remoteServiceBaseUrl;
AppConsts.appBaseUrl = result.appBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
AppConsts.remoteServiceBaseUrl = result.remoteServiceBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
callback();
});
}
private static getCurrentClockProvider(currentProviderName: string): abp.timing.IClockProvider {
if (currentProviderName === 'unspecifiedClockProvider') {
return abp.timing.unspecifiedClockProvider;
}
if (currentProviderName === 'utcClockProvider') {
return abp.timing.utcClockProvider;
}
return abp.timing.localClockProvider;
}
private static getRequestHeadersWithDefaultValues() {
const cookieLangValue = abp.utils.getCookieValue('Abp.Localization.CultureName');
let requestHeaders = {
'.AspNetCore.Culture': 'c=' + cookieLangValue + '|uic=' + cookieLangValue,
[abp.multiTenancy.tenantIdCookieName]: abp.multiTenancy.getTenantIdCookie(),
};
if (!cookieLangValue) {
delete requestHeaders['.AspNetCore.Culture'];
}
return requestHeaders;
}
private static getUserConfiguration(callback: () => void): any {
const token = abp.auth.getToken();
let requestHeaders = AppPreBootstrap.getRequestHeadersWithDefaultValues();
if (token) {
requestHeaders['Authorization'] = 'Bearer ' + token;
}
return XmlHttpRequestHelper.ajax(
'GET',
AppConsts.remoteServiceBaseUrl + '/AbpUserConfiguration/GetAll',
requestHeaders,
null,
(response) => {
let result = response.result;
_merge(abp, result);
abp.clock.provider = this.getCurrentClockProvider(result.clock.provider);
AppPreBootstrap.configureLuxon();
abp.event.trigger('abp.dynamicScriptsInitialized');
AppConsts.recaptchaSiteKey = abp.setting.get('Recaptcha.SiteKey');
AppConsts.subscriptionExpireNootifyDayCount = parseInt(
abp.setting.get('App.TenantManagement.SubscriptionExpireNotifyDayCount')
);
callback();
}
);
}
private static configureLuxon() {
let luxonLocale = new LocaleMappingService().map('luxon', abp.localization.currentLanguage.name);
DateTime.local().setLocale(luxonLocale);
DateTime.utc().setLocale(luxonLocale);
Settings.defaultLocale = luxonLocale;
if (abp.clock.provider.supportsMultipleTimezone) {
Settings.defaultZone = abp.timing.timeZoneInfo.iana.timeZoneId;
}
Date.prototype.toISOString = function () {
return DateTime.fromJSDate(this)
.setLocale('en')
.setZone(abp.timing.timeZoneInfo.iana.timeZoneId)
.toString();
};
Date.prototype.toString = function () {
return DateTime.fromJSDate(this)
.setLocale('en')
.setZone(abp.timing.timeZoneInfo.iana.timeZoneId)
.toString();
};
DateTime.prototype.toString = function () {
let date = this.setLocale('en').setZone(abp.timing.timeZoneInfo.iana.timeZoneId) as DateTime;
return date.toISO();
};
}
}
That's it! Now we can make use of ASPNETZERO's localization capabilities in our minimalist ASPNETZERO Angular client!
This documentation shows how to make use of package ngx-spinner.
Add package:
npm install --save --legacy-peer-deps ngx-spinner
Adapt @NgModule
decorator in ./src/app/app.module.ts
as follows:
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
ServiceProxyModule,
HttpClientModule,
NgxSpinnerModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{ provide: API_BASE_URL, useFactory: getRemoteServiceBaseUrl },
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps: [Injector, PlatformLocation],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Adapt appInitializerFactory()
in ./src/app/app.module.ts
as follows:
export function appInitializerFactory(injector: Injector, platformLocation: PlatformLocation) {
return () => {
const spinnerService = injector.get(NgxSpinnerService);
spinnerService.show();
return new Promise<boolean>((resolve, reject) => {
AppConsts.appBaseHref = getBaseHref(platformLocation);
let appBaseUrl = `${getDocumentOrigin()}${AppConsts.appBaseHref}`;
AppPreBootstrap.run(
appBaseUrl,
() => {
spinnerService.hide();
resolve(true);
},
resolve,
reject
);
});
};
}
Add stylings to ./angular.json
:
Note: Which css file to import depends on the type of animation you want to use.
"styles": [
"node_modules/ngx-spinner/animations/ball-clip-rotate.css"
],
That's it! Now we can make use of NgxSpinner in our minimalist ASPNETZERO Angular client!
This project was generated with Angular CLI version 14.0.4.
This project is a minimalist ASPNETZERO Angular client. It implements the very minimal requirements to make use of the ASPNETZERO Core Services (ASPNETZERO 11.2.1).
This documentation shows how to create a minimalist ASPNETZERO Angular client that can consume ASPNETZERO Core Services (public/non-authorized API only).
Add package:
npm install --save-dev nswag
Copy ./nswag/*
from original ASPNETZERO Angular client.
In ./nswag/service.config.nswag
update typeScriptVersion
and rxJsVersion
according to ./project.json
.
Go to ./nswag
and run ./resfresh.bat
(ASPNETZERO Core Services must be up and running).
This creates ./src/shared/service-proxies/service-proxies.ts
.
Copy ./src/shared/service-proxies/service-proxy.module.ts
from original ASPNETZERO Angular client.
Remove everything irrelevant:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { AbpHttpInterceptor } from 'abp-ng2-module';
import * as ApiServiceProxies from './service-proxies';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AbpHttpInterceptor, multi: true },
ApiServiceProxies.MyServiceProxy,
],
})
export class ServiceProxyModule {}
As we can see ServiceProxyModule
requires package abp-ng2-module
.
Add packages:
npm install --save luxon
npm install --save-dev @types/luxon
npm install --save --legacy-peer-deps abp-web-resources
npm install --save --legacy-peer-deps abp-ng2-module
Adding @types
to ./tsconfig.*.json
:
see: angular.io / guide / typescript-configuration / installable-typings-files
In ./tsconfig.app.json
add "luxon"
to compilerOptions.types
:
"compilerOptions": {
// ...,
"types": [
"luxon"
]
}
In ./tsconfig.spec.json
add "luxon"
to compilerOptions.types
:
"compilerOptions": {
// ...,
"types": [
// ...,
"luxon"
]
}
Adding "skipLibCheck": true
to ./tsconfig.compilerOptions
:
This is necessary to prevent build errors caused by abp-ng2-module
not being able to find references to abp-web-resources
.
{
"compilerOptions": {
// ...,
"skipLibCheck": true
}
}
Add ./src/typings.d.ts
with the following content:
see: typescriptlang.org / docs / handbook / declaration-files / publishing / red-flags
///<reference path="../node_modules/abp-web-resources/Abp/Framework/scripts/abp.d.ts"/>
Adding abp-web-resources
to ./src/assets/
:
Copy ./src/assets/abp-web-resources/abp.js
from original ASPNETZERO Angular client.
Adding "src/assets/abp-web-resources/abp.js"
to ./angular.json
to build.options
and test.options
:
"scripts": [
"src/assets/abp-web-resources/abp.js"
]
Copy the following files from original ASPNETZERO Angular client:
./src/app-pre-bootstrap.ts
./src/assets/appconfig.json
./src/assets/appconfig.production.json
./src/shared/app-consts.ts
(original name: AppConsts.ts
)./src/shared/helpers/xml-http-request-helper.ts
(original name: XmlHttpRequestHelper.ts
)The content of ./src/app-pre-bootstrap.ts
is the following:
import { environment } from './environments/environment';
import { AppConsts } from './shared/app-consts';
import { XmlHttpRequestHelper } from './shared/helpers/xml-http-request-helper';
export class AppPreBootstrap {
static run(appRootUrl: string, callback: () => void, resolve: any, reject: any): void {
AppPreBootstrap.getApplicationConfig(appRootUrl, () => {
callback();
});
}
private static getApplicationConfig(appRootUrl: string, callback: () => void) {
let type = 'GET';
let url = appRootUrl + 'assets/' + environment.appConfig;
let customHeaders = [{
name: 'Abp.TenantId',
value: abp.multiTenancy.getTenantIdCookie() + ''
}];
XmlHttpRequestHelper.ajax(type, url, customHeaders, null, (result: any) => {
AppConsts.localeMappings = result.localeMappings;
AppConsts.appBaseUrlFormat = result.appBaseUrl;
AppConsts.remoteServiceBaseUrlFormat = result.remoteServiceBaseUrl;
AppConsts.appBaseUrl = result.appBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
AppConsts.remoteServiceBaseUrl = result.remoteServiceBaseUrl.replace(AppConsts.tenancyNamePlaceHolderInUrl + '.', '');
callback();
});
}
}
Modify the following files:
./src/environments/environment.prod.ts
export const environment = {
production: true,
appConfig: 'appconfig.production.json'
};
./src/environments/environment.ts
export const environment = {
production: false,
appConfig: 'appconfig.json'
};
Modify ./src/app/app.module.ts
:
import { PlatformLocation } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppPreBootstrap } from 'src/app-pre-bootstrap';
import { AppConsts } from 'src/shared/app-consts';
import { API_BASE_URL } from 'src/shared/service-proxies/service-proxies';
import { ServiceProxyModule } from 'src/shared/service-proxies/service-proxy.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
export function appInitializerFactory(platformLocation: PlatformLocation) {
return () => {
return new Promise<boolean>((resolve, reject) => {
AppConsts.appBaseHref = getBaseHref(platformLocation);
let appBaseUrl = `${getDocumentOrigin()}${AppConsts.appBaseHref}`;
AppPreBootstrap.run(
appBaseUrl,
() => { resolve(true); },
resolve,
reject
);
});
};
}
function getDocumentOrigin() {
const docloc = document.location;
return docloc.origin ?? `${docloc.protocol}//${docloc.hostname}${docloc.port ? ':' + docloc.port : ''}`
}
export function getRemoteServiceBaseUrl(): string {
return AppConsts.remoteServiceBaseUrl;
}
export function getBaseHref(platformLocation: PlatformLocation) {
return platformLocation.getBaseHrefFromDOM() ?? '/';
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ServiceProxyModule,
HttpClientModule,
],
providers: [
{ provide: API_BASE_URL, useFactory: getRemoteServiceBaseUrl },
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps: [PlatformLocation],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Hey everybody!
A few years back I tried to create a separate Angular client that was able to consume ASPNETZERO's public API's (non-authorized): [Reduced Angular Client #4596](https://support.aspnetzero.com/QA/Questions/4596/Reduced-Angular-Client)
While this approach did the job back then I never was too happy with it as a lot of not needed resources remained.
A few days ago I decided to try another approach: starting with a new Angular application and only add what is necessary to make it work with ASPNETZERO's Core Services.
Today, I want to share with you my results (see next comment)!
I hope it is useful to some of you!
Cheers!
Alright, for whatever reason I thought I could use Metronic's ressources... My bad.
Still, the result is not working very well:
Anyway, I'm not going to try and get it right with noUiSlider. PrimeNG's Slider component works as expected and is all I need, so I switched to that one.
Thank you for your help!
Hi @ismcagdas and thank you for your fast response!
Could you please elaborate on what exactly I have to add to angular.json
file!?
So far, I tried the following (without success):
nouislider
to allowedCommonJsDependencies
src/assets/metronic/themes/default/plugins/global/plugins.bundle.css
to styles and adding src/assets/metronic/themes/default/plugins/global/plugins.bundle.js
to scriptsASPNETZERO v11.1.0, Angular, .NET 6.0
I want to make use of Metronic's noUiSlider in my Angular application but I can't figure out how... Unfortunately, there is no information about how to make use of certain components in Angular in Metronic's documentation.
Please, give me some instructions on how to make use of it!
Maybe also add it to 'Demo-UI-Elements'-page in ASPNETZERO-demo-application.