June 20, 2022

Convert seconds to hh-mm-ss with JavaScript/TypeScript

I received this requirement while working on Angular application and developing the two-factor authentication screen with SMS OTP. This screen has the timer countdown to show remaining time in seconds (in mm:ss format) to enable the button to resend OTP.

I found two different functions to format the seconds in hh:mm:ss format.

In first method, you can manually perform the arithmetic operations to extract hours, minutes and seconds from given value.

Convert_Seconds_To_HHMMSS(seconds) {

      let hour = Math.floor(seconds / 3600);
      let minute = Math.floor((seconds % 3600) / 60);
      let second = seconds % 60;

      if(hour.toString().length === 1) {
            hour = `0${hour}`;
      }
      if(minute.toString().length === 1) {
            minute = `0${minute}`;
      }
      if(second.toString().length === 1) {
            second = `0${second}`;
      };

      let timeFormatted = `${hour}-${minute}-${second}`;

      return timeFormatted;
}

In second method, you can use one-liner solution using Date.toISOString() function.

new Date(seconds * 1000).toISOString().substring(11, 16)

If the seconds value is less than 3600 (less than an hour) or you don't want t show the hours in formatted string, and only need to show minutes and seconds (mm:ss), then simply change the arguments for substring() function to extract the required string part.

new Date(seconds * 1000).toISOString().substring(14, 19)

May 19, 2022

Configure IIS for CORS preflight OPTIONS request

To configure IIS to allow an ASP.NET app to receive and handle OPTIONS requests, we have to add the following configuration to the app's web.config file in the system.webServer > handlers section:

<system.webServer>
  <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="OPTIONSVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" 
       type="System.Web.Handlers.TransferRequestHandler" 
       preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>

Since the default ExtensionlessUrlHandler-Integrated-4.0 module registration only allows GET, HEAD, POST, and DEBUG requests with extensionless URLs. We will replace this module by first removing it and add with different attribute values to allow OPTIONS requests to reach the app.

After making these configuration changes, if the application still not responding as expected then you need to check the IIS's Request Filtering. It might be the case that IIS disallows OPTIONS verb at the root web application level.

  • Open IIS Manager
  • Click on root application
  • Click on Request Filtering
  • If OPTIONS appears in list, remove that entry and re-add with with Allow Verb... context menu option.

References:

Related Post(s):

How Cross Origin Resource Sharing (CORS) Works

Browser security prevents a web page from making AJAX requests to another domain. This restriction is called the same-origin policy, which preempts a malicious site from reading sensitive data from another site. .

Cross Origin Resource Sharing (CORS) is a W3C standard that allows a server to lighten the same-origin policy. Because sometimes you might want to let other sites call your web API, in this case you have to configure CORS policy as per the requirements, so that the server can accept traffic for pre-defined scenarios and reject the calls otherwise.

You might enable the CORS using [EnableCors] attribute correctly in .Net Web API Project, but still the things don't work as per the expectation. So its important to understand how CORS works.

The CORS specification introduces several new HTTP headers that enable cross-origin requests. If a browser supports CORS, it sets these headers automatically for cross-origin requests; you don't need to do manually in your JavaScript code.

Lets say you are making an AJAX call to the API http://abc.com/api/test, from the website http://localhost:4201. Since the Origin header defines the domain of the source website which is making the request, in this case the Origin is http://localhost:4201

Here is an example of a cross-origin request representation:

(In Raw Format)

Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Host: http://abc.com/api/test
Origin: http://localhost:4201
Referer: http://localhost:4201/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0

If the server allows the request, it sets the Access-Control-Allow-Origin response header. The value of this header either matches the Origin header (if the given domain is allowed), or is the wildcard value * (if any origin is allowed).

(In Raw Format)

content-type: application/javascript
content-length: 678
access-control-allow-origin: *
date: Mon, 16 May 2022 09:31:51 GMT

If the response does not include the Access-Control-Allow-Origin header, the AJAX request fails, and the browser disallows the request.

Preflight Requests

For some CORS requests, the browser sends an additional request, called a "preflight request", before it sends the actual request for the resource.

Browser sends the preflight request in certain conditions. Following are conditions in which the browser will not send the preflight request:

  1. The request method is GET, HEAD, or POST
  2. The application sends only these request headers: Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID (any other request header will cause the browser to send preflight request).
    • Note that this restriction applies to the headers which the application will add to the request by calling setRequestHeader() method on the XMLHttpRequest object. These request headers are called as author request headers in the CORS specification .
    • The headers set by the browser (like User-Agent, Host, or Content-Length) are excluded from this restriction.
  3. If the Content-Type header is set, its value could only be one of the following (any other value for this header will cause the browser to send preflight request):
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Here is an example of a preflight OPTIONS request:

