Base solution for your next web application

Activities of "morindo"

Ok, I've added npm packages like classlist.js and web-animations-js in order to support IE 11.

How To Fix Your Angular App When It’s Not Working in IE11 https://medium.com/better-programming/how-to-fix-your-angular-app-when-its-not-working-in-ie11-eb24cb6d9920

Now, last error is due too SignalR, related to:

IE (11) - not working, no dashboard - datatables not loading https://support.aspnetzero.com/QA/Questions/8471

I'm getting SCRIPT1002: Syntax error abp.signalr-client.js (32,30)

` function configureConnection(connection) { // Set the common hub abp.signalr.hubs.common = connection;

    let tries = 1;
    let reconnectTime = abp.signalr.reconnectTime;

    // Reconnect loop
    function tryReconnect() {
        if (tries > abp.signalr.maxTries) {
            return;
        } else {
            connection.start()
                .then(() => {
                    reconnectTime = abp.signalr.reconnectTime;
                    tries = 1;
                    console.log('Reconnected to SignalR server!');
                }).catch(() => {
                    tries += 1;
                    reconnectTime = abp.signalr.increaseReconnectTime(reconnectTime);
                    setTimeout(() => tryReconnect(), reconnectTime);
                });
        }
    }

`

I fixed the problem locally adding es5 syntax in abp.signalr-client.js file.

Found the answer: @swimlane/ngx-charts v13 has peer dependency d3-array v2.4.0. Since v2 d3-array has stopped to support ie11.

https://github.com/swimlane/ngx-charts/issues/1353

I've downgrade @swimlane/ngx-charts to "^12.1.0" and it work now.

I'm now getting a new error, yeah!

In app-main-main-module.js, Syntax error.

/**
 * The Emitter is a simple emitter class that provides you with `on()`, `off()` and `trigger()` methods
 * @class Emitter
 * @module Emitter
 */
class Emitter {
  constructor() {
    this.callbacks = {};
  }

  /**
   * Registers callbacks by event name
   * @param {String} type
   * @param {...Function} callbacks
   */
  on(type, ...callbacks) {
    if (!this.callbacks[type]) {
      this.callbacks[type] = [];
    }

    this.callbacks[type].push(...callbacks);

    return this;
  }

  /**
   * Unregisters callbacks by event name
   * @param {String} type
   * @param {Function} callback
   */
  off(type, callback) {
    if (!this.callbacks[type]) {
      return null;
    }

    const copy = this.callbacks[type].slice(0);

    for (let i = 0; i < copy.length; i++) {
      if (callback === copy[i]) {
        this.callbacks[type].splice(i, 1);
      }
    }

    return this;
  }

  /**
   * Triggers event callbacks by event object
   * @param {AbstractEvent} event
   */
  trigger(event) {
    if (!this.callbacks[event.type]) {
      return null;
    }

    const callbacks = [...this.callbacks[event.type]];
    const caughtErrors = [];

    for (let i = callbacks.length - 1; i >= 0; i--) {
      const callback = callbacks[i];

      try {
        callback(event);
      } catch (error) {
        caughtErrors.push(error);
      }
    }

    if (caughtErrors.length) {
      /* eslint-disable no-console */
      console.error(`Draggable caught errors while triggering '${event.type}'`, caughtErrors);
      /* eslint-disable no-console */
    }

    return this;
  }
}
exports.default = Emitter;

Hi guys,

Truly sorry to ask this question, I know, IE11 is dead. Unfortunately, couple of our clients still have IE11 and they cannot use our software at the moment.

We are getting a script error in Angular vendor.js file probably caused by es6 syntax. It seems to be related to [email protected] package that reference d3-array@^2.4.0.

Is there anything we can do to fix this?

We use Angular/ASP.NET Core 8.6.0.0 version.

Thank you,

`// "./node_modules/d3-array/src/count.js": /!**************************************!
!
./node_modules/d3-array/src/count.js ! **************************************/ /! exports provided: default / // (function(module, webpack_exports, webpack_require) {

"use strict"; webpack_require.r(webpack_exports); /* harmony export (binding) */ webpack_require.d(webpack_exports, "default", function() { return count; }); function count(values, valueof) { let count = 0; if (valueof === undefined) { for (let value of values) { if (value != null && (value = +value) >= value) { ++count; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) { ++count; } } } return count; }`

Thank you very much maliming. Cheers!

So adding SwaggerNullableParameterFilter class is wokring, it remove the null check.

Now, on the nswag (refresh.bat) generation process, how can we opt out null parameter in query string or replace null value by empty string so encodeURIComponent does not encode null to "null". Is there a way to do this? I've been searching this morning and can't find anything to fix it.

