Sunday, March 29, 2020

Build an Angular 8 App with REST API and ASP.NET Core 2.2

 Create the Angular 8 app

Now, let’s finally start with the Angular app. We will use Node.js and Angular CLI to generate the Angular project and necessary files.
Remember. Github repo is here: Angular 8 blog app tutorial using .NET Core 2.2 and Entity Framework back-end

Prerequisites

When Node.js is installed, you can open the Node.js command prompt.
Execute this command in the command prompt to install the Angular 8 CLI:
npm install -g @angular/cli
This will install the latest Angular CLI globally and it’ll take a little while. When you’re done, you can check the Angular version with this command:
ng --version
The Node.js command prompt should look something like this:
Node.js command prompt and Angular CLI
Now, let's move to the folder where our Visual Studio back-end is located. Use the cd command to do this:
cd c:/projects/blog
We will simply call our Angular 8 application ClientApp. Let’s execute the command that creates our Angular application:
ng new ClientApp
We’ll be prompted with some questions. We want to use routing (press Y) and stylesheet format: SCSS. Then let Node do its thing and create the web app. It’ll take a minute or so.
When the app is created, cd command into the app folder:
cd ClientApp
And then build and run the app with the ng serve command:
ng serve
The command prompt will look like this:
Node.js Angular app compilation
The build succeded and now you can browse to your Angular app with the URL http://localhost:4200. The basic Angular 8 app is based on a template and it’ll look something like this:
Angular template app in the browser
If you have a look at the source code, it’ll look like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ClientApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" type="module"></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body>
</html>
view rawindex.html hosted with ❤ by GitHub
The interesting thing here is <app-root></app-root>, which is Angular specific and tells us where our Angular app will execute.
One final command is good to know — it’s Ctrl+C to close the Angular application, and you should press it twice to terminate the batch job and stop ng serve.
One of the nice features of Angular development is changes you save in front-end files will immediately be reflected in the browser. For this to happen the app needs to be running.
For some changes though, like adding Bootstrap, you need to restart the application to make it work.

Angular 8 fundamentals

Let’s pause, take a step back and learn some of the Angular 8 fundamentals.
Angular 8 is an open source client-side JavaScript framework, based on TypeScript which is compiled to JavaScript.
The Angular 8 architecture consists of the following:
  • Modules
  • Components
  • Templates, Directives, Data-Binding
  • Services and dependency injection
  • Routing
You can delve deeper into the Angular architecture here in the official documentation. Here’s a quick rundown though:

Modules

Angular NgModules are fundamental to any Angular application. Every Angular app has a root module called AppModule, which bootstraps and launches the app. Modules can call components and services. The default module is app.module.ts.

Components

Components provide us with a class and a view, and are parts of the application. The class is TypeScript based and the view is HTML. All Angular app has at least one component called app.component.ts.

Templates, directives, data-binding

A template combines HTML with Angular markup. The directives provide logic and the binding markup connects the application data with the DOM.

Services and dependency injection

Service classes provide application logic that isn’t tied to a specific view and shared across the app. They are injectable using the @Injectable() decorator. Component classes are kept nice and tidy using dependency injection.

Routing

The Router NgModule provides a service that defines navigation in the app. It works the same way as a browser’s navigation.

Visual Studio 2019 for back-end, VS Code for front-end

While Visual Studio 2019 works very well for back-end as well as front-end, VS Code is actually better for front-end heavy work with frameworks like Angular. I recommend you try VS Code and most instructions for the Angular application in this tutorial, will be for VS Code.
To make front-end and Angular development easier in VS Code, install these extensions. You can do it easiest through the VS Code Extensions module.
There’s obviously a lot more awesome extensions like Beautify and Path Intellisense that makes your development more productive. It’s all up to your preference and style.
In VS Code, make sure you open the folder ClientApp on your disk and work from there.

Add components and services to our Angular app

Let’s continue building the Angular app. First, press Ctrl+C twice in the Node.js command prompt, if you haven’t closed the connection to your app.
Next, let’s add Bootstrap 4 to the application. Execute this command in the Node.js command prompt:
npm install bootstrap --save
Then find the angular.json file and edit the build node to make styles look like this:
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
]
view rawangular.json hosted with ❤ by GitHub
The angular.json build node should look like this:
Angular.json build node
Next up, let’s create our components. We will have three components for our blog application:
  • BlogPosts — shows all blog posts.
  • BlogPost — show a specific blog post.
  • BlogPostAddEdit — add new or edit existing blog post.
