Showing posts with label TypeScript. Show all posts
Showing posts with label TypeScript. Show all posts

October 29, 2023

Kendo UI Grid - Nested child grids

To add hierarchy or nested child grids in Kendo UI Grid component, we can use the detailInit event for the parent grid and initialize the next level of hierarchy.

Lets say we have the following function which will return dummy data based on ParentAccountID.

private getGridData(parentAccountID: string) {
	var data = [
		{
			"RowLabel": "Initial Balance",
			"AccountID": "1",
			"AccountNo": 1,
			"AccountName": "Account1",
			"AccountTitle": "1 - Account1",
			"ParentAccountID": null,
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "2",
			"AccountNo": 2,
			"AccountName": "Account2",
			"AccountTitle": "2 - Account2",
			"ParentAccountID": null,
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "3",
			"AccountNo": 3,
			"AccountName": "Account3",
			"AccountTitle": "3 - Account3",
			"ParentAccountID": "1",
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "4",
			"AccountNo": 4,
			"AccountName": "Account4",
			"AccountTitle": "4 - Account4",
			"ParentAccountID": "2",
		}
	];

	var tempData = data;
	tempData = tempData.filter((obj) => {
		return obj.ParentAccountID === parentAccountID;
	});
		
	return tempData;
}

Here is how we can define the grid configuration to call above function to get data for ParentAccountID.

private createDataInquiryGrid3() {

	var tempData = this.getGridData(null);//Pass null as ParentAccountID for 1st level data.
	
	var element = $("#grid").kendoGrid({
		dataSource: {
			data: tempData,
		},
		height: 600,
		sortable: false,
		pageable: false,
		detailInit: this.detailInit,//this event will define child grid configuration
		dataBound: function () {
			this.expandRow(this.tbody.find("tr.k-master-row").first());
		},
		columns: [
			{ field: "AccountTitle", title: " ", aggregates: ["count"] },
			{ field: "AccountNo", title: "AccountNo" },
			{ field: "AccountName", title: "AccountName" },
		]
	});
}

private detailInit(e) {
	//e.data contains the current row's data-object.
	var getDataFunction = window["getGridData"];
	var tempData = getDataFunction(e.data.AccountID);
	
	$("<div/>").appendTo(e.detailCell).kendoGrid({
		dataSource: {
			data: tempData,
		},
		scrollable: false,
		sortable: false,
		pageable: false,
		columns: [
			{ field: "AccountTitle", title: " ", aggregates: ["count"] },
			{ field: "AccountNo", title: "AccountNo" },
			{ field: "AccountName", title: "AccountName" },
		]
	});
}

It will display the grids like this:

With detailInit event you can add multiple level of nested grids using the same event in nested grids.

References:

Related Post(s):

October 28, 2023

Kendo UI Grid - Column Template

Kendo UI grid will display the column data in table format. If we need to customize the display value for specific column we can use the template function. It allows us to define the template which renders the actual content for that column.

columns array can be defined like this:

columns: [
    { field: "AccountTitle", title: "AccountTitle"},
    { field: "TransactionType", title: "Type" },
    { field: "DatePosting", title: "Date", format: "{0:dd/MM/yyyy}" },
	{
		field: "Status",
		template: function (dataRow) {
			var linkHtml = "";
			if (dataRow.StatusNo == '1') {
				linkHtml = "<span style='color:green;'>Active</span>";
			}
			else {
				linkHtml = "<span style='color:red;'>Deactive</span<";
			}
			return linkHtml;
		},
		title: "Status"
	},
]

Note that Status column, we have defined a template function, which will accept the dataRow as parameter and return the final html as string, which the Kendo Grid will display as cell's content.

References:

Related Post(s):

July 24, 2023

Compile TypeScript code with ASP.NET WebSite Project

In previous posts we have seen how to configure TypeScript code with ASP.NET Core Project and Web Application Project . In this post we will configure the TypeScript in an ASP.NET WebSite Project (.Net Framework).

