#Angular in #VSCode: Using SwitchMap to check a service for the existence of an item

In Angular, you can use the switchMap to “Switch” from one stream to another using Observables. To follow along, add this component to your project in VS Code.

Let’s look at the code first, then we will go over it:

@Component({
template: `
<input [formControl]="petControl"/>
<span *ngIf="isPetExists">Pet already exists!</span>
`
})
export class PetComponent implements OnInit {
petControl = new FormControl('');
isPetExists = false;
constructor(private service: Service) {}
ngOnInit() {
this.petControl.valueChanges
.pipe(debounceTime(500),
switchMap((value: string) => {
return this.service.isPetExists(value);
}).subscribe((isPetExists: boolean) => {
this.isPetExists = isPetExists;
});
}
}
}

In the Component we create a simple form control so that we can get a stream of values via its valueChanges obserable.

We use debounceTime to only check every .5 seconds

We then take the value of the control, the pet name, the return a new observable, this time a boolean, from a service that will check if the pet exists.

Debugging an #Asp.Net Core Site with #Angular all in #VS Code. #Visual Studio Not Required.

Whoa. This is really cool. I’ve been trying to move completely to VS Code as my editor for everything, but debugging an Asp.Net Core 3 site with Angular all at once proved difficult, until now.

This article will show you the easy way to do it, if you want to get more into the weeds, there is always the docs 🙂

Step 1:

I assume you used Visual Studio to create a new Asp.Net Core application with Angular using File | New:

Step 2:

Edit the Startup.cs so that it relies on you running Angular.

app.UseSpa(spa =>
   {
      spa.Options.SourcePath = "ClientApp";
      if (env.IsDevelopment())
       {
         spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
       }
  });

Close Visual Studio.

Step 3:

Open VS Code at the same folder as the .sln file is

Step 4:

Tap the RUN menu then Add Configuration, this will open the launch.json file

Replace it with this (Change the .dll and .csproj path as needed)

"version": "0.2.0",
  "compounds": [
    {
      "name": ".Net+Browser",
      "configurations": [".NET Core Start", "Launch Chrome"]
    }
  ],
  "configurations": [

    {
      "name": ".NET Core Start",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      // If you have changed target frameworks, make sure to update the program path.
      "program": "${workspaceFolder}/HelloWorld/bin/Debug/netcoreapp3.1/HelloWorld.dll",
      "args": [],
      "cwd": "${workspaceFolder}/HelloWorld",
      "stopAtEntry": false,
      "env": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    {
      "name": "Launch Chrome",
      "request": "launch",
      "type": "chrome",
      "webRoot": "${workspaceFolder}/HelloWorld/ClientApp",
      "sourceMaps": true,
      "url": "https://localhost:5001"
    }
  ]
}

Step 5:

Add “start” to your /ClientApp/package.json

  "scripts": {
    "start": "ng serve",

We are now ready to debug

Debug Step 1: Start up Angular in the VS Code Terminal by running ‘npm start’

Debug Step 2: Press F5 and choose “.Net+Browser” as your startup task

Place debug stop points and enjoy!

#Angular Confirm Popup (Not Modal!) in #VSCode

A simple, and easy to use Confirmation Popup, that is not modal. We will use Visual Studio Code to edit the Typescript.

I’ll assume you have Google Material added to your Angular project as we will use that for style, however, it is not needed. This project is based on the great work of Netanel, the guy behind Akita.

Part one: The end result

<app-confirm [confirmQuestion]="'Delete your order # ' + item.caseNumber" (confirmedAction)="delete(item)">
      <mat-icon>delete</mat-icon>
</app-confirm>

You will end up with a component that you can wrap an item with.

Let’s make that now:

Part two: Add Helipopper

Add Helipopper to your project with npm.

Part three: Add the component

Now add a new component called confirmPopup.ts.

import { Component, Input, ViewChild, TemplateRef, Output, ElementRef, HostListener } from '@angular/core';
import { HelipopperService, HelipopperDirective } from '@ngneat/helipopper';
import { EventEmitter } from '@angular/core';

@Component({
  selector: 'app-confirm',
  template: `
    <ng-template>
      <div>
        <h4>{{ confirmQuestion }}?</h4>
        <div>
          <button class="mat-raised-button mat-button-base" (click)="closeButton()">No</button>
          <button class="mat-raised-button mat-button-base mat-primary" (click)="confirmActionButton()">Yes</button>
        </div>
      </div>
    </ng-template>
    <ng-content></ng-content>
  `,
})
export class ConfirmComponent {
  @Input() confirmQuestion = '';
  @ViewChild(TemplateRef) tpl: TemplateRef<any>;
  @Output() confirmedAction: EventEmitter<any> = new EventEmitter();

  private popper: HelipopperDirective;

  constructor(private service: HelipopperService, private host: ElementRef) {}

  @HostListener('click')
  open(): any {
    if (this.popper) {
      return;
    }

    this.popper = this.service.open(this.host, this.tpl, {
      variation: 'popper',
      allowClose: false,
    });
  }

  confirmActionButton(): any {
    this.closeButton();
    this.confirmedAction.emit();
  }

  closeButton(): any {
    this.popper.destroy();
    this.popper = null;
  }
}

Styling

You’ll probably want to add some SCSS styling , here’s a nice example to get you going (Helipopper uses tippy.js)

@import '~tippy.js/dist/tippy.css';
@import '~tippy.js/themes/light.css';
@import '~tippy.js/animations/scale.css';

.tippy-content {
  position: relative;
}

.tippy-close {
  position: absolute;
  width: 24px;
  height: 24px;
  top: 9px;
  right: 9px;
  fill: rgb(158, 160, 165);
  cursor: pointer;
  z-index: 1;
}

.tippy-box {
  border-radius: 4px;
  font-size: 11px;

  .tippy-content {
    padding: 4px 6px;
  }
}

.tippy-box[data-theme~='light'] {
  font-size: 12px;
  word-break: break-word;
  border-radius: 0;
  background-color: rgb(255, 255, 255);
  box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.2);
  color: rgb(79, 80, 83);

  .tippy-content {
    padding: 13px 48px 13px 20px;
  }
}

.tippy-arrow::before {
  box-shadow: -4px 4px 14px -4px rgba(0, 0, 0, 0.2);
}

Advanced trick:

If you’d like to change this to an attribute directive, change the selector to:

selector: '[app-confirm]',

Then you can use it like this:

<span app-confirm [confirmQuestion]="'Delete your order # ' + item.caseNumber" (confirmedAction)="delete(item)">
          <mat-icon>delete</mat-icon>
</span>

You can’t add this to a Google Angular Material component directly, so I tend to use it as an element instead.

VS Code #Typescript: Using Named Alias Path Mapping in Import Statements

In Typescript, you need to import code from other files and to do so, you need to specify a path like this:

import {
  FormFieldMetadataService,
  FormFieldMetadataQuery,
  HandleApiErrorService
} from '../../../core/services/blah/blah/this/is/crazy';

Fortunately there is a much easier TypeScript way to do it. You can add an Paths Mapping alias to the tsconfig.json so that you can just use this:

} from '@myservices'; 

First, add an index.ts file to your folder containing a group of items, like services, that you wish to expose to the rest of your app like this:

#index.ts Contents below. Note: This file simply exports your "public" services to the rest of the app, like services:

export { AuthSessionQuery } from './auth/auth-session/query';
export { AuthSessionStore } from './auth/auth-session/store';
export { AuthSessionService } from './auth/auth-session/service';

Now, in your tsconfig.json check that the baseUrl is set, as your alias will be relative to this:

"baseUrl": "./",

Next, in the same tsconfig.json file, add your named typescript alias.

  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@myservices": ["src/app/core/services/index.ts"],
      "@mymodels": ["src/app/core/models/index.ts"],
      "@myguards": ["src/app/core/guards/index.ts"]
    },

You can then import from these using the named Typescript alias:

#Asp.Net Core Web API and #Angular File Downloading: The easy way

Here’s how you can download a Word, Excel or another document to the browser using Asp.Net Core Web API and Angular. The simplest way.

Our data store has documents stored for use in our Angular application, we can retrieve them as byte[] arrays. But how to get them to download in Angular?

Step 1: Asp.Net Core Web API Controller

In your controller, get the bytes, convert to a Stream then return a new FileStreamResult like this:

[HttpGet]
[Route("getdocument")]
public async Task<ActionResult> DownloadTheDocument(string documentUrl, string filename, string ext)
{
   var dataBytes = await _manager.GetDocument(documentUrl);
   var dataStream = new MemoryStream(dataBytes);
   return new FileStreamResult(dataStream, new MediaTypeHeaderValue($"application/{ext}"))
   {
        FileDownloadName = filename
   };

}