Example:

getAll(filter: string | null | undefined, productCategoryIdFilter: string | null | undefined, taxRateIdFilter: string | null | undefined, sorting: string | null | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable<PagedResultDtoOfGetProductForViewDto> { let url_ = this.baseUrl + "/api/services/app/Products/GetAll?"; if (filter !== undefined) url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent("" + productCategoryIdFilter) + "&"; if (taxRateIdFilter !== undefined) url_ += "TaxRateIdFilter=" + encodeURIComponent("" + taxRateIdFilter) + "&"; if (sorting !== undefined) url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; if (skipCount === null) throw new Error("The parameter 'skipCount' cannot be null."); else if (skipCount !== undefined) url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; if (maxResultCount === null) throw new Error("The parameter 'maxResultCount' cannot be null."); else if (maxResultCount !== undefined) url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; url_ = url_.replace(/[?&]$/, "");

The line:

if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent("" + productCategoryIdFilter) + "&";

Should be:

if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent(productCategoryIdFilter ? "" + productCategoryIdFilter : "") + "&";

Thank you,

Thank you very much @maliming, it just works!

/**
 * @param filter (optional) 
 * @param nameFilter (optional) 
 * @param sorting (optional) 
 * @param skipCount (optional) 
 * @param maxResultCount (optional) 
 * @return Success
 */
getAll(filter: string | null | undefined, nameFilter: string | null | undefined, sorting: string | null | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable&lt;PagedResultDtoOfGetProductCategoryForViewDto&gt; {
    let url_ = this.baseUrl + "/api/services/app/ProductCategories/GetAll?";
    if (filter !== undefined)
        url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; 
    if (nameFilter !== undefined)
        url_ += "NameFilter=" + encodeURIComponent("" + nameFilter) + "&"; 
    if (sorting !== undefined)
        url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; 
    if (skipCount === null)
        throw new Error("The parameter 'skipCount' cannot be null.");
    else if (skipCount !== undefined)
        url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; 
    if (maxResultCount === null)
        throw new Error("The parameter 'maxResultCount' cannot be null.");
    else if (maxResultCount !== undefined)
        url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; 
    url_ = url_.replace(/[?&]$/, "");

    let options_ : any = {
        observe: "response",
        responseType: "blob",			
        headers: new HttpHeaders({
            "Accept": "text/plain"
        })
    };

Thanks @maliming for the quick reply. So, for Swashbuckle.AspNetCore nuget package the version is 5.0.0-rc4. Just so you know, it's a new solution, started from ASP.NET Zero (Angular) version 8.1.0 and I didn't update any nuget or npm packages.


Here is my service.config.nswag configuration file.

{
  "runtime": "Default",
  "defaultVariables": null,
  "documentGenerator": {
    "fromDocument": {
      "url": "http://localhost:22742/swagger/v1/swagger.json",
      "output": null
    }
  },
  "codeGenerators": {
    "openApiToTypeScriptClient": {
      "className": "{controller}ServiceProxy",
      "moduleName": "",
      "namespace": "",
      "typeScriptVersion": 2.7,
      "template": "Angular",
      "promiseType": "Promise",
      "httpClass": "HttpClient",
      "useSingletonProvider": false,
      "injectionTokenType": "InjectionToken",
      "rxJsVersion": 6.0,
      "dateTimeType": "MomentJS",
      "nullValue": "Undefined",
      "generateClientClasses": true,
      "generateClientInterfaces": false,
      "generateOptionalParameters": false,
      "exportTypes": true,
      "wrapDtoExceptions": false,
      "exceptionClass": "ApiException",
      "clientBaseClass": null,
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "protectedMethods": [],
      "configurationClass": null,
      "useTransformOptionsMethod": false,
      "useTransformResultMethod": false,
      "generateDtoTypes": true,
      "operationGenerationMode": "MultipleClientsFromPathSegments",
      "markOptionalProperties": false,
      "generateCloneMethod": false,
      "typeStyle": "Class",
      "classTypes": [],
      "extendedClasses": [],
      "extensionCode": "service.extensions.ts",
      "generateDefaultValues": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateConstructorInterface": true,
      "convertConstructorInterfaceData": false,
      "importRequiredTypes": true,
      "useGetBaseUrlMethod": false,
      "baseUrlTokenName": "API_BASE_URL",
      "queryNullValue": "",
      "inlineNamedDictionaries": false,
      "inlineNamedAny": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "../src/shared/service-proxies/service-proxies.ts"
    },
    "openApiToCSharpClient": {
      "clientBaseClass": null,
      "configurationClass": null,
      "generateClientClasses": true,
      "generateClientInterfaces": false,
      "injectHttpClient": false,
      "disposeHttpClient": true,
      "protectedMethods": [],
      "generateExceptionClasses": true,
      "exceptionClass": "SwaggerException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": false,
      "useBaseUrl": true,
      "generateBaseUrlProperty": true,
      "generateSyncMethods": false,
      "exposeJsonSerializerSettings": false,
      "clientClassAccessModifier": "public",
      "typeAccessModifier": "public",
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "s",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "{controller}Client",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": false,
      "generateJsonMethods": true,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.ObjectModel.ObservableCollection",
      "responseDictionaryType": "System.Collections.Generic.Dictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "MyNamespace",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTime",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTime",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.ObjectModel.ObservableCollection",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.Dictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Inpc",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": null
    },
    "openApiToCSharpController": {
      "controllerBaseClass": null,
      "controllerStyle": "Partial",
      "controllerTarget": "AspNet",
      "useCancellationToken": false,
      "useActionResultType": false,
      "generateModelValidationAttributes": false,
      "routeNamingStrategy": "None",
      "className": "{controller}",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [
        "System.Web.Http"
      ],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": false,
      "generateJsonMethods": true,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.ObjectModel.ObservableCollection",
      "responseDictionaryType": "System.Collections.Generic.Dictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "MyNamespace",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTime",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTime",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.IEnumerable",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.Dictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Inpc",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": null
    }
  }
}

When I run refresh.bat, I get the following result.

R:\Dev\Repos\BakeSimple\angular\nswag>"..\node_modules\.bin\nswag" run
NSwag NPM CLI
NSwag command line tool for .NET Core NetCore21, toolchain v13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0))
Visit http://NSwag.org for more information.
NSwag bin directory: R:\Dev\Repos\BakeSimple\angular\node_modules\nswag\bin\binaries\NetCore21

Executing file 'R:\Dev\Repos\BakeSimple\angular\nswag\service.config.nswag' with variables ''...
Done.

Duration: 00:00:05.3494464

Finally, here is part of my service-proxies.ts generated file.

/**
     * @param filter (optional) 
     * @param nameFilter (optional) 
     * @param sorting (optional) 
     * @param skipCount (optional) 
     * @param maxResultCount (optional) 
     * @return Success
     */
    getAll(filter: string | undefined, nameFilter: string | undefined, sorting: string | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable<PagedResultDtoOfGetProductCategoryForViewDto> {
        let url_ = this.baseUrl + "/api/services/app/ProductCategories/GetAll?";
        if (filter === null)
            throw new Error("The parameter 'filter' cannot be null.");
        else if (filter !== undefined)
            url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; 
        if (nameFilter === null)
            throw new Error("The parameter 'nameFilter' cannot be null.");
        else if (nameFilter !== undefined)
            url_ += "NameFilter=" + encodeURIComponent("" + nameFilter) + "&"; 
        if (sorting === null)
            throw new Error("The parameter 'sorting' cannot be null.");
        else if (sorting !== undefined)
            url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; 
        if (skipCount === null)
            throw new Error("The parameter 'skipCount' cannot be null.");
        else if (skipCount !== undefined)
            url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; 
        if (maxResultCount === null)
            throw new Error("The parameter 'maxResultCount' cannot be null.");
        else if (maxResultCount !== undefined)
            url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; 
        url_ = url_.replace(/[?&]$/, "");

        let options_ : any = {
            observe: "response",
            responseType: "blob",
            headers: new HttpHeaders({
                "Accept": "text/plain"
            })
        };

        return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => {
            return this.processGetAll(response_);
        })).pipe(_observableCatch((response_: any) => {
            if (response_ instanceof HttpResponseBase) {
                try {
                    return this.processGetAll(<any>response_);
                } catch (e) {
                    return <Observable<PagedResultDtoOfGetProductCategoryForViewDto>><any>_observableThrow(e);
                }
            } else
                return <Observable<PagedResultDtoOfGetProductCategoryForViewDto>><any>_observableThrow(response_);
        }));
    }

    protected processGetAll(response: HttpResponseBase): Observable<PagedResultDtoOfGetProductCategoryForViewDto> {
        const status = response.status;
        const responseBlob = 
            response instanceof HttpResponse ? response.body : 
            (<any>response).error instanceof Blob ? (<any>response).error : undefined;

        let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }};
        if (status === 200) {
            return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
            let result200: any = null;
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
            result200 = PagedResultDtoOfGetProductCategoryForViewDto.fromJS(resultData200);
            return _observableOf(result200);
            }));
        } else if (status !== 200 && status !== 204) {
            return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
            }));
        }
        return _observableOf<PagedResultDtoOfGetProductCategoryForViewDto>(<any>null);
    }
    ```

I'm having the same issue. Should optional parameters accept null value?

Thanks,

Okay, I found a way.

I created this SkipConcurrentExecutionAttribute class for Hangfire in order to skip concurrent task execution. Since Hangfire already supports logging with Log4Net, I've used the LogProvider.GetCurrentClassLogger() method to get the logger.

See Hangfire documentation - Configuring logging [http://docs.hangfire.io/en/latest/configuration/configuring-logging.html])

/// <summary>
    /// Attribute to skip a job execution if the same job is already running.
    /// Mostly taken from: http://discuss.hangfire.io/t/job-reentrancy-avoidance-proposal/607
    /// </summary>
    public class SkipConcurrentExecutionAttribute : JobFilterAttribute, IServerFilter
    {
        private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();

        private readonly int _timeoutInSeconds = 1;

        public SkipConcurrentExecutionAttribute()
        {
        }

        public SkipConcurrentExecutionAttribute(int timeoutInSeconds) : this()
        {
            if (timeoutInSeconds < 0) throw new ArgumentException("Timeout argument value should be greater that zero.");

            _timeoutInSeconds = timeoutInSeconds;
        }


        public void OnPerforming(PerformingContext filterContext)
        {
            var resource = String.Format(
                                 "{0}.{1}",
                                filterContext.Job.Type.FullName,
                                filterContext.Job.Method.Name);

            var timeout = TimeSpan.FromSeconds(_timeoutInSeconds);

            try
            {
                var distributedLock = filterContext.Connection.AcquireDistributedLock(resource, timeout);
                filterContext.Items["DistributedLock"] = distributedLock;
            }
            catch (Exception)
            {
                filterContext.Canceled = true;
                Logger.WarnFormat("Cancelling run for {0} job, id: {1} ", resource, filterContext.JobId);
            }
        }

        public void OnPerformed(PerformedContext filterContext)
        {
            if (!filterContext.Items.ContainsKey("DistributedLock"))
            {
                throw new InvalidOperationException("Can not release a distributed lock: it was not acquired.");
            }

            var distributedLock = (IDisposable)filterContext.Items["DistributedLock"];
            distributedLock.Dispose();
        }
    }

Hi,

I'm trying to create an attribute class and I would like to get the ILogger but I'm getting a null reference, how can I inject the ILogger into an attribute class?

I've tried to register the class with an ITransientDependency and also in the PreInitialize() method:

IocManager.Register<SkipConcurrentExecutionAttribute>(DependencyLifeStyle.Transient);

Thank you,

/// <summary>
    /// Attribute to skip a job execution if the same job is already running.
    /// Mostly taken from: http://discuss.hangfire.io/t/job-reentrancy-avoidance-proposal/607
    /// </summary>
    public class SkipConcurrentExecutionAttribute : JobFilterAttribute, IServerFilter
    {
        public ILogger Logger { get; set; }

        private readonly int _timeoutInSeconds = 1;

        public SkipConcurrentExecutionAttribute()
        {
            Logger = NullLogger.Instance;
        }

        public SkipConcurrentExecutionAttribute(int timeoutInSeconds) : this()
        {
            if (timeoutInSeconds < 0) throw new ArgumentException("Timeout argument value should be greater that zero.");

            _timeoutInSeconds = timeoutInSeconds;
        }


        public void OnPerforming(PerformingContext filterContext)
        {
            var resource = String.Format(
                                 "{0}.{1}",
                                filterContext.Job.Type.FullName,
                                filterContext.Job.Method.Name);

            var timeout = TimeSpan.FromSeconds(_timeoutInSeconds);

            try
            {
                var distributedLock = filterContext.Connection.AcquireDistributedLock(resource, timeout);
                filterContext.Items["DistributedLock"] = distributedLock;
            }
            catch (Exception)
            {
                filterContext.Canceled = true;
                Logger.WarnFormat("Cancelling run for {0} job, id: {1} ", resource, filterContext.JobId); // Not logging anything...
            }
        }

        public void OnPerformed(PerformedContext filterContext)
        {
            if (!filterContext.Items.ContainsKey("DistributedLock"))
            {
                throw new InvalidOperationException("Can not release a distributed lock: it was not acquired.");
            }

            var distributedLock = (IDisposable)filterContext.Items["DistributedLock"];
            distributedLock.Dispose();
        }
    }
Showing 1 to 10 of 13 entries