Suppose we already have an ASP.Net WebSite Project.

Inside Scripts folder, Create a new TypeScript file, say app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

As we changed some configurations for TypeScript compiler in the last post, we will do the same here. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": false,
    "target": "ES2015",
    "allowJs": false,
    "inlineSourceMap": true,
    "sourceRoot": "./",
    "outDir": "Scripts",
    "inlineSources": true,
    "lib": [ "es2015", "es2016", "dom", "es2018.promise" ]
  },
  "exclude": [
    "./Scripts/JS"
  ],
  "include": [
    "Scripts/*.ts"
  ],
  "compileOnSave": true
}

The include section above instructs the compiler to compile all typescritps files inside Scripts folder. It will exlude the files for compilation which will be palced inside Scripts/JS folder as mentioned in exclude section above. It also tells the compiler to copy the output js files in Scripts folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="Scripts/app.js"></script>

Save the changes, and reload the project.

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

Related Post(s):

June 22, 2023

Compile TypeScript code with ASP.NET Web Applicaton Project

In the last post we have seen how to configure TypeScript code with ASP.NET Core Project. In this post we will configure the TypeScript in an ASP.NET Web Applicaton Project (.Net Framework).

Suppose we already have an ASP.Net Web Application Project.

Inside Scripts folder, Create a new TypeScript file, say app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

As we changed some configurations for TypeScript compiler in the last post, we will do the same here. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": false,
    "target": "ES2015",
    "allowJs": false,
    "inlineSourceMap": true,
    "sourceRoot": "./",
    "outDir": "Scripts",
    "inlineSources": true,
    "lib": [ "es2015", "es2016", "dom", "es2018.promise" ]
  },
  "exclude": [
    "./Scripts/JS"
  ],
  "compileOnSave": true
}

It will exlude the files for compilation which will be palced inside Scripts/JS folder as mentioned in exclude section above. It also tells the compiler to copy the output js files in Scripts folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="Scripts/app.js"></script>

Sometimes you might also need to manually add a TypeScript compiler task to your website project. Edit [YourProjectName].csproj file, and add the following <Target> element before the </Project> closing tag:

<Target Name="TypeScriptCompile" BeforeTargets="Build">
  <Exec Command="tsc" />
</Target>

Save the changes, and reload the project.

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

Related Post(s):

June 18, 2023

Compile TypeScript code with ASP.NET Core

In this post I will explain the steps we need to setup TypeScript into an ASP.NET Core project.

Lets suppose we already have an ASP.Net Core Project.

We need to install Nuget Package Microsoft.TypeScript.MSBuild to build typescript code/files.

Create a new file named app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

We need to tell TypeScript by configuration settings to direct the behavior for compilation. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compileOnSave": true,
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "outDir": "wwwroot/ts_build"
  },
  "exclude": [
    "./node_modules",
    "./wwwroot",
  ],
  "include": [
    "./TypeScripts"
  ]
}

It will include any typescript file for compilation which will be palced inside TypeScripts folder as mentioned in include section above. It also tells the compiler to copy the output js files in wwwroot/ts_build folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="~/ts_build/app.js"></script>

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

References:

Related Post(s):

May 4, 2023

TypeScript - Promise.race()

The Promise.race() static method takes an iterable of promises as input and returns a single Promise. This returned promise settles with the eventual state of the first promise that settles (either fulfilled or rejected).

It's useful when you want the first async task to complete, but do not care about its eventual state (i.e. it can either succeed or fail).

Lets see an example:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, 'promise1 value');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'promise2 value');
});

Promise.race([promise1, promise2]).then((value) => {
     // promise2 is faster, so the 'value' will be the result of promise2
   console.log(value);
});

Output will be :

promise2 value

In this example, both promises will get resolved, but since the promise2 is faster (with less waiting time), the Promise.race() method will return promise2, it is the first promise in the input list which get settled (rejected or resolved).