Step 2: Angular Html

In your HTML wire up a click event on an element that knows about the document:

<div  style="cursor: pointer;" (click)='download(doc)'></doc>

Step 3: Angular Component

download(doc: any): void {
    window.location.href = `https://localhost/api/documents/getdocument?documentUrl=${doc.downloadUrl}&filename=${doc.name}${doc.ext}`
    );
}

That’s it. When tapped, your Component will get the document and download it. Works on iPhone, Android, PC and Mac.

#VS Code and RxJS: Simple Observable Concepts everyone should know

  1. Calling subscriber() without params will case an observable to fire:
this.myService.Do().subscribe();

2. When you catch a Pipe error, throw it again:

this.httpThingy.get()
.pipe(
   catchError(err=> {
      console.log('failed', err);
      const friendlyMsg = "sorry, try again";
      return throwError(friendlyMsg);
      }),
   map((data: ComplexMoviesTreeOfData) => data.movies.starwarsmovies)
);

3. The component can then display the error:

this.myService.Do().subscribe(
   theData => (this.movies = theData),
   theNiceError => this.errorMsg = theNiceError
);

4. Map operator takes the data list, then lets you choose what to actually return

map((data: ComplexMoviesTreeOfData) => data.movies.starwarsmovies)

5. asyncPipe can be a short hand for displaying data without having to subscribe and unsubscribe. Simply assign the observable from the service to a component member$ and reference it:

<div *ngFor="let movie of movies$ | async>{{movie.name}}</div>

To use this, in our component we set the member variable to the observable and of course catch the error.

this.movies$ = this.myService.Do().pipe(
     catchError(err => {
        this.errorMsg = err;
         return [];
       })
);

6. A [formControl] can be a simple observable, perhaps for a simple search box.

<input type='text' [formControl]='searchTerm' />

In your component:

searchTerm = new FormControl();
searchTerms$ : Observable<string> = this.searchTerm.valueChanges;

Now you can handle the stream of text from the searchTerms$ and react only when typing something new, and after a delay.

movies$ = this.searchTerms$.pipe(
  tap(() => this.errorMsg = ''), // clear previous error
  debounceTime(1000),  // wait for a puse in typing
  distinctUntilChanged(),   // only if there was some kind of change

// Do the search if we get this far in the operators list and "switch" it to a list of movies
  switchMap(searchTerm => this.movieService.search(searchTerm)
  .pipe(
     catchError(err=> {
       this.errorMsg = err.message;
       return EMPTY;
     })
    )
  ),

  //If we get this far, then map the results and return what the UI expects
  map(this.cleanUpResults)
);

cleanUpResults(list: movies[]) : movies[] {
    return list.length === 0 ? [{title: 'No Results'}] : list;
}

7. When an error happens in an observable, it stops the stream(s). That’s bad as you can’t restart it. So we use error isolation and handle it in a sub observable where it can stop that, but not the top level one.