To create these components, execute the following commands in the Node.js command prompt:
ng generate component BlogPosts
ng generate component BlogPost
ng generate component BlogPost-AddEdit
Under ClientApp/src/app, the components are now there:
Components (blog-posts folder) in VS Code
As you can see, we have a .html file, scss file, spec.ts file and component.ts file for each component.
  • HTML and SCSS are used for the view.
  • spec.ts is for tests.
  • component.ts contains our component class and logic.
While we’re at it, let’s create our service as well, using the command prompt:
ng generate service BlogPost
Create a new folder under app and call it services. Move the two generated service files to the folder:
Services folder in VS Code
Now let’s leave our components and services and have a look at the app.module.ts file. This is where we import modules and components, declare them and also add providers.
We get a few things for free from the created app. Necessary imports are added and a few modules too. When we add components in the Node.js command prompt, the app.modules.ts file is updated as well. However we don’t get help with everything. For our blog app, we need to manually import some modules on our own and add them. We also need to import and add our service to providers.
Let’s update the file to look like this:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BlogPostsComponent } from './blog-posts/blog-posts.component';
import { BlogPostComponent } from './blog-post/blog-post.component';
import { BlogPostAddEditComponent } from './blog-post-add-edit/blog-post-add-edit.component';
import { BlogPostService } from './services/blog-post.service';
@NgModule({
declarations: [
AppComponent,
BlogPostsComponent,
BlogPostComponent,
BlogPostAddEditComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
ReactiveFormsModule
],
providers: [
BlogPostService
],
bootstrap: [AppComponent]
})
export class AppModule { }
view rawapp.module.ts hosted with ❤ by GitHub
Necessary modules like HttpClientModule and ReactiveFormsModule are imported. AppRoutingModule and AppComponent were already created for us from the beginning.
Just make sure to declare components, add modules to imports and also add our service to providers.
Just one thing on import and export.
TypeScript uses the module concept from EcmaScript 2015. Modules are executed in their own scope and not in the global scope. To make one module’s classes, variables, functions etc visible to other modules, export is used. Also to use some of these from another module, an import is needed.

Setup routing

Open app-routing.module.ts. Here you have the routes setup with no routes configured:
const routes: Routes = [];
Update the file to look like this instead:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BlogPostsComponent } from './blog-posts/blog-posts.component';
import { BlogPostComponent } from './blog-post/blog-post.component';
import { BlogPostAddEditComponent } from './blog-post-add-edit/blog-post-add-edit.component';
const routes: Routes = [
{ path: '', component: BlogPostsComponent, pathMatch: 'full' },
{ path: 'blogpost/:id', component: BlogPostComponent },
{ path: 'add', component: BlogPostAddEditComponent },
{ path: 'blogpost/edit/:id', component: BlogPostAddEditComponent },
{ path: '**', redirectTo: '/' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
We import necessary component and update Routes with paths and tell what components will be loaded in those paths.
{ path: '', component: BlogPostsComponent, pathMatch: 'full' }
This tells us we will load the BlogPostsComponent on the app start page.
{ path: '**', redirectTo: '/' }
This tells us all invalid paths for the application will be redirected to the start page.
Open app.component.html and update the file to look like this:
<div class="container">
<a [routerLink]="['/']" class="btn btn-info">Start</a>
<router-outlet></router-outlet>
</div>
view rawapp.component.html hosted with ❤ by GitHub
The <router-outlet></router-outlet> element will be replaced by the correct component and this file will be used for all components in the app.
Now let’s build and run the app again using the ng serve command in the Node.js command prompt. When Node is done compiling, go to http://localhost:4200. The start page will now look like this:
Basic Angular app start page - blog posts listing
This is our BlogPostsComponent in action. Try browsing to http://localhost:4200/add as well and you’ll get the view for our BlogPostAddEditComponent.
If you try to browse to a path that doesn’t exist, you’re redirected to the start page again.

Different ways to build and run the application

We have two different ways that we can use to build and run our Angular application:
  • Node.js command prompt and ng serve.
  • Visual Studio F5 command and IIS Express.
This is good to know. The simplest thing to do is to just use Visual Studio to build and run our Angular app as well as the back-end. To make the Angular app work, we need to edit Startup.cs to allow SPA static files.
In Startup.cs, we already have commented out configuration for SPA. In the ConfigureServices method, uncomment the services.AddSpaStaticFiles section:
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
view rawStartup.cs hosted with ❤ by GitHub
In the Configure method, uncomment the app.UseSpaStaticFiles() line and app.UseSpa() section. Since before, we already have app.UseMvc():
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
view rawStartup.cs hosted with ❤ by GitHub
Also, let’s update environment.ts. Add appUrl to the environment constant, it should look like this:
export const environment = {
production: false,
appUrl: 'https://localhost:44382/'
};
view rawenvironment.ts hosted with ❤ by GitHub
Now in Visual Studio 2019, press F5 and your Angular app AND back-end will be up and running on the same address, on IIS Express:
IIS Express browser url
Whether you want to use the Node.js command prompt to build and run the Angular application is up to you. Just remember the back-end needs to be up and running too.
Visual Studio building and running both the front-end and back-end means one less thing to think about.

Create blog post model and service methods

We need a blog post model to work with in TypeScript. Let’s create a new folder called models and then a TypeScript file(right click the folder -> New file in VS Code) and name it blogpost.ts.
Copy and paste this BlogPost model class into blogposts.ts:
export class BlogPost {
postId?: number;
creator: string;
title: string;
body: string;
dt: Date;
}
view rawblogpost.ts hosted with ❤ by GitHub
Our BlogPost model will now be available across the application.

Angular 8 service CRUD tasks

Our Angular service will call our back-end and carry out these tasks:
  • Create blog post.
  • Show all blog posts / show a single blog post.
  • Update an existing blog post.
  • Delete a blog post.
Now let’s go back to our previously created service, located in the services folder. Open blog-post.service.ts and edit the file to look like this:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
import { BlogPost } from '../models/blogpost';
@Injectable({
providedIn: 'root'
})
export class BlogPostService {
myAppUrl: string;
myApiUrl: string;
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json; charset=utf-8'
})
};
constructor(private http: HttpClient) {
this.myAppUrl = environment.appUrl;
this.myApiUrl = 'api/BlogPosts/';
}
getBlogPosts(): Observable<BlogPost[]> {
return this.http.get<BlogPost[]>(this.myAppUrl + this.myApiUrl)
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
getBlogPost(postId: number): Observable<BlogPost> {
return this.http.get<BlogPost>(this.myAppUrl + this.myApiUrl + postId)
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
saveBlogPost(blogPost): Observable<BlogPost> {
return this.http.post<BlogPost>(this.myAppUrl + this.myApiUrl, JSON.stringify(blogPost), this.httpOptions)
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
updateBlogPost(postId: number, blogPost): Observable<BlogPost> {
return this.http.put<BlogPost>(this.myAppUrl + this.myApiUrl + postId, JSON.stringify(blogPost), this.httpOptions)
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
deleteBlogPost(postId: number): Observable<BlogPost> {
return this.http.delete<BlogPost>(this.myAppUrl + this.myApiUrl + postId)
.pipe(
retry(1),
catchError(this.errorHandler)
);
}
errorHandler(error) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}
We already injected the service into the providers array in app.module.ts, meaning the service can be used right away across the application.

Observables in Angular

The Angular HttpClient methods use RxJS observables. Observables provide support for passing messages between publishers and subscribers in your application. They are powerful and have several advantages and are therefore used extensively in Angular.
Observables in the Angular documentation
When we’ve created (published) an observable, we need to use the subscribe() method to receive notifications. We then get a Subscription object we can work with. Also, we can use unsubscribe() to stop receiving notifications.
We make our BlogPostService injectable via the @Injectable() decorator. We will inject the service into our components later.
For our service’s post and put methods, we will send application/json.
Then we use the pipe() method for each service call. Here we can pass in operator functions for data transformation in our observable collection. We add retry and catchError to our pipe method.
It’s very common to subscribe to observables in Angular. This is fine, but you have to remember to unsubscribe too. pipe does that automatically for you, freeing up memory resources and preventing leaks.

Update components to show service data

Over to our three blog components. Let’s start with BlogPostsComponent which will list all our blog posts. Update the file blog-posts.component.ts to look like this:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BlogPostService } from '../services/blog-post.service';
import { BlogPost } from '../models/blogpost';
@Component({
selector: 'app-blog-posts',
templateUrl: './blog-posts.component.html',
styleUrls: ['./blog-posts.component.scss']
})
export class BlogPostsComponent implements OnInit {
blogPosts$: Observable<BlogPost[]>;
constructor(private blogPostService: BlogPostService) {
}
ngOnInit() {
this.loadBlogPosts();
}
loadBlogPosts() {
this.blogPosts$ = this.blogPostService.getBlogPosts();
}
delete(postId) {
const ans = confirm('Do you want to delete blog post with id: ' + postId);
if (ans) {
this.blogPostService.deleteBlogPost(postId).subscribe((data) => {
this.loadBlogPosts();
});
}
}
}
We dependency inject BlogPostService in the constructor and in loadBlogPosts() we simply call our Angular service.
Since the service getBlogPosts() method gives us an Observable<BlogPost[]> back, we assign it to this component’s blogPost$ object. It’s common practice to name observable objects with a $ sign at the end.
In the delete() method we need to subscribe to our observable instead to execute the action and then reload the blog post list.
Now open blog-posts.component.html and update it to look like this:
<h1>Blog posts</h1>
<p *ngIf="!(blogPosts$ | async)"><em>Loading...</em></p>
<p>
<a [routerLink]="['/add']" class="btn btn-primary float-right mb-3">New post</a>
</p>
<table class="table table-sm table-hover" *ngIf="(blogPosts$ | async)?.length>0">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Creator</th>
<th>Date</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let blogPost of (blogPosts$ | async)">
<td>{{ blogPost.postId }}</td>
<td><a [routerLink]="['/blogpost/', blogPost.postId]">{{ blogPost.title }}</a></td>
<td>{{ blogPost.creator }}</td>
<td>{{ blogPost.dt | date: "dd.MM.y" }}</td>
<td><a [routerLink]="['/blogpost/edit/', blogPost.postId]" class="btn btn-primary btn-sm float-right">Edit</a></td>
<td><a [routerLink]="" (click)="delete(blogPost.postId)" class="btn btn-danger btn-sm float-right">Delete</a></td>
</tr>
</tbody>
</table>
We use the AsyncPipe to subscribe to our observables. When we want to display our observable value in our HTML template file we use this syntax:
(blogPosts$ | async)
ngIf and ngFor are structural directives which change the DOM structure by adding or removing elements.
The routerLink directive lets us link to specific routes in our app.
You can press F5 in Visual Studio 2019 or use the Node.js command prompt and ng serve to launch the app. If you use Node.js to launch the app, make sure the back-end is launched as well in the background (using Visual Studio F5 command).
Since we’ve manually added a blog post in Postman before, we should now see this:
Blog posts list view
Excellent!
Next up is blog-post.component.ts to view a single blog post. Edit the file to look like this:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { BlogPostService } from '../services/blog-post.service';
import { BlogPost } from '../models/blogpost';
@Component({
selector: 'app-blog-post',
templateUrl: './blog-post.component.html',
styleUrls: ['./blog-post.component.scss']
})
export class BlogPostComponent implements OnInit {
blogPost$: Observable<BlogPost>;
postId: number;
constructor(private blogPostService: BlogPostService, private avRoute: ActivatedRoute) {
const idParam = 'id';
if (this.avRoute.snapshot.params[idParam]) {
this.postId = this.avRoute.snapshot.params[idParam];
}
}
ngOnInit() {
this.loadBlogPost();
}
loadBlogPost() {
this.blogPost$ = this.blogPostService.getBlogPost(this.postId);
}
}
As it’s a single blog post we want to show, we fetch the id key from the url querystring with the built in ActivatedRoute component, and pass it to the service getBlogPost() method.
Now open blog-post.component.html and edit it to look like this:
<ng-container *ngIf="(blogPost$ | async) as blogPost; else loading">
<h1>{{ blogPost.title }}</h1>
<div>{{ blogPost.body }}</div>
<ul>
<li>{{ blogPost.creator }}</li>
<li>{{ blogPost.dt }}</li>
</ul>
</ng-container>
<ng-template #loading>Loading…</ng-template>
We use the AsyncPipe again and also use the alias blogPost so we don’t have to write blogPost | async everywhere we want to access a blogPost property. We also provide a loading screen.
We’re getting closer. Now we just need a way to create new blog posts and edit existing ones. Open blog-post-add-edit.component.ts and edit it to look like this:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { BlogPostService } from '../services/blog-post.service';
import { BlogPost } from '../models/blogpost';
@Component({
selector: 'app-blog-post-add-edit',
templateUrl: './blog-post-add-edit.component.html',
styleUrls: ['./blog-post-add-edit.component.scss']
})
export class BlogPostAddEditComponent implements OnInit {
form: FormGroup;
actionType: string;
formTitle: string;
formBody: string;
postId: number;
errorMessage: any;
existingBlogPost: BlogPost;
constructor(private blogPostService: BlogPostService, private formBuilder: FormBuilder, private avRoute: ActivatedRoute, private router: Router) {
const idParam = 'id';
this.actionType = 'Add';
this.formTitle = 'title';
this.formBody = 'body';
if (this.avRoute.snapshot.params[idParam]) {
this.postId = this.avRoute.snapshot.params[idParam];
}
this.form = this.formBuilder.group(
{
postId: 0,
title: ['', [Validators.required]],
body: ['', [Validators.required]],
}
)
}
ngOnInit() {
if (this.postId > 0) {
this.actionType = 'Edit';
this.blogPostService.getBlogPost(this.postId)
.subscribe(data => (
this.existingBlogPost = data,
this.form.controls[this.formTitle].setValue(data.title),
this.form.controls[this.formBody].setValue(data.body)
));
}
}
save() {
if (!this.form.valid) {
return;
}
if (this.actionType === 'Add') {
let blogPost: BlogPost = {
dt: new Date(),
creator: 'Martin',
title: this.form.get(this.formTitle).value,
body: this.form.get(this.formBody).value
};
this.blogPostService.saveBlogPost(blogPost)
.subscribe((data) => {
this.router.navigate(['/blogpost', data.postId]);
});
}
if (this.actionType === 'Edit') {
let blogPost: BlogPost = {
postId: this.existingBlogPost.postId,
dt: this.existingBlogPost.dt,
creator: this.existingBlogPost.creator,
title: this.form.get(this.formTitle).value,
body: this.form.get(this.formBody).value
};
this.blogPostService.updateBlogPost(blogPost.postId, blogPost)
.subscribe((data) => {
this.router.navigate([this.router.url]);
});
}
}
cancel() {
this.router.navigate(['/']);
}
get title() { return this.form.get(this.formTitle); }
get body() { return this.form.get(this.formBody); }
}
Here we’re introducing Angular forms: FormBuilder, FormGroup and also Validators.
Depending on if we’re creating a new blog post or editing an existing one, we use actionType to show the correct form view with or without data. When we save or update a blog post, we create a new BlogPost object which we then fill with correct form data and then post to our service.
Let’s open the blog-post-add-edit.component.html and edit it to look like this:
<h1>{{actionType}} blog post</h1>
<form [formGroup]="form" (ngSubmit)="save()" #formDir="ngForm" novalidate>
<div class="form-group row">
<label class=" control-label col-md-12">Title</label>
<div class="col-md-12">
<input class="form-control" type="text" formControlName="title">
</div>
<span class="text-danger ml-3" *ngIf="title.invalid && formDir.submitted">
Title is required.
</span>
</div>
<div class="form-group row">
<label class="control-label col-md-12" for="Body">Body text</label>
<div class="col-md-12">
<textarea class="form-control" rows="15" formControlName="body"></textarea>
</div>
<span class="text-danger ml-3" *ngIf="body.invalid && formDir.submitted">
Body is required.
</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success float-right">Save</button>
<button class="btn btn-secondary float-left" (click)="cancel()">Cancel</button>
</div>
</form>
Here’s the form with validation.
We’re done!
Press F5 in Visual Studio 2019 or use the Node.js command prompt and ng serve to browse our final app. (If you use Node.js to launch the app, make sure the back-end is launched as well in the background (using Visual Studio F5 command))
Updated blog posts list view
Edit blog post view
Single blog post view

Saturday, March 28, 2020

Creating a TypeScript Workflow with Gulp


TypeScript provides a lot of great functionality that lets you leverage many of the features available in ES6 today but how do you get started using it in your favorite editor? If you’re using Visual Studio or WebStorm then TypeScript support can be used directly and everything happens magically without much work on your part. But, if you’re using Sublime Text, Brackets, Atom, or another editor you’ll have to find a plugin to compile .ts files to JavaScript or create your own custom workflow.

While several plugins exist to compile TypeScript and even provide code help as you’re writing TypeScript code in different editors, I generally prefer to use my own custom workflow. There are multiple benefits associated with going with this approach including the ability to standardize and share tasks across team members as well as being able to tie the workflow into a custom build process used in continuous integration scenarios. In this post I’ll walk through the process of creating a custom TypeScript workflow using Gulp (a JavaScript task manager). It’s a workflow setup that my friend Andrew Connell and I created when we recently converted anapplication to TypeScript. Throughout the post you’ll learn how to setup a file named gulpfile.js to compile TypeScript to JavaScript and also see how you can “lint” your TypeScript code to make sure it’s as clean and tidy as possible.

Getting Started Creating a Custom TypeScript Workflow

I talked about using Gulp to automate the process of transpiling ES6 to ES5 in a previous post. The general process shown there is going to be used here as well although I’ll be providing additional details related to TypeScript. If you’re new to Gulp, it’s a JavaScript task manager that can be used to compile .ts files to .js files, lint your TypeScript, minify and concatenate scripts, and much more. You can find additional details athttp://gulpjs.com.
Here’s a step-by-step walk-through that shows how to get started creating a TypeScript workflow with Gulp. Although there are several steps to perform, it’s a one-time setup that can be re-used across projects. If you’d prefer to use a starter project rather than walking through the steps that are provided in this post then see the project at https://github.com/DanWahlin/AngularIn20TypeScript or download the project associated with the exact steps shown in this post here

Creating the Application Folders and Files

  1. Create a new folder where your project code will live. You can name it anything you’d like but I’ll call ittypescriptDemo in this post.
  2. Create the following folders inside of typescriptDemo:
    • src
    • src/app
    • src/js
  3. Open a command-prompt in the root of the typescriptDemo folder and run the following npm command (you’ll need to have Node.js installed) to create a file named package.json
  4. npm init
  5. Answer the questions that are asked. For this example you can go with all of the defaults it provides. After completing the wizard a new file named package.json will be added to the root of the folder.
  6. Create the following files in the typescriptDemo folder:
    • gulpfile.js
    • gulpfile.config.js
    • tslint.json


Installing Gulp, Gulp Modules and TSDimage

  1. Now let’s get Gulp installed globally on your machine. Open a command-prompt and run the following command:
  2. npm install gulp –g
  3. Open package.json and add the following devDependencies property into it. The location of the property in the file doesn’t really matter but I normally put it at the bottom. A sample package.json file with the dependencies already in it can be found at https://github.com/DanWahlin/AngularIn20TypeScript

    Note: The module versions shown here will certainly change over time. You can visit http://npmjs.org to find the latest version of a given module. 
  4. "devDependencies": { 
        "gulp": "^3.8.11", 
        "gulp-debug": "^2.0.1", 
        "gulp-inject": "^1.2.0", 
        "gulp-sourcemaps": "^1.5.1", 
        "gulp-tslint": "^1.4.4", 
        "gulp-typescript": "^2.5.0", 
        "gulp-rimraf": "^0.1.1" 
    }
    
  5. Ensure that your command window path is at the root of the typescriptDemo folder and run the following command to install the dependencies:

    npm install
  6. The http://definitelytyped.org site provides a Node.js module named tsd that can be used to install TypeScript type definition files that are used to provide enhanced code help in various editors. Install thetsd module globally by running the following command:

    npm install tsd@next -g
  7. Run the following command:

    tsd init
  8. Open the tsd.json file that is generated in the root of typescriptDemo and change the following properties to include “tools” in the path as shown next:

    "path": "tools/typings","bundle": "tools/typings/tsd.d.ts"
  9. Let’s use tsd to install a TypeScript definition file for Angular (an angular.d.ts file) and update the tsd.jsonfile with the Angular file details as well. Run the following command:
  10. tsd install angular --save 
    Note: You can install additional type definition files for other JavaScript libraries/frameworks by running the same command but changing the name from “angular” to the appropriate library/framework. Seehttp://definitelytyped.org/tsd for a list of the type definition files that are available.
  11. Let’s now install the jQuery type definition as well since the Angular type definition file has a dependency on it:

    tsd install jquery --save
  12. If you look in the typescriptDemo folder you’ll see a new folder is created named tools. Inside of this folder you’ll find a file named typings that has an angular/angular.d.ts type definition file and ajquery/jquery.d.ts file in it. You’ll also see a file named tsd.json.
  13. Create a file named typescriptApp.d.ts in the typescriptDemo/tools/typings folder. This file will track all of the TypeScript files within the application to simplify the process of resolving dependencies and compiling TypeScript to JavaScript.
  14. Add the following into the typescriptApp.d.ts file and save it (the comments are required for one of the Gulp tasks to work properly):
//{
//}


Creating Gulp Tasks

  1. Open https://github.com/DanWahlin/AngularIn20TypeScript/blob/master/gulpfile.config.js in your browser and copy the contents of the file into your empty gulpfile.config.js file. This file sets up paths that will be used when performing various tasks such as compiling TypeScript to JavaScript.
  2. Open https://github.com/DanWahlin/AngularIn20TypeScript/blob/master/gulpfile.js in your browser and copy the contents of the file into your empty gulpfile.js file. This creates the following Gulp tasks:

    gen-ts-refs: Adds all of your TypeScript file paths into a file named typescriptApp.d.ts. This file will be used to support code help in some editors as well as aid with compilation of TypeScript files.
  3. ts-lint: Runs a “linting” task to ensure that your code follows specific guidelines defined in the tsline.js file.
    compile-ts: Compiles TypeScript to JavaScript and generates source map files used for debugging TypeScript code in browsers such as Chrome.
    clean-ts: Used to remove all generated JavaScript files and source map files.
    watch: Watches the folder where your TypeScript code lives and triggers the ts-lint, compile-ts, and gen-ts-refs tasks as files changes are detected.
    default: The default Grunt task that will trigger the other tasks to run. This task can be run by typing gulpat the command-line when you’re within the typescriptDemo folder.
  4. Open https://github.com/DanWahlin/AngularIn20TypeScript/blob/master/tslint.json in your browser and copy the contents of the file into your empty tslint.js file. This has the “linting” guidelines that will be applied to your code. You’ll more than likely want to tweak some of the settings in the file depending on your coding style.

Compiling TypeScript to JavaScript

  1. Now that the necessary files are in place (whew!), let’s add a test TypeScript file into the application folder and try to compile it to JavaScript. Create a file named customer.ts in the typescriptDemo/src/appfolder.
  2. Add the following code into the customer.ts file:

    class Customer {
        name: string;
    
        constructor(name: string) {
            this.name = name;
        }
    
        getName() {
            return this.name;
        }
    }
    
  3. Run the following command in your command window (run it from the root of the typescriptDemofolder):

    gulp
  4. You should see output that shows that the tasks have successfully completed. 
  5. Open the src/js folder and you should that two new files named customer.js and customer.js.map are now there.
  6. Go back to customer.ts and change the case of the Customer class to customer. Save the file and notice that the gulp tasks have run in the command window. You should see a tslint error saying that the case of the class is wrong.
  7. Your Gulp/TypeScript workflow is now ready to go. 

Conclusion

In this post you’ve seen the steps required to create a custom TypeScript workflow using the Gulp JavaScript task runner. Although you may certainly want to tweak some of the settings and tasks, the steps shown here should help get you started using TypeScript in your applications.

Free hosting web sites and features -2024

  Interesting  summary about hosting and their offers. I still host my web site https://talash.azurewebsites.net with zero cost on Azure as ...