If the iterable contains one or more non-promise values and/or an already settled promise, then Promise.race() will settle to the first of these values found in the iterable.

See this example:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, 'promise1 rejected');
});

const promise2 = new Promise((resolve, reject) => {
  reject('promise2 rejected');
});

Promise.race([promise1, promise2])
.then((value) => {
  console.log(value);
})
// promise2 is already settled (rejected), 
// so the 'error' will be the rejection-error of promise2
.catch((error) => { console.error(error);})
;

Output will be :

promise2 rejected

Since promise2 is already settled (rejected), Promise.race() method will return promise2.

References:

Related Post(s):

TypeScript - Promise.any()

The Promise.any() method is useful for returning the first promise that fulfills. It short-circuits after a promise fulfills, so it does not wait for the other promises to complete once it finds one.

This method returns the first fulfilled value and ignores all rejected promises up until the first promise that fulfills. This can be beneficial if we need only one promise to fulfill but we do not care which one does.

Lets see an example:

const p1 = Promise.reject("some error");
const p2 = Promise.resolve("resolve value1");
const p3 = Promise.resolve("resolve value2");

const promises = [p1, p2, p3];

(Promise as any).any(promises)
.then((value) => console.log(value))
.catch((error) => { console.error(error.message);});

Output will be :

resolve value1

In this example, the first promise p1 is rejected, but Promise.any method ignores this and continue to the next promise p2 which get resolved/fulfilled. Once it finds the first fulfilled promise it stops processing further promises.

If all of the input promises are rejected, then it rejects with an AggregateError containing an array of rejection reasons.

See this example:

const p1 = Promise.reject("some error1");
const p2 = Promise.reject("some error2");
const p3 = Promise.reject("some error3");

const promises = [p1, p2, p3];

(Promise as any).any(promises)
.then((value) => console.log(value))
.catch((error) => { console.error(error);});

Output will be :

[AggregateError: All promises were rejected] {
  [errors]: [ 'some error1', 'some error2', 'some error3' ]
}

When all of the input promises are rejected, it generates the AggregateError (array of the rejection error from each input promise).

References:

Related Post(s):

April 6, 2023

TypeScript - Promise.allSettled()

The Promise.allSettled() method is one of the promise concurrency methods. It takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises settle, with an array of objects that describe the outcome of each promise.

The result(object) returned by each input promise has the following properties:
  • status: A string, indicating the eventual state of the promise (either "fulfilled" or "rejected").
  • value: if status is "fulfilled". The result/value of the promise.
  • reason: if status is "rejected". The reason for promise rejection.

Promise.allSettled() is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, and we like to know the result of each promise.

Lets see an example:

const p1 = 10;
const p2 = Promise.resolve(20);
const p3 = Promise.reject("some error");
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => {
	resolve("value from promise4");
  }, 100);
});

Promise.allSettled([p1, p2, p3, p4])
.then((results) => {
  console.log(results);
});

Output will be :

[
  { status: 'fulfilled', value: 10 },
  { status: 'fulfilled', value: 20 },
  { status: 'rejected', reason: 'some error' },
  { status: 'fulfilled', value: 'value from promise4' }
]

Promise.allSettled() executes .then() for all the input promises regardless of the status of promise (either rejected or fulfilled). You can inspect the result object to find status, value and reason properties.

References:

Related Post(s):

TypeScript - Promise.all()

The Promise.all() method is one of the promise concurrency methods. It takes an interable of promises as input and returns a single promise object. It is useful for aggregating the result of multiple promises passed as input.

  • This returned promise fulfills when all of the input's promises fulfill/resolve.
  • It rejects immediately when any of the input's promises rejects.

It is typically used when there are multiple asynchronous tasks that we want to fulfill before the code execution continues.

In this example, since all the input promises are resolved, so the final promise will also get resolved.

const p1 = 10;
const p2 = Promise.resolve(20);
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
	resolve("value from promise3");
  }, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values);
});