moviesWithErrorsIsolated$ = this.movies$.pipe(
   switchMap(id=> this.getMovieById$(id)
      //PIPE AGAIN off to get the movies
      .pipe(
           map(result => result.title),  //extract title
           catchError(e=> of('Didn't find it '))
        )
   )
  );

8. Short form for calling an observable with a param

getMovies$ = theId => this.http.get<movie>(https..{theId});

9. Route object is an observable too

this.theCurrentMovie$ = this.route.paramMap.pipe(
   map(params = > params.get('id')),
   switchMap(id => {
      return this.movieService.getMovie(id).pipe(
         tap(movie => this.currentId = movie ? movie.id : 0)
      );
      })
    );  

#VSCode + #Angular: Adding an #AuthGuard with multiple authorization conditions

This article will show you how to protect an Angular component with an AuthGuard which requires multiple authorization conditions found in RxJS Observables.

I’ll assume you have an AuthGuard up and you just want to know who to return a value from 2 conditions.

Here’s the end code if you just want to jump to that:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(....omitted) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {

    const storeAuth = this.store.isLoggedIn$;
    const heartBeatAuth = this.http.get<boolean>('/api/auth/heartbeat');

    return combineLatest([storeAuth, heartBeatAuth]).pipe(
      map((results) => {
        if (results[0] && results[1]) {
          return true;
        }
        this.router.navigateByUrl('/login');
        return false;
      }),
      take(1)
    );
  }
}

Let’s break that down:

There are 2 observables that both return a boolean. One talks to a store, in this case Akita. The other a simple http call that does a ping to a heartbeat that will say if the user’s auth cookie is valid still.

const storeAuth = this.store.isLoggedIn$;
const heartBeatAuth = this.http.get<boolean>('/api/auth/heartbeat');

You’ll notice there are no subscriptions yet, these observables haven’t been activated.

The next step is to get the values from both, in paralelle, then check tha that they are both true. For that we use the forkJoin from RxJS. forkJoin will execute both observables and then send you the values which you can either return, or redirect with the router:

return combineLatest([storeAuth, heartBeatAuth]).pipe(
      map((results) => {
        if (results[0] && results[1]) {
          return true;
        }
        this.router.navigateByUrl('/login');
        return false;
      }),
      take(1)
    );

It’s that easy. forkJoin should be used anytime you want to get all the values in parallel.

Speeding up VS Code and/or spring cleaning

VS Code is awesome, much faster than Visual Studio in many web related tasks. However, I’ve found it can benefit from some spring cleaning every once and a while. Here’s a quick way to improve its speed and get back that shiny new-car smell.

Overview: We are going to backup the settings you’ve made, extensions etc. Uninstall it, remove caching, then reinstall it and restore settings.

Step 1: Sync your settings

Tap the gear icon in the lower left and make sure you check for updates to get the latest VSCode. Then Go back there and choose the Settings Sync option. Allow Settings Sync to back you up.

Step 2: Uninstall

Run <drive>:\Program Files\Microsoft VS Code\unins000.exe

Remove  %AppData%\Code

Remove %userprofile%\.vscode

Step 3: Install

Download and install VS Code

4: Restore settings

Tap that same gear icon as in Step 1, then choose Sync Settings. All your extensions and settings will return.

That’s it. You have completely uninstalled, backed up and restored VS Code. Shiny new car smell.

Asp.Net Core: Clearing the entire #IMemoryCache

There isn’t a default (easy) way to clear the entire cache in Asp.net Core’s IMemoryCache. Here’s a way you can do it without having to remember all the keys and doing it yourself.

First, let’s add caching to the system by adding this code to your ConfigureServices() method call:

services.AddMemoryCache();

Now, the Asp.Net Core dependency injection system knows about IMemoryCache so we can get it in any class that we wish in its constructor:

public MyNiceController(IMemoryCache cache)

Now we can clear the cache by calling some non public methods (a little hacky, you have been warned):

[HttpGet]
[Route("Refresh")]
public async Task<ActionResult> Refresh()
 {
    PropertyInfo prop = _cache.GetType().GetProperty("EntriesCollection", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Public);
    object secretCache = prop?.GetValue(_cache);
    MethodInfo clearMethod = secretCache?.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public);
    clearMethod?.Invoke(secretCache, null);
    return new OkResult();
 
}

Writing a 1 pixel transparent PNG to the Asp.Net Core response stream

I had a requirement that if a PNG failed to download from a database and be sent to the Response.Body that a 1 pixel PNG would be sent instead. We didn’t want the 1 pixel png file on the web server.

Part 1: The solution, quick an dirty

In your Startup.cs locate the Configure method and add this.

Note: Of course you’ll want to cache and code this nicely into a service, but for simplicity….

app.Use(async (context, next) =>
{
   bool shouldAwaitNext = true;
   if (context.Request.Path == "/bad.png")
   {
      if (!context.Response.HasStarted)
      {
          shouldAwaitNext = false;
          byte[] onePixelPng = JsonConvert.DeserializeObject<byte[]>("\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQI12P4zwAAAgEBAKrChTYAAAAASUVORK5CYII=\"");
          await context.Response.Body.WriteAsync(onePixelPng, 0, onePixelPng.Length);
      }
   }
});
if (shouldAwaitNext) await next();

Part 2: How to get the serialized values in Part 1

Create the png you’d like, I created 1.png, a 1 pixel transparent png then ran it through a PNG Crusher. Put 1.png in your wwwroot.

Run this in the Configure() method once to get the serialized values:

var t = env.WebRootFileProvider.GetFileInfo("1.png").PhysicalPath;
using WebClient client = new WebClient();
byte[] data = await client.DownloadDataTaskAsync(new Uri(t, UriKind.RelativeOrAbsolute));
var hereIsYourSerializedValue= JsonConvert.SerializeObject(data);