Base solution for your next web application

Activities of "alexanderpilhar"

UPDATE [A minimalist ASPNETZERO Angular client #11164](https://support.aspnetzero.com/QA/Questions/11164/A-minimalist-ASPNETZERO-Angular-client)

That's it for now ; )

SignalR

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);
    }
}

DONE

That's it! Now we can make use of ASPNETZERO's SignalR capabilities in our minimalist ASPNETZERO Angular client!

Localization

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 {}

AbpModule

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();
        };
    }
}

DONE

That's it! Now we can make use of ASPNETZERO's localization capabilities in our minimalist ASPNETZERO Angular client!

NgxSpinner

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"
],

DONE

That's it! Now we can make use of NgxSpinner in our minimalist ASPNETZERO Angular client!

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).

Features

  • Access to ASPNETZERO Core Service (public/non-authorized API only)
  • NgxSpinner (package ngx-spinner)
  • Localization
  • SignalR (public/non-authorized endpoint only)

Access to ASPNETZERO Core Services

This documentation shows how to create a minimalist ASPNETZERO Angular client that can consume ASPNETZERO Core Services (public/non-authorized API only).

Add package 'nswag'

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 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"
]

Additional Resources and AppModule

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):

  • Adding nouislider to allowedCommonJsDependencies
  • Adding theme ressources 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 scripts

ASPNETZERO 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.

Showing 21 to 30 of 224 entries