(In Raw Format)

OPTIONS http://abc.com/api/test HTTP/2
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Access-Control-Request-Headers: myheader1
Access-Control-Request-Method: GET
Connection: keep-alive
Host: http://abc.com
Origin: http://localhost:4201
Referer: http://localhost:4201/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0

As seen in above example request, the pre-flight request uses the HTTP OPTIONS method. It includes two special headers:

  • Access-Control-Request-Method: The HTTP method that will be used for the actual request (GET method in this above example).
  • Access-Control-Request-Headers: A list of request headers that the application set on the actual request, myheader1 is a custom header set in above example. Note that this does not include headers that the browser sets itself.

Here is an example response, assuming that the server allows the request:

(In Raw Format)

HTTP/1.1 200 OK
Access-Control-Allow-Headers: myheader1
Access-Control-Allow-Origin: *
Content-Length: 0
Date: Mon, 16 May 2022 09:40:05 GMT

The response can include the Access-Control-Allow-Methods header that lists the allowed methods (not listed in above example). The Access-Control-Allow-Headers header lists the allowed headers. If the preflight request succeeds (as in above case), then the browser sends the actual request.

References:

Related Post(s):

April 25, 2022

Angular - Can't bind to 'x' since it isn't a known property of 'y'

In Angular 11, I got this error:

error NG8002: Can't bind to 'x' since it isn't a known property of 'my-component'.
  1. If 'my-component' is an Angular component and it has 'x' input, then verify 
       that it is part of this module.
  2. If 'my-component' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the 
       '@NgModule.schemas' of this component to suppress this message.
  3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.

Apparently it is telling the reasons behind it, but still it did not inform about which specific module is missing if you do not know it yourself.

While searching for my problem, I have found the following possible scenarios, which may cause this error.

  1. When you have defined a custom input field 'x' in your component, first thing to check is if the property 'x' is defined in your component with an proper Input() decorator.

    In Html:

    <my-component [someInputProperty]="someValue">
    	

    In .ts file:

    export class MyComponent {
    
      @Input()
      someInputProperty: string;
      ...
    }
    	
  2. Also check the spelling for property names (case-sensitive) to make sure its free from typo.

    In .ts file

    export class MyComponent {
    
      @Input()
      someInputProperty: string;
      ...
    }
    	

    In Html (wrong):

    <my-component [someinputproperty]="someValue">
    	

    Which should be (correct):

    <my-component [someInputProperty]="someValue">
    	
  3. If you have defined the component in a custom module, make sure that you have registered this component (or directive or pipe) class in NgModule's declarations array:

    @NgModule({
      ...
      declarations: [
    	...,
    	MyComponent
      ],
      ...
    })
    export class AModule { }
    	
  4. If you have defined the component in a custom module (AModule), and want to use it in another module (BModule). Make sure that you have registered this component (or directive or pipe) class in source (AModule) module's declarations array as well as exports array:

    @NgModule({
      ...
      declarations: [
    	...,
    	MyComponent
      ],
      ...
      exports: [
    	...,
    	MyComponent
      ],
    
    })
    export class AModule { }
    	
  5. For common Angular default attributes, we need to memorize the module needs to be imported. For example:

    • For ngFor attribute, we need to import CommonModule.
    • In a similar fashion, for ngModel we need to import FormsModule.

April 20, 2022

Angular CLI - Generate components in a specific folder

In Visual Studio Code, following are the methods that can be used to create a component in a specific directory.

Method 1: Open in Integrated Terminal

You want to create a component in a app/components folder as shown in the image below:

  • Right click on the folder in which you want to create component.
  • Select option Open in Integrated Terminal

  • It will open a new terminal pane with the selected path.

  • In new terminal, type the command to create new component:

       ng g c component1
    

Method 2: Copy Relative Path and Paste on Terminal

  • Right click on folder in which you want to create component
  • From context menu, select Copy Relative Path

  • On termincal, type cd, press space and then paste the copied path. It will change the current working directory.
  • Then you can run the command to create new component.

       ng g c component1
    

Method 3: Manually Type complete path on terminal

In create component command, append the full folder path before compnent name.

   ng g c path/from/app/folder/component1

In our exmaple, to create a component in components folder (which is a sub-folder inside app), the command will be:

   ng g c components/component1

If your application have multiple modules, you may want to specify the existing module in which you need to create new component.

   ng g c employee/component1 --module=employee/employee.module.ts

This command will create component in employee module.