Category: Angular

Integrating SPA ( Angular, React, Vue) with .NET Core for production

If you have a .NET Core WebAPI application ( that only serves data ) and  a SPA application ( Angular, Vue,  React) you can have just one application following this 2 steps:

1.  In .NET Core add in Startup.cs ( with using Microsoft.AspNetCore.Builder from Microsoft.AspNetCore.StaticFiles )

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
             {
                

            // If you know the endpoints, put it here

           //endpoints.MapFallbackToFile(“dbodepartment/{**slug}”,”/index.html”);
                         
                 endpoints.MapControllers();
                 endpoints.MapFallbackToFile(“{**slug}”, “/index.html”);
});

2. Compile the SPA to index.html, deploy in wwwroot with the js and css

( yes, you should modify the endpoint for data when compiling to / –  for Angular see my previous post , http://msprogrammer.serviciipeweb.ro/2020/09/14/angular-base-href-for-web-desktop-mobile-and-paths-for-services/ )

Angular base href for web, desktop, mobile ( and paths for services)

My current stack is .NET Core (backend) + Angular ( frontend) ( For  database, it is usual SqlServer – but I use Sqlite, Mongo, local storage ….)

With Angular I can have Mobile application ( by Cordova ) and Windows Desktop ( with Electron)

But the BaseHref it is a PIA …. even for Web, if you host into a virtual directory .

So this is my setup in 2 steps. Maybe could be somehow easy, but… I did not discover something better yet.

Step 1: Create a favicon.svg into the assets folder

Step 2: Instead of

<base href=”/”>

put

<!– <base href=”/”> –>

<script>

var isCordovaApp = !!window.cordova;

var isWindowApp = !!window.MSApp;

if (isCordovaApp) {

        hrefApp = “.”;

      }

if (isWindowApp) {

        hrefApp = “./”;

      }

if (!(isCordovaApp || isWindowApp)) {

var baseHref = window.location.href.split(“/”);

while (true) {

          baseHref.pop();

          hrefApp = baseHref.join(“/”) + “/”;

// window.alert(hrefApp);

var request = new XMLHttpRequest();

          request.open(“GET”, hrefApp + “favicon.svg”, false);           request.send(null);

if (request.status === 200) {

if(request.responseText.indexOf(“meta name=”)<1){

              console.log(`found ${hrefApp} `);

break;

            }

          }

        }

      }

    document.write(‘<base href=”‘ + hrefApp+ ‘” />’);

</script>

And this is all!

For correct path of services , I do this

For each service , I construct with

constructor(@(Inject)(APP_BASE_HREF) baseHref: string, private client: HttpClient)

this.baseUrl = environment.webAPIUrl + baseHref ;

and  when I call a function

const url = this.baseUrl+’api/WeatherForecast;

In the envoronment.ts I have

export const environment = {

  production: false,

  webAPIUrl: ‘http://localhost:5000’

};

and in the environment.prod.ts I do have

export const environment = {

  production: true,

  webAPIUrl: ”

};

One more thing: to inject APP_BASE_HREF , I must have into app.modue.ts

providers: [{

    provide: APP_BASE_HREF,

    useFactory: (s: PlatformLocation) => s.getBaseHrefFromDOM(),

    deps: [PlatformLocation]

  }

That’s all! ( yes , I know it is a bit complicated….)

Interceptor in Angular–Error service

Similar video at https://www.youtube.com/watch?v=ajosuyhcKV4

I need to inform the user when the application is loading data with error ( i.e. the backend is giving some error back). And do it seamlessly , every time.

I found the documentation about interceptors at https://angular.io/api/common/http/HttpInterceptor”– and you can find more searching on google /  bing .

Also, I have the need to display this no matter in what page  – so a service will help


import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ErrorService {

  private err: Subject<string>;

  constructor() {
    this.err = new Subject<string>();
  }

  public NextError(): Observable<string> {

    return this.err.asObservable();

  }

  public setNextError(str: string) {

    this.err.next(str);

  }

}


Now the interceptor

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorService } from './error.service';

/** Pass untouched request through to the next request handler. */
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {


  constructor(private err: ErrorService ) {


  }
  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {

    return next.handle(req).pipe(
        catchError( err => this.handleError(req, err))
    );
  }

  private handleError(req: HttpRequest<any>, error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      const str = 'An error occurred:' +   error.error.message;
      console.log(str);
      this.err.setNextError(str);

    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      const str =
        `Backend returned code ${error.status}, ` +
        `body was: ${JSON.stringify(error.error)}`;
      console.log(str);
      this.err.setNextError(str);
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something bad happened; please try again later.');
  }
}


The class itself

@Component({
  selector: 'app-covid-navig',
  templateUrl: './covid-navig.component.html',
  styleUrls: ['./covid-navig.component.css']
})
export class CovidNavigComponent  {
  public isLoading = false;
  constructor(private breakpointObserver: BreakpointObserver
    ,         private err: ErrorService
    ,         private snackBar: MatSnackBar
    ,         private ls: LoaderService) {
    this.err.NextError().pipe(
      tap(it => {
        this.snackBar.open(it, 'ERROR', {
          duration: 5000,
        });
      }),
      shareReplay()
    ).subscribe();
    this.ls.loading$().subscribe(it => this.isLoading = it);

  }

And this is all !( besides registering the interceptors in a barrelInterceptors.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorInterceptor } from './ErrorInterceptor';
import { LoaderInterceptor } from './LoaderInterceptor';





/** Http interceptor providers in outside-in order */
export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
  { provide: HTTP_INTERCEPTORS, useClass: LoaderInterceptor, multi: true },
];

and register to the appmodule.ts


providers: [
    httpInterceptorProviders,
  ] 

Interceptor in Angular–loader and service

Video at https://www.youtube.com/watch?v=ajosuyhcKV4

I need to inform the user when the application is loading data – and when it is not loading anymore. Usually , the data comes from a backend via an HTTP call– and will be some timeout before it becomes available. So – simple – just intercept this and then show some spinning to the user, right ?

I found the documentation about interceptors at https://angular.io/api/common/http/HttpInterceptor – and you can find more searching on google /  bing .

Also, my inspiration was started from here:

https://grensesnittet.computas.com/loading-status-in-angular-done-right/

But , if we intercept, how to distribute ? The services singleton in Angular will provide help

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LoaderService {
  constructor() {
    this.s = new Subject<boolean>();

  }

  public s: Subject<boolean>;
  public loading$(): Observable<boolean> {
    return this.s.asObservable();
  } 

  public isLoading(b: boolean) {
    this.s.next(b);
  }

}

So I have created the HttpInterceptor to call the service , like here:

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { catchError, tap, finalize } from 'rxjs/operators';

import { LoaderService } from './loader.service';

/** Pass untouched request through to the next request handler. */
@Injectable()
export class LoaderInterceptor implements HttpInterceptor {

  private requests: HttpRequest<any>[] = [];
  constructor(private ls: LoaderService ) {


  }
  removeRequest(req: HttpRequest<any>) {
    const i = this.requests.indexOf(req);
    if (i >= 0) {
      this.requests.splice(i, 1);
    }
    this.ls.isLoading(this.requests.length > 0);
  }
  // https://grensesnittet.computas.com/loading-status-in-angular-done-right/
  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    this.requests.push(req);
    this.ls.isLoading(true);
    return next.handle(req).pipe(
        finalize(() => this.removeRequest(req))
    );
  }

}

