#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!

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)
      );
      })
    );  

Visualizing your JavaScript Load in Visual Studio Code with Source-Map-Explorer and Angular

When you create a new JavaScript application with a framework like Angular, the internal distribution of what library resources are hidden behind a vail…your JavaScript is all combined into one or a few large JavaScript files.

Here’s the result visualization we’ll get to:

So, let’s get started. Open Visual Studio Code and then a command shell will CRTL+`

Now, let’s turn on sourceMaps and namedChunks in the production node of your Angular.json file.

Now build the production version of your Angular app with:

ng build --prod

We’ll need a simple npm package; add source-map-explorer to your global install:

npm install -g source-map-explorer

Once that’s done, you’re ready to explorer. Enter this (check your dist folder the your own main javascript file, you can use Tab when entering the name in source-map-explorer to get path hints.

source-map-explorer .\dist\main.b56fb80f5c91e70970e1.js

You’ll see the minified, but not compressed, file size in the headers:

Perhaps you see something you no longer need, it’s a great time to remove it!

#VSCode: Handling Debugging Error “Breakpoint set but not yet bound”

download.pngYour Angular VS Code project can contain either a simple application, or multiple. When you have multiple, it can cause issues with connecting a debugger. Here’s the trick on how to handle that situation:

After you create a new Angular app then try to debug it in VSCode, you can often get the error “Breakpoint set but not yet bound” when you try to set a break point.

The fix is simple, just indicate the actual path to the webRoot, not just the workspaceFolder in the launch.json. This depends on if you opened the workspace in the root of the app, or deeper. It must take that into consideration. workspaceFolder or workspaceFolder/your-app/

{
 
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://localhost:4200",
      "webRoot": "${workspaceFolder}/<app folder here sometimes>",
      "sourceMaps": true,
      
      --SOMETIMES THIS IS NEEDED--
      "sourceMapPathOverrides": {
        "webpack:///./*": "${workspaceRoot}/*"
      }
       
    }
  ]
}

When you just have a single application (you don’t have a Project/ folder) you might have to set the webRoot to

"webRoot": "${workspaceFolder}"

Tip: When you open the folder in VS Code, do so at the root where the .vscode folder is. The e2e, src etc folders should all be at the top level.

2020-03-10 12_36_19-launch.json - application - Visual Studio Code.png

Some Gotchas:

  • It’s all about in what folder you opened your workspace in. This affects “webRoot”: “${workspaceFolder}/MAYBE-THIS-my-app/”
  • I’ve noticed that sometimes you still get the error “Breakpoint set but not yet bound” after the first try. After you build then try to debug, close VSCode and Chrome then open up again. That sometimes helps.
  • Make sure that tsconfig.json has sourceMaps set to true
  • When you run the app in Chrome, go to the dev tools and see if the ts files are displayed like this:
2020-03-11 11_23_43-Cloning.png

#VSCode: One #Angular Workspace with multiple projects

In VS Code (Visual Studio Code) you can create a single Workspace with 1 git repo but contain multiple projects. This lets you create library and app and contain them all under the same angular.json as well.

Here’s how.

Create a folder where you’d like to create the Workspace. In my example e:\ngLearn\CLIPlayGround

2020-03-09 10_42_08-Select Windows PowerShell.png

VS Code will appear. Open the Terminal Ctrl+Shift+`

Now, ask the CLI to create an Workspace, but with no default application:

ng new –createApplication=false

In the prompt give your workspace a name, like my-workspace

2020-03-09 10_46_33-CLIPlayGround - Visual Studio Code.png

It’s take a few minutes to set up. If you open Windows Explorer you’ll see the new workspace made, but without any application inside:

2020-03-09 10_47_29-my-workspace.png

Let’s now add a few applications by using the commands in the new folder

cd .\my-workspace\

ng g application

Run this twice and you’ll then notice there are 2 applications in your workspace: under a new Projects folder

2020-03-09 10_50_11-CLIPlayGround - Visual Studio Code.png

 

 

#Angular and Asp.Net 3 Core 3: Creating up to date documentation

imagesAfter you create a new Asp.Net Core 3 Angular project, you’ll want to add documentation. Keeping it up to date is always a chore, but with some new tools, that can be made much easier.

We’re going to go over CompoDoc, a free tool to create and update Angular Documentation.

2019-11-12 11_15_10-Window.png

The first step is to install it into your Asp.Net Core 3 Angular app. Open up the ClientApp folder in a Terminal and enter this:

npm install --save @compodoc/compodoc

Once done, go to your package.json file and add the compodoc script to the script’s node:

2019-11-12 11_19_34-PluralsiteAngularRouting - Microsoft Visual Studio.png

To generate the docs, just run this in the same terminal:

 npm run compodoc

It will generate a ClientApp/documentation folder. Open the resulting index.html to view your new shiny docs.

Re-run the compodoc script to update as needed.

2019-11-12 11_23_01-Window.png

 

 

#Angular and Asp.Net Core 3: Using a real-fake backend for prototyping and testing.

2019-11-11 12_59_09-Window.png

Real-Fake backend. Yah, I know, it sounds weird.

I’m in a situation where I want my Angular code to post to a real HTTP backend and return some values, but I don’t want to wait/create a real Asp.Net Core 3 backend to test or wire up selenium. I need a really fast turn around in a prototype. In this article that’s what we’ll do.

I’ll use Angular for this post, but React or Vue, etc would work just fine.

I’ll assume you have a little bit of an Angular background, but it’s not needed too much to get the idea.

In my component I have a form submit handler wired up and a data service stub as well:

export class UserSettingsFormComponent implements OnInit {

  constructor(private dataService: DataService) {}

  onSubmit(form: NgForm) {
    this.dataService.postUserSettingsForm(this.userSettings).subscribe(
      result => console.log('success', result),
      error => console.log('error: ', error)
    );
  }

My data service just returns what is posted:

export class DataService {
  constructor(private http: HttpClient) { }

   postUserSettingsForm(userSettings: UserSettings): Observable<any> {
       return this.http.post('https://putsreq.com/qYVARlIpT0IiOUSAmp0a', userSettings);
  }
}  

What’s the funny-looking¬†URL? Here’s the magic. Go to https://putsreq.com and tap the Create a PutsReq button.

2019-11-11 12_53_58-Window.png

Then in the screen that appears,¬† add this to the “Response Builder and press Update:

var parsedBody = JSON.parse(request.body);
parsedBody.id='MyIdIsBest';
response.body=parsedBody;

Now when we run our Angular form and post the value, PutsReq will execute our request and return what we indicated.

With this we can quickly edit and experiment with posting and modifying a response from our Angular app.

Here’s what the PutsReq page will show as Requests from your app come in:

2019-11-11 12_57_47-qYVARlIpT0IiOUSAmp0a.png