Output will be :

[ 10, 20, 'value from promise3' ]

Note that, the values parameter in .then() function will be an array containing all the output values from each input promise.

Lets see an example, if one of the promise from input is rejected.

// pass 4 promises: (1,2,3 and rejected-promise)
const p = Promise.all([1, 2, 3, Promise.reject(new Error("some error"))]);

p.then((values) => {
  //this will not get called.
  console.log(values);
})
.catch((error) => {
  //catch here for rejected promise
  console.error(error.message);
});

Output will be :

some error

Promise.all() executes the success callback when all the input promises are resolved. If any of the promise is rejected, then the rejection callback will be executed.

References:

Related Post(s):

March 20, 2023

JavaScript - What is Promise

A promise object is an instance of the Promise class. It represents the eventual completion or failure of an asynchronous operation.

A Promise is kind of proxy for a value might be unknown when the promise is created. You can associate handlers with a promise to receive notification/result of success and failure of asynchronous operation performed by promise executor.

Promise executor is simply a function passed to the constructor of Promise class, it controls the behavior of promise's resolution (success )or rejection (failure).

Promise lets asynchronous methods return values like synchronous methods. Instead of immediately returning the final value, it returns a promise object as a mechanism to supply the value at some point in the future.

A Promise object can have one of these states:

  • pending: initial state (neither fulfilled nor rejected)
  • fulfilled: when the operation was completed successfully.
  • rejected: when the operation failed.

To create a promise, we use new Promise(executor) syntax and provide an executor function as an argument.

To consume a promise, we use .then() function to receive the result of promise.

The .then() method of the promise object takes up to two arguments:

  1. First argument is a callback function for the fulfilled case of the promise (usullay known as resolve)
  2. Second argument is a callback function for the rejected case (usullay known as reject).

An example:

let myPromise = new Promise(function(resolve, reject) {
    let status_code = 200;  //to call resolve/success
    //let status_code = 201; //to call reject/failure
  
    if (status_code == 200) {
      resolve("Status code is 200");
    } else {
      reject("Status code is not 200");
    }
  });
  
myPromise.then(
	function(value) {console.log("Success:", value);},
	function(error) {console.log("Error:", error);}
);

For this example, inside the Promise executor function, if the variable status_code value is 200 then it will call resolve handler of the promise, which ultimately fall in the fulfilled callback of .then() function.

If you change the status_code value to anything other than 200, then it will call reject handler of the promise, which ultimately fall in the rejected callback of .then() function.

References:

Related Post(s):

TypeScript - Promise only refers to a type, but is being used as a value

When using Promise in TypeScript code and transpiling, it generates the error:

'Promise' only refers to a type, but is being used as a value here. 
Do you need to change your target library? 
Try changing the 'lib' compiler option to es2015 or later

There could be following reasons/fixes for this issue:

  • Check in tsconfig.json file, if the target property (under compilerOptions) is set to es2015 or later (as suggested in the error message).
    {
        "compilerOptions": {
            "target": "es2015",
        }
    }
    
  • In tsconfig.json file, add lib property (under compilerOptions), and set it values to es2015 or later.
    {
        "compilerOptions": {
            "target": "es2015",
            "lib": ["dom", "es2015", "es5", "es6"],
        }
    }
    
  • A quick work around for this error is just removing the type check for Promise, rather than fixing it. Declar the Promise as a variable with type any.
    declare var Promise: any;
    
  • Try to install @types/node from npm;
    npm instal @types/node
    
  • Please be aware that if you are running the tsc command with a file name, then the compiler will ignore the tsconfig.json file. For example, transpiling the file like this:
    tsc myfile.ts
    
    You can edit the tsconfig.json to include a set of files with files property, e.g:
    {
        "compilerOptions": {
            "module": "commonjs",
            "noImplicitAny": true,
            "removeComments": true,
            "preserveConstEnums": true,
            "sourceMap": true,
            "target": "es2015",
            "lib": ["dom", "es2015", "es5", "es6"],
        },
        "files": [
            "myfile.ts",
            "service1.ts",
            "common.ts",
            "util.ts",
        ]
    }
    

