Base solution for your next web application

Activities of "alexanderpilhar"

Ah, right, I would need to have this file located at aspnet-zero-core\aspnet-core\src\MyCompanyName.AbpZeroTemplate.Web.Host\wwwroot\Common\Images!

But it is not there, not even in the github project. But there are the .svg files - so, it seems there is the wrong file linked somewhere?

Hi @ismcagdas

No, I don't use the public website and it is not running anywhere. Still, the log message appears very often.

Hi @m.aliozkaya

It doesn't require authentication on my project (tried using browser's private mode, also tried on phone where I never ever logged in to swagger ui for sure)... I need to figure out why. Any ideas?

UPDATE

~~It does require authentication in development environment - but it does NOT require authentication in production environment...~~

What I mean is that I can open https://mydomain/swagger/index.html without any authentication / authorization. I want Swagger UI to be available only when authorized.

This seems to do what i want to achieve: Swagger UI with login form and role-based api visibility

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

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!

Showing 11 to 20 of 173 entries