And now it is simple to display in any page: – in the HTML

<mat-spinner *ngIf="isLoading"></mat-spinner>

and in the .ts pass in the constructor via DI

@Component({
  selector: 'app-covid-navig',
  templateUrl: './covid-navig.component.html',
  styleUrls: ['./covid-navig.component.css']
})
export class CovidNavigComponent  {
  public isLoading = false;
  constructor(private breakpointObserver: BreakpointObserver
    ,         private err: ErrorService
    ,         private snackBar: MatSnackBar
    ,         private ls: LoaderService) {
    this.err.NextError().pipe(
      tap(it => {
        this.snackBar.open(it, 'ERROR', {
          duration: 5000,
        });
      }),
      shareReplay()
    ).subscribe();
    this.ls.loading$().subscribe(it => this.isLoading = it);

  }

Simple , right ?

Covid Data – making the application – part 4

Now I need an application. The first version was a simple html file with Data Tables ( https://datatables.net/ ). You can find at https://ignatandrei.github.io/WFH_Resources/output.html . It is a simple table with search / filter and sorting.

The next , with help from Bogdan we have  started an Angular application , deployed at https://ignatandrei.github.io/WFH_Resources within the docs folder : http://github.com/ignatandrei/wfH_Resources/docs

For Angular the only thing that gives me problems were the charts : https://ignatandrei.github.io/WFH_Resources/covidData/Italy-Romania  . I used the https://www.chartjs.org/ . It is very flexible and doing all that I have wanted. The only problem : it retains previous graphic. I have to manually clear the canvas before rendering again the chart.

if (this.lineChart1) {

this.lineChart1.destroy();

}

You can find the charts at https://ignatandrei.github.io/WFH_Resources/covidData/Italy-Romania

Caching data frontend- Angular interceptors for Observable–part 48

Now I want to cache data for fast loading site- if it comes next time with the same browser, should have some data returned fast – and then load from HTTP. So first return from cache, then make the request

So I need to create an HTTP interceptor to cache things-  fortunately, Angular already provide tutorial about this: https://angular.io/guide/http#intercepting-requests-and-responses  – see CachingInterceptor. And the question is how to return 2 results ( of the same type ) from the same function  ?  Fortunately, the answer is: Observable ! ( It took me a while to figure that intercept returns Observable …). Also, when making the http request, the code should also store the result for the next time.

So I need to combine the 2 observables, one from cache and one from HTTP result  – or I can use startWith ( to start with the value).

First, I wanted to put into a static dictionary ( key: url, value: value of the request) the data. It did not work. Why ? Because of this

 

static cache: Map<string, any> = new Map<string, any>();

//code for finding if we have something in the case

if (CachingInterceptor.cache.has(req.url)) {

//code to put into the cache

this.cache[req.url] = event.body; // Update the cache.

 

Can you spot the error ? ( Yes, I know: Dictionary in C# has tricked my mind)

Last line should be this:

CachingInterceptor.cache.set(url,event.body); // Update the cache.

Now the caching works – but only for local .  And only in memory .

You can find the commit at https://github.com/ignatandrei/InfoValutar/commit/2bde8130add59c84d11d7348ad0a88bb576e356a

Infovalutar

And one hour passes...
(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
( You can see the sources at https://github.com/ignatandrei/InfoValutar/ )
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure
20Pipeline send to Docker Hub
21Play with Docker - online
22Run VSCode and Docker
23Deploy Azure
24VSCode see tests and powershell
25Code Coverage
26Database in Azure
27Sql In Memory or Azure
28Azure ConString, RSS
29Middleware for backward compatibility
30Identical Tables in EFCore
31Multiple Data in EFCore
32Dot net try again
33Start Azure Function
34Azure function - deploy
35Solving my problems
36IAsyncEnumerable transformed to IEnumerable and making Azure Functions works
37Azure functions - final
38Review of 37 hours
39Last Commit in AzureDevOps
40Create Angular WebSite
41Add static Angular to WebAPI .NET Core
42Docker for Angular
43Angular and CORS
44SSL , VSCode, Docker
45Routing in Angular
46RxJS for Routing
47RxJs Unsubscribe

RxJS–Unsubscribe automatically –part 47

I am searching for a simple method to unsubscribe from Observables. Some of methods are detailed here : https://blog.bitsrc.io/6-ways-to-unsubscribe-from-observables-in-angular-ab912819a78f

I do not want

  1. remember to unsubscribe
  2. being forced to use html ( the async pipe)
  3. take operator – I want later to cache first data and then make the http call
  4. first operator – same with 3
  5. decorator – seems ok.
  6. tsLint – no, can be easy to disable

 

So Decorator it is. More reading

https://www.typescriptlang.org/docs/handbook/decorators.html

https://netbasal.com/automagically-unsubscribe-in-angular-4487e9853a88

Added code the autoUnsub

export function AutoUnsub( constructor ) {

const original = constructor.prototype.ngOnDestroy;

console.log(‘from unsubscribe’);

constructor.prototype.ngOnDestroy = function() {

// tslint:disable-next-line: forin

for ( const prop in this ) {

const property = this[ prop ];

if ( property && (typeof property.unsubscribe === ‘function’) ) {

console.log(‘unsubscribe !’);

property.unsubscribe();

}

}

console.log(‘finish unsub’);

// tslint:disable-next-line: no-unused-expression

original && typeof original === ‘function’ && original.apply(this, arguments);

};

}

Modified code to make property of unsubscribe, e.g. from

this.banksService.GetBanksIds().subscribe(

it=>{

console.log(it.length);

this.banks=it;

},

err=> window.alert(“error:”+ JSON.stringify(err))

)

}

to

banksObs: Observable<string[]>;

//ommitted code

this.banksObs = this.banksService.GetBanksIds();

this.banksObs.subscribe(

it => {

console.log(it.length);

this.banks = it;

},

err => window.alert(‘error:’ + JSON.stringify(err))

);

Created also a new component, for programmers, in order to observe the unsubscribe moving from component to component,

See commit https://github.com/ignatandrei/InfoValutar/commit/66e358848652ce761cb6ef2d69a460f052b12e09

Infovalutar

And one hour passes...
(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
( You can see the sources at https://github.com/ignatandrei/InfoValutar/ )
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure
20Pipeline send to Docker Hub
21Play with Docker - online
22Run VSCode and Docker
23Deploy Azure
24VSCode see tests and powershell
25Code Coverage
26Database in Azure
27Sql In Memory or Azure
28Azure ConString, RSS
29Middleware for backward compatibility
30Identical Tables in EFCore
31Multiple Data in EFCore
32Dot net try again
33Start Azure Function
34Azure function - deploy
35Solving my problems
36IAsyncEnumerable transformed to IEnumerable and making Azure Functions works
37Azure functions - final
38Review of 37 hours
39Last Commit in AzureDevOps
40Create Angular WebSite
41Add static Angular to WebAPI .NET Core
42Docker for Angular
43Angular and CORS
44SSL , VSCode, Docker
45Routing in Angular
46RxJS for Routing
47RxJs Unsubscribe

Routing angular with .net core and RxJs switchmap- part 46

Now the problem is that the route

 

works if I access first the index.html file, but it does not route when entered directly

Trying to get from SPATemplate

https://github.com/ignatandrei/InfoValutar/commit/a364dc57653ac2ba04ab0596f1540a8f9fdb73f6

However, this will redirect ALL routes to index.html – including swagger and API.  Trying to learn more – looking at the code.

I have observed that I have put

app.UseOpenApi();
app.UseSwaggerUi3();

after

this.route.params.pipe(

tap(rp => {

this.idBank = rp.id;

})

, switchMap((it) => this.bs.GetRates(it.id))

, tap(v => this.rates = v)

).subscribe();

What If I put first?

app.UseOpenApi();
app.UseSwaggerUi3();

app.UseSpa(spa =>

 

And it works! https://github.com/ignatandrei/InfoValutar/commit/726ebd07095ea62387d4eeebb017b4f6ae4ab8e6

What I need more, is to retrieve the exchange rates for today for the selected bank.

The code to retrieve the bank id was:

this.route.params.subscribe(rp => {

this.idBank = rp.id;

}

);

I can put into subscribe the next call, but it will be not so nice ( subscribe into subscribe into subscribe)

this.route.params.subscribe(rp => {

this.idBank = rp.id;

this.bs.GetRates(it.id).subscribe( ….)

}

)

 

Instead of this, I use switchMap , pipe and tap

this.route.params.pipe(

tap(rp => {

this.idBank = rp.id;

})

, switchMap((it) => this.bs.GetRates(it.id))

, tap(v => this.rates = v)

).subscribe();

To fast show data, I use the Json pipe from Angular

<ul>

<li *ngFor=”let rate of rates”>

{{rate | json}}

</li>

</ul>

Modifications at https://github.com/ignatandrei/InfoValutar/commit/a27c7c7beb4ea10b9f8a522b01d558c6003d5386

Infovalutar

And one hour passes...
(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
( You can see the sources at https://github.com/ignatandrei/InfoValutar/ )
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure
20Pipeline send to Docker Hub
21Play with Docker - online
22Run VSCode and Docker
23Deploy Azure
24VSCode see tests and powershell
25Code Coverage
26Database in Azure
27Sql In Memory or Azure
28Azure ConString, RSS
29Middleware for backward compatibility
30Identical Tables in EFCore
31Multiple Data in EFCore
32Dot net try again
33Start Azure Function
34Azure function - deploy
35Solving my problems
36IAsyncEnumerable transformed to IEnumerable and making Azure Functions works
37Azure functions - final
38Review of 37 hours
39Last Commit in AzureDevOps
40Create Angular WebSite
41Add static Angular to WebAPI .NET Core
42Docker for Angular
43Angular and CORS
44SSL , VSCode, Docker
45Routing in Angular
46RxJS for Routing
47RxJs Unsubscribe

Displaying banks with Angular routing-part 45

Now I want to have a separate URL for each bank, like  https://infovalutar.azurewebsites.net/bank/ECB

For this I read routing in Angular, https://angular.io/tutorial/toh-pt5 .

However , a small problem : when we change URL , from

https://infovalutar.azurewebsites.net/bank/ECB

to

https://infovalutar.azurewebsites.net/bank/BNR

we should re-load component – or the component be aware of changing.

Reading

https://kamranahmed.info/blog/2018/02/28/dealing-with-route-params-in-angular-5/

makes me change from

this.idBank = this.route.snapshot.paramMap.get(‘id’);

to

this.route.params.subscribe(rp=> {

this.idBank = rp.id;

}

    );

Seems little to do, but I have had problems with changing the router-outlet – figuring where to put it. The creation of dashboard component and the routes

const routes: Routes = [

{ path: ”, redirectTo: ‘/dashboard’, pathMatch: ‘full’ },

{ path: ‘dashboard’, component: DashboardComponent },

{path:’bank/:id’,component:BankComponent}

]

were enough trouble.

Infovalutar

And one hour passes...
(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
( You can see the sources at https://github.com/ignatandrei/InfoValutar/ )
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure
20Pipeline send to Docker Hub
21Play with Docker - online
22Run VSCode and Docker
23Deploy Azure
24VSCode see tests and powershell
25Code Coverage
26Database in Azure
27Sql In Memory or Azure
28Azure ConString, RSS
29Middleware for backward compatibility
30Identical Tables in EFCore
31Multiple Data in EFCore
32Dot net try again
33Start Azure Function
34Azure function - deploy
35Solving my problems
36IAsyncEnumerable transformed to IEnumerable and making Azure Functions works
37Azure functions - final
38Review of 37 hours
39Last Commit in AzureDevOps
40Create Angular WebSite
41Add static Angular to WebAPI .NET Core
42Docker for Angular
43Angular and CORS
44SSL , VSCode, Docker
45Routing in Angular
46RxJS for Routing
47RxJs Unsubscribe

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.