February 23, 2023

TypeScript - Class Decorators

The class decorator takes the constructor function as a parameter, allows us to change the way how this class is initialized.

Let's say we have a couple of classes Customer and Order. It is required that every class needs to have created property.

The normal solution is to create a base class which will have common fields and allow the childern to inherit from this.

In this example, we will use decorator to achieve this behavior, the decorator function will receive the target's constructor function as a parameter, and add the created property to its prototype.

Here is the code for decorator:

function EntityWithTimeStamp(constructorFunction: Function) {
    constructorFunction.prototype.created = new Date().toLocaleString("en-US");
    console.log("decorator called");
}

It receives constructorFunction(of the target class) as parameter, and adds created property to its prototype.

The decorator is ready to be used in each entity. We only need to add @EntityWithTimeStamp before class definition.

Here we define our classes and adds @EntityWithTimeStamp decorator.

@EntityWithTimeStamp
class Customer {
  constructor(public name: string) {
    console.log("Customer controctor called");
  }
}

@EntityWithTimeStamp
class Order {
  constructor(public amount: number) {
    console.log("Order  controctor called");
  }
}

Both of these classes have defined their own public properties received in constructor. Also they will have an additional property created which will be added by the decorator.

You can create objects of these classes as usual, and output the public properties to console:

let customer = new Customer("Idrees");
let order  = new Order(100);

console.log(order.amount);
console.log(customer.name);

Note that the decorator does not change the TypeScript type and the new property created is not known to the type system. The following lines will give you the error because compiler will not find created property in these referenced classes/types:

console.log(order.created);
console.log(customer.created);

Error message:

error TS2339: Property 'created' does not exist on type 'Customer'.
error TS2339: Property 'created' does not exist on type 'Order'.

There is a work around this issue, you can access the created property by temporarily casting the object to type any.

console.log((<any>order).created);
console.log((<any>customer).created);

Output will display the value of crearted property, which shows that decorator has added this field as the class member.

Related Post(s):

February 22, 2023

Decorators in TypeScript

Decorators provide a mechanism to metaprogramming syntax in TypeScript, which is a programming technique basically means "writing code that writes code".

Decorators allow us to decorate members of a class, or a class itself, with extended functionality. This is a function that we can hook into our code, to extend with some behavior and helps us to write code abstractions and provide extension mechanism.

When you apply a decorator to a class or a class member, it will actually call a function that is going to receive details of target (what is being decorated), and the decorator implementation will then be able to transform the code dynamically (e.g. adding extra functionality, and reducing boilerplate code).

The decorators are used on class itself and its members:

  • class definition
  • properties
  • methods
  • accessors
  • parameters

Note that:

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript.

Before using decorators in TypeScript, we need to enable it. If the decorator support is not enabled and you try to compile a TypeScript file/project that is using decorator, it will give you this error:

 error TS1219: Experimental support for decorators is a feature that is subject to change 
 in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig'
 to remove this warning.

We have two ways to enable decorators support in TypeScript:

  • You can enable decorators support at compile time. When using the TypeScript Compiler CLI (tsc), we need to provide additional flag --experimentalDecorators:
    tsc --experimentalDecorators
    
  • When working in a project that has a tsconfig.json file, to enable decorators support you can add the experimentalDecorators property (with value true) to the compilerOptions object:
    {
      "compilerOptions": {
        "experimentalDecorators": true
      }
    }
    

Related Post(s):

January 24, 2023

TypeScript - Function as a parameter

Sometimes we need to pass a function as a parameter to another function, which will internally call the parameter function.

Lets see an example how we can receive a function as a parameter.

We have to define the parameter type as Function.

function shipOrder(func: Function): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Lets say we have a function getBillingAddress as follows:

getBillingAddress() {
   //some logic
   console.log("getBillingAddress function is called");
}

shipOrder is the function which will accept another function as a parameter, and then internally call this function. Since getBillingAddress accepts not parameter and returns void, we can simply invoke the function by its name alongwith paranthesis.

This is how we will call the shipOrder function by passing the function name as parameter.

shipOrder(getBillingAddress);

We can make it easier to read, define an interface describing the function signature that need to pass as paramter:

interface IFunction {
(): void;
}

The shipOrder function will become like this:

function shipOrder(func: IFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Since IFunction interface contains a function, with not paramters and void return type. Passing our old getBillingAddress function is still valid because of same function signature.

shipOrder(getBillingAddress);

We can also specify the paramters and return type for the function. Lets define a new interface:

interface INumberFunction {
(num: number): string;
}

Change the paramter type to INumberFunction

function shipOrder(func: INumberFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   //now the func is of type INumberFunction, we need to pass a number paramter and it will return a string.
   let someValue: string = func(1);
}

Calling the function and passing parameter is same.

shipOrder(getBillingAddress);

But when you need to invoke the actual function (which is passed a paramter), you have to take care of its signature, the required paramters and return type.

   let someValue: string = func(1);

December 20, 2022

What is the purpose of using tsconfig.json file ?

tsconfig.json file allows you to point the root level files and different compiler options to setup that require to compile a TypeScript based projects. The existence of this file in a project specifies that the given directory is the TypeScript project folder root.

Here is an example tsconfig.json file.

{
"compileOnSave": true,
"compilerOptions": {
        "target": "es5",
		"module": "system",
		"moduleResolution": "node",
		"noImplicitAny": true,
		"sourceMap": true,
        "removeComments": false
},
	"files": [
		"program.ts",
		"sys.ts"
	],
	"include": [
		"src/**/*"
	],
	"exclude": [
		"node_modules",
		"src/**/*.spec.ts"
	]
}

Lets understand what each option specifies:

  • compileOnSave: If sets true, it instructs the IDE to automatically compile the given TypeScript files and generate the output.
  • compilerOptions: It allows specifying additional options to the TypeScript compiler:
    • target: the language used for the compiled output, e.g. es5, es6.
    • module: the module manager used in the compiled output. system is for SystemJS, commonjs for CommonJS.
    • moduleResolution: the strategy used to resolve module declaration files (.d.ts files). With the node approach, they are loaded from the node_modules folder like a module (require('module-name'))
    • noImplicitAny: if sets false, Raise error on expressions and declarations with an implied any type.
    • sourceMap: if sets true, it will generate source map files to debug directly your application TypeScript files in the browser,
    • removeComments: if set, Remove all comments except copy-right header comments beginning with /*!
  • files: Gives a list of TypeScript files that will be included by the compiler. The URL of the files can be both relative or absolute.
  • include: Allows you to include a list of TypeScript files using the glob wildcards pattern.
  • exclude: Allows you to exclude a list of TypeScript files using the glob wildcards pattern.

Install TypeScript compiler in VS Code

If you need to run the typescript from VS Code terminal you need to install the typescript compiler either globally or in your workspace.

This typescript compiler will transpile the TypeScript code to JavaScript.

Following are the steps we need to install and compile TypeScript in VS Code.

  • Install node.js Package Manager (npm).
  • Install TypeScript globally (-g) by this command:
    npm install -g typescript
    
  • If want to install TypeScript locally in a single project, you can use this command:
    npm install --save-dev typescript
    
  • Test your install by checking the version.
    tsc --version
    

TypeScript HelloWorld:

Let's start with a simple Hello World example. Create a new folder HelloWorld and open in VS Code.

mkdir HelloWorld
cd HelloWorld
code .

Create a new file called helloworld.ts.

Add the following TypeScript code in helloworld.ts file.

let message: string = 'Hello World';
console.log(message);

To compile the TypeScript code, you can open the Integrated Terminal (Ctrl+`)

Run this command to compile helloworld.ts

tsc helloworld.ts

It will create a new helloworld.js JavaScript file in the same folder.

To run the javascript file, you can use this command:

node helloworld.js

It will run the javascript code and display the script output in console.

July 25, 2022

TypeScript: How to Pass a Function as a Parameter

In JavaScript you can pass the callback functions as parameters when triggering other functions. In this post, we will see an example how to do the same in TypeScript.

Let see the following code:

function f1(callback) {
  console.log('f1() function called!');
  callback();
}

function f2() {
  console.log('f2() function called!');
}

f1(f2);

In this exmaple, we have two functions f1() and f2(). We are passing f2() function as a callback paramter to function f1(), which will internally call the function f2().

It seems all ok in Javascript. However we are not leveraging the power of TypeScript.

If we pass anything (other than function, e.g. number) as a parameter to f1(), it will be compiled successfully but gives the runtime error. To avoid this issue, we can define the type of the callback parameter as Function.

So the f1() function will become:

function f1(callback: Function) {
  console.log('f1() function called!');
  callback();
}

Here f1() is enforcing the paramter should be of type Function. If you will pass anything other than the Function type, you will get compile error.

If you want to also use the return type of the callback function (lets say the return type will be number), then this solution will not work. Because the Function type could not enforce TypeScript to pass a function with desired return type.

For this, you can use an arrow function expression that returns a type to provide a valid type definition.

Let’s modify the definition of the f1() function.

function f1(callback: () => number) {
  console.log('f1() function called!');
  const number = callback();
  console.log('callback() function returns value: ' + number);
}

This helps the code to be more predictable with TypeScript, and prevent the unexpected runtime behavior often caused by lack of type definition.

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)

March 21, 2022

Using JavaScript Object.assign() method

In this post, we will see how to use the JavaScript Object.assign() method to copy and merge objects.

The syntax of the Object.assign() method is:

Object.assign(target, ...sources)

The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.

The Object.assign() invokes the getters on the source objects and setters on the target.

Clone an object

The following example uses the Object.assign() method to clone an object.

let book = {
    title: 'My Title'
};

let clonedBook = Object.assign({}, book);

console.log(clonedBook);
Output will contain the properties copied from source object.
{ title: 'My Title' }

Note that the Object.assign() only creates a shallow clone, not a deep clone.

Merge objects

The Object.assign() can merge two or more source objects into a target object which will contain properties consisting of all the properties of the source objects. For example:

let book = {
    title: 'My Title'
};

let bookTemplate = {
    noOfPage: 200,
    author: 'Author'
};

let bookPublishTemplate = {
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
};

let newBook = Object.assign({}, book, bookTemplate, bookPublishTemplate);

console.log(newBook);
Output will contain the properties merged from all the source objects.
{
    title: 'My Title',
    noOfPage: 200,
    author: 'Author'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
}

If two or more source objects have the property with same name, the property of the later object overwrites the earlier one:

let book = {
    title: 'My Title'
};

let bookTemplate = {
    title: 'My Title from bookTemplate'
    noOfPage: 200,
    author: 'Author'
};

let bookPublishTemplate = {
    author: 'Author from bookPublishTemplate'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
};

let newBook = Object.assign({}, book, bookTemplate, bookPublishTemplate);

console.log(newBook);
Output will contain the properties merged from all the source objects, also the duplicate properties will be overwritten by the later source objects.
{
    title: 'My Title from bookTemplate'
    noOfPage: 200,
    author: 'Author from bookPublishTemplate'
    publisher: 'My Publisher',
    publishDate: '21-Mar-2022'
}

February 24, 2022

Angular - Receive event notification from child component

Angular enables the components based development which helps you create small cohesive components to design the application in a manageable approach. This may also lead to a complex heirarchy among the components. Then you need a mechanism to communicate between these components which may be in two directions top-down and bottom-up.

In this post we will see an example of bottom-up approach to allow communication from the child component to the parent component.

Lets say we have two components, ParentComponent and ChildComponent, with structure like this.

<parent-component>
    <child-component />
</parent-component>

The child-component has a button called btnSearch, which needs to invoke the parent-component's function searchCalledByChild();

Lets walk through this example to see how we can achieve this behavior.

This is the child component's ts file:

// child.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-child-component',
  templateUrl: './child-component.component.html',
  styleUrls: ['./child-component.component.css']
})
export class ChildComponentComponent implements OnInit {
  count: number = 0;
  child_msg : string = "";

  constructor() { }

  ngOnInit(): void {
  }
  
  btnSearchClicked() {
    this.child_msg = "Clicked Counter: " + this.count++;
  }
}

This will display a string child_msg in html template, showing the counter for button clicks.

This is the parent component's ts file:

// parent.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent-component',
  templateUrl: './parent-component.component.html',
  styleUrls: ['./parent-component.component.css']
})
export class ParentComponentComponent implements OnInit {
	
  parent_msg : string = "";
  chil_msg_inside_parent : string = "";

 constructor() { }

  ngOnInit(): void {
  }
	
  //method in parent class, this needs to be invoked 
  //when button is clicked in child component
  searchCalledByChild(child_msg_received: string)
  {	
    this.parent_msg = "Message from parent component";
		
    //child_msg_received is the data passed from child component.
    this.chil_msg_inside_parent = child_msg_received;
  }
}

To enable the parent component to receive notification of child-component's event, we have to makes these changes in child-component's ts file:

First import Output and EventEmitter from '@angular/core'

import { Output, EventEmitter } from '@angular/core';

Decorate a property with @Output(). Here searchButtonClickedEvent is the name of the property declared as EventEmitter, which means it's an event.

@Output() searchButtonClickedEvent = new EventEmitter<string>();

The type parameter we passed to EventEmitter<> tells angular that this event will emit the string data.

Raise this event from child-component's local method btnSearchClicked() using the emit() function.

btnSearchClicked() 
{
   this.child_msg = "Clicked Counter: " + this.count++;

   this.searchButtonClickedEvent.emit(this.child_msg);
}

Here is the html template for child-component:

<div style="background-color:grey;margin:10px;padding:10px;">
   <button id="btnSearch" (click)="btnSearchClicked()">Click child</button>
   <p>Child Content: {child_msg}}</p>
</div>

Now the child component is ready to emit events whenever the btnSearchClicked() function is called. In this example we are calling this function from child-component's button click.

To enable the parent-component to receive this event, we will bind the parent' local method to the child's event. To bind the event we will use the same event property of EventEmitter we have defined in the child-component.

<div style="background-color:tan;margin:10px; padding:10px;">
   
   <app-child-component 
         (searchButtonClickedEvent)="searchCalledByChild($event)">
   </app-child-component>
   
   <p>{{parent_msg}}</p>
   <p>Parent Content: {{chil_msg_inside_parent}}</p>
   
</div>

This event binding, (searchButtonClickedEvent)="searchCalledByChild($event), connects the event in the child (searchButtonClickedEvent) to the function in the parent(searchCalledByChild).

The $event parameter contains the data that we have passed from the child-compoment in the emit() function.

The complete listing of the parent-component's ts file will be look like this:

// parent.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent-component',
  templateUrl: './parent-component.component.html',
  styleUrls: ['./parent-component.component.css']
})
export class ParentComponentComponent implements OnInit {
	
  parent_msg : string = "";
  chil_msg_inside_parent : string = "";

 constructor() { }

  ngOnInit(): void {
  }
	
  //method in parent class, this needs to be invoked 
  //when button is clicked in child component
  searchCalledByChild(child_msg_received: string)
  {	
    this.parent_msg = "Message from parent component";
		
    //child_msg_received is the data passed from child component.
    this.chil_msg_inside_parent = child_msg_received;
  }
}

References:

Related Post(s):