Category: Angular

Db2Code–part 7 – Angular vs React

Once you have the REACT implementation, you will think that Angular implementation will be a breeze. Not . Some pain points and differences :

1.  Angular have the power of separating components into html code and JS/TS code. While this is a feature for the developer, it becomes hard for code generators  to generate 2 different files .So it will be one file and the component will have template instead of templateURL  React, instead , is just one file/function to generate .

2.  REACT can load dynamic function with just import  the js/ts file- since all HTML in JS/TS is basically JS . To  import dynamic in Angular is a hard  – read https://angular.io/guide/dynamic-component-loader 

3. In the same idea , the Angular components must be registered into an NgModule – or make them standalone :https://angular.io/guide/standalone-components . Anyway, this affects the DI – a hard thing to do . In REACT, just import them ( and yes, framework vs library – but I wish in Angular to be easier )

4. In REACT we have the useState – in Angular there will be Signals.  I wait – the code will be prettier .

5. REACT is more ugly – with all callbacks and memos. Angular seems prettier.

6. The DI in Angular is awesome . Just decorate what you need with @Injectable

The final result is at https://marketplace.visualstudio.com/items?itemName=ignatandrei.databasetocode

TILT- Telemetry/Observability for FE and BE-part 27

Now it is the case to monitor the calls how they arrive from Web ( frontend ) and continue to the backend.

Fortunately, it is very simple in Application Insights

There are 2 relevant links for Angular

https://devblogs.microsoft.com/premier-developer/angular-how-to-add-application-insights-to-an-angular-spa/

https://learn.microsoft.com/en-us/azure/azure-monitor/app/javascript-angular-plugin

https://learn.microsoft.com/en-us/azure/azure-monitor/app/javascript?tabs=snippet#enable-distributed-tracing

The code is relative well written and appears ok. The fact that the AppInsights SDK is on the environment.ts is somewhat disturbing – but there is a way to circumvent it – see https://learn.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings .

So now a large view of the calls looks like

And, if you want to see all calls

TILT-Count TILTS for each user-part 27

I just wanted to know for each user how many tilts have each user/ url . As a Business Requirement , it is not a big deal. Let’s see what it means for a programmer .

1. Add functions to the backend to calculate the count

2. Add to the frontend call to the function

3. Figure where in the frontend this information must be shown ( yes, Business Analysis is here )

4. Display and correct eventual errors.

Those are the files modified ( .cs for backend, other for backend)

src/backend/Net6/NetTilt/NetTilt.Logic/PublicTILTS.cs
src/backend/Net6/NetTilt/NetTilt/NetTilt.WebAPI/Controllers/PublicTILTsController.cs
src/frontend/AngTilt14/src/app/app.module.ts
src/frontend/AngTilt14/src/app/one-public-tilt/one-public-tilt.component.html
src/frontend/AngTilt14/src/app/one-public-tilt/one-public-tilt.component.spec.ts
src/frontend/AngTilt14/src/app/one-public-tilt/one-public-tilt.component.ts
src/frontend/AngTilt14/src/app/public-tilts/public-tilts.component.html
src/frontend/AngTilt14/src/app/public-tilts/public-tilts.component.spec.ts
src/frontend/AngTilt14/src/app/public-tilts/public-tilts.component.ts
src/frontend/AngTilt14/src/app/public-tilts/publicTilt.ts
src/frontend/AngTilt14/src/app/services/public-tilts.service.ts

( this list was obtained with git diff  cd75105 8e0ba8c –name-only )

And this is without counting the thinking of when this data should be obtained. You can see the end result at https://tiltwebapp.azurewebsites.net/AngTilt/tilt/public/ignatandrei

TILT-Typed Reactive Forms and arrays–part 26

I want now to pass to typed reactive forms ( with Angular 14 )- what is good is that are typed , i.e. I can see errors if some property is not correct.

The code previously was

profileForm = this.fb.group({
    url: [''],
    publicTILTS: this.fb.array([])
  });

Now the code looks like

profileForm =  new FormGroup({
    url: new FormControl(''),
    publicTILTS: new FormArray([new FormControl(new TILT())])
  });

And you may ask – what is the difference ? The difference is type inferring from generics. For example , the constructor for FormControl ( after several steps, since it is a interface)

new <T = any>(value: FormControlState<T> | T  //more code

So the above definition is , actually, this one:

profileForm =  new FormGroup({
    url: new FormControl<string>(''),
    publicTILTS: new FormArray<FormControl<TILT|null>>([new FormControl<TILT>(new TILT())])
  });

Now the other modifications are

  1. Get rid of formArray

  2. typed data in HTML :
    <div  *ngFor="let item of profileForm.value.publicTILTS; let i = index;let f=first"> 
  3. modify the string url It was hardcoded:
  tap(it => this.profileForm.controls['url'].setValue(it)),

Now it is typed patchValue that has a Partial – typed!

tap(it => this.profileForm.patchValue({url:it}))

Just technical, the relevant definitions are

patchValue(value: ɵFormGroupValue<TControl>, options?: {
        onlySelf?: boolean;
        emitEvent?: boolean;
    }): void;

export declare type ɵFormGroupValue<T extends {
    [K in keyof T]?: AbstractControl<any>;
}> = ɵTypedOrUntyped<T, Partial<{
    [K in keyof T]: ɵValue<T[K]>;
}>, {
    [key: string]: any;
}>;
  1. Modified the array – patchValue does not work with arrays, so setControl to the rescue

Now it is

//this do not work - do not modify the array length, but the contents
// this.profileForm.patchValue({publicTILTS: [...it]});
//setControl<K extends string & keyof TControl>
this.profileForm.setControl("publicTILTS",new FormArray(it.map(a=>new FormControl(a))));        

Again it is typed , even if it seems a string- I have put a comment above with the definition – see keyof ? It will raise an error at compile time!

For the end user, the site is THE SAME. However, the code now is typed – and , yes, this is a great deal for programmer!

TILT-Passing to IAsyncEnumerable instead of array–part 25

When tansmitting an array of data to an application , usually the application transfers all data – or in chunks – if it is a large numbers ( page 1, page 2 and so on). Both are having drawbacks

  • all data from start means that the time to see the data increases with the number of data
  • paging the data means to show the user the number of pages and an action from the user to go further to the next page.

What if I can use IASyncEnumerable to push data one by one to the GUI ?

So – those are the steps for the backend (.NET ) and frontend( Angular ) to pass the transfer from array to one by one ( IASyncEnumerable)

Backend

I have a function that loads the data from database and transforms into an array . Also caches the data

private async Task<TILT_Note_Table[]?> LatestTILTs(string urlPart, int numberTILTS){
    if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
    {
        return result;
    }
    //find id from urlPart - if not found , return null
    //caches the data and returns the array
}

Also a controller that sends the data

 public async Task<ActionResult<TILT_Note_Table[]?>> LatestTILTs(string urlPart, int numberTILTS, [FromServices] ISearchDataTILT_Note searchNotes)
{
    var data = await publicTILTS.LatestTILTs(urlPart,numberTILTS);
    if (data== null)
    {
        return new NotFoundObjectResult($"cannot find {urlPart}");
    }
    return data;
}

Also some tests that verifies that when I post a new tilt, the numbers is 1

Step 1: transform from array to IASyncEnumerable

transformation of the main function

 private async Task<IAsyncEnumerable<TILT_Note_Table>?> privateLatestTILTs(string urlPart, int numberTILTS)
        {
            if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
            {
                return result.ToAsyncEnumerable();// modification here
            }
    //find id from urlPart - if not found , return null
    //caches the data and returns the array.ToAsyncEnumerable();
        }

transformation of the controller- just add IAsyncEnumerable

 public async Task<ActionResult<IAsyncEnumerable<TILT_Note_Table[]>?>> LatestTILTs(string urlPart, int numberTILTS, [FromServices] ISearchDataTILT_Note searchNotes)
    {
        var data = await publicTILTS.LatestTILTs(urlPart,numberTILTS);
            return new NotFoundObjectResult($"cannot find {urlPart}");
        }
        return Ok(data);
    }

Also for the tests you can add .ToArrayAsync()

Step 2: Get rid of the Task

So now the obtaining of TILTS looks like this

private async IAsyncEnumerable<TILT_Note_Table> privateLatestTILTs(string urlPart, int numberTILTS)
{
    if (cache.TryGetValue<TILT_Note_Table[]>(urlPart, out var result))
    {
        //why I cant return result.ToAsyncEnumerable() ?
        await foreach (var item in result.ToAsyncEnumerable())
        {
            await Task.Delay(1000);
            yield return item;
        }
    }
    //same with retrieving data
    //when sending back data, we have to send one by one , as for the caching

The controller looks pretty much the same

[HttpGet("{urlPart}/{numberTILTS}")]
public ActionResult<IAsyncEnumerable<TILT_Note_Table>> LatestTILTs(string urlPart, int numberTILTS, [FromServices] ISearchDataTILT_Note searchNotes)
{
    var data =  publicTILTS.LatestTILTs(urlPart,numberTILTS);
    
    if (data== null)
    {
        return new NotFoundObjectResult($"cannot find {urlPart}");
    }

    return Ok(data);
}

And this is the modification for the backend . If you want to see in action , open your browser to https://tiltwebapp.azurewebsites.net/api/PublicTILTs/LatestTILTs/ignatandrei/100000 and see TILTS how they come one by one

Frontend

In Angular I have obtained the whole array at once .

 public getTilts(id:string, nr:number): Observable<TILT[]>{
    return this.http.get<TILT[]>(this.baseUrl+'PublicTILTs/LatestTILTs/'+id + '/'+nr)
    .pipe(
      tap(it=>console.log('received',it)),
      map(arr=>arr.map(it=>new TILT(it)))
    )
    ;
 }

Now we are obtaining one by one – how to let the page knows that it has an array instead of modifying also the page ?

( Yes, the display could be modified to accumulate – but I want minimal modifications to the page)

To obtain one by one we display the fetch

 //https://gist.github.com/markotny/d21ef4e1af3d6ea5332b948c9c9987e5
  //https://medium.com/@markotny97/streaming-iasyncenumerable-to-rxjs-front-end-8eb5323ca282
  public fromFetchStream<T>(input: RequestInfo, init?: RequestInit): Observable<T> {
    return new Observable<T>(observer => {
      const controller = new AbortController();
      fetch(input, { ...init, signal: controller.signal })
        .then(async response => {
          const reader = response.body?.getReader();
          if (!reader) {
            throw new Error('Failed to read response');
          }
          const decoder = new JsonStreamDecoder();
          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            if (!value) continue;
            decoder.decodeChunk<T>(value, item => observer.next(item));
          }
          observer.complete();
          reader.releaseLock();
        })
        .catch(err => observer.error(err));
      return () => controller.abort();
    });
  }

And for modifying the function to have an array, instead of just one, RXJS scan to the rescue

 public getTilts(id:string, nr:number): Observable<TILT[]>{
    return this.fromFetchStream<TILT>(this.baseUrl+'PublicTILTs/LatestTILTs/'+id + '/'+nr)
    .pipe(
      tap(it=>console.log('received',it)),
      map(it=>new TILT(it)),
      scan((acc,value)=>[...acc, value], [] as TILT[])
    );  
}

You can see the final result ( with 1 sec delay between tilts, to be visible ) here http://tiltwebapp.azurewebsites.net/AngTilt/tilt/public/ignatandrei

TILT–Details for programmers- part 24

I have organized the About in order to show more details. See https://tiltwebapp.azurewebsites.net/AngTilt/

Zero and the most important, the date when the CI was done

First , Licences – .NET Core and Angular . Usefull to know.

Second, Info about Versions – Repo and history – UI and JSON – mostly for making managers happy .

Third, Automation – Swagger and Blockly Automation – in order for others to try how to interact.

Fourth , Info about deployment – HealthCheck and info about deployment Environment – user, environment, error – for SRE .

And to have something new, this is the map of the API’s

obtained with the NetCoreUsefullEndpoints ( https://tiltwebapp.azurewebsites.net/api/usefull/graph/text ) – and digraph rendering https://dreampuf.github.io/GraphvizOnline/

Tools Used

Visual Studio

Visual Studio Code

https://github.com/ignatandrei/RSCG_AMS

https://github.com/ignatandrei/NetCoreUsefullEndpoints/

https://github.com/ignatandrei/blocklyAutomation/

https://github.com/domaindrivendev/Swashbuckle.AspNetCore

TILT–Some improvements-part 23

I wanted to have a share on TILTs, instead of a clipboard copy. Discovered that browser navigator has a native share – https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share .

Implementation very simple ( not so good, )

private share(str:string): boolean{
if ('share' in navigator) {
    navigator
    .share({
    title: 'TILT!',
    text: str,
    url: environment.url + 'AngTilt/tilt/public/'+this.profileForm.controls['url'].value
    })
    .then(() => {
    console.log('Callback after sharing');
    })
    .catch(console.error);
    return true;
} else {
    return false;
}
}
  1. Added .NET Tools to have data about the outdated and more
feat: dot net tools
e0fd82f
{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-project-licenses": {
      "version": "2.4.0",
      "commands": [
        "dotnet-project-licenses"
      ]
    },
    "dotnetthx": {
      "version": "0.2.0",
      "commands": [
        "dotnet-thx"
      ]
    },
    "powershell": {
      "version": "7.2.6",
      "commands": [
        "pwsh"
      ]
    },
    "dotnet-depends": {
      "version": "0.6.1",
      "commands": [
        "dotnet-depends"
      ]
    },
    "dotnet-outdated-tool": {
      "version": "4.1.0",
      "commands": [
        "dotnet-outdated"
      ]
    },
    "run-script": {
      "version": "0.4.0",
      "commands": [
        "r"
      ]
    }
  }
}

Tools used

Visual Studio

Visual Studio Code

CLI

TILT-Angular 14 and small updates-part 20

It is always good to have the latest version of NuGet packages – if it does not break the application, of course. Easy task when you have some automated test -and Visual Studio is doing this for you.

However, to update an Angular application where the version is fixed – a nightmare. I will do in a full time.

Also, being deployed to Azure, a few clicks on AppInsights and you have monitoring the application without instrumenting the code.

Also, added a feature to the application – you can add now a link to your TILT. This small modification went also when to display the TILT – keep remembering that any modification that you will do to the model will reflect in more than 1 place in the GUI.

Also, modifying the interface – show first the TILTs in a list, rather than in a calendar format.

Now the Angular 14 is on market – see https://blog.angular.io/angular-v14-is-now-available-391a6db736af. I was impressed by Title and NgMOdel onPush

So – update the cli ( npm update -g @angular/cli ) and crating a new app (ng new ) and adding Angular (ng add @angular/material)

So now it is time to bring the code to

  1. Fix the versions

  2. Add the code from the Ang13 app

  3. Add the title

For 1:

Fix the versions in package.json

Delete the npm_modules

npm i

For 2: I use WinMerge with a custom filter to exclude node_modules

Added missing npm packages from the previous application

Fix again the versions

For 3:

This is the code for TILT:

import { Injectable, NgModule } from '@angular/core';
import {
  RouterModule,
  RouterStateSnapshot,
  Routes,
  TitleStrategy,
} from '@angular/router';
import { LoginUrlGuard } from './login-url.guard';
import { LoginUrlComponent } from './login-url/login-url.component';
import { MyTiltComponent } from './my-tilt/my-tilt.component';
import { OnePublicTiltComponent } from './one-public-tilt/one-public-tilt.component';
import { PublicTiltsComponent } from './public-tilts/public-tilts.component';
import { TiltMainComponent } from './tilt-main/tilt-main.component';

const routes: Routes = [
  {
    path: 'tilt/public',
    component: PublicTiltsComponent,
    title: 'List of public tilts',
  },
  { path: 'tilt/public/:id', component: OnePublicTiltComponent },
  { path: '', redirectTo: '/tilt/public', pathMatch: 'full' },
  {
    path: 'tilt/my',
    component: MyTiltComponent,
    canActivate: [LoginUrlGuard],
    title: 'My tilts',
  },
  {
    path: 'loginURL',
    component: LoginUrlComponent,
    title: 'Login to add a tilt',
  },
];

@Injectable()
export class TemplatePageTitleStrategy extends TitleStrategy {
  override updateTitle(routerState: RouterStateSnapshot) {
    const title = this.buildTitle(routerState);
    if (title !== undefined) {
      document.title = `TILT! - ${title}`;
    } else {
      var arr = routerState.url.split('/');
      if(arr.length==0)
        document.title = `TILT! - AAA`;
      else
        document.title = `TILT! - tilts for ` + arr[arr.length-1];
    }
  }
}

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [{provide: TitleStrategy,  useClass: TemplatePageTitleStrategy}],
})
export class AppRoutingModule {}

Tools used

npm

ng

Visual Studio Code

WinMerge

Windows terminal + Powershell to run IDE, Angular, .NET Core

I work at http://github.com/ignatandrei/BlocklyAutomation – and every time I need to run Angular , Visual Studio Code IDE and .NET Core run ( and Visual Studio  sometimes  – it is more web based)

Also, sometimes I need to run in a container – to install globally something that I do not want to have in my PC.

So  I have 1 windows terminal command to run those :

First one

wt new-tab   –title RootSource –suppressApplicationTitle -p “Windows PowerShell” -d . cmd /k “cd src && code . && powershell” ;split-pane  –title Angular –suppressApplicationTitle -V -p “Windows PowerShell” -d . cmd /k “cd src &&  npm run start –watch && powershell” ;split-pane  –title LocalAPI_NetCore –suppressApplicationTitle -V -p “Windows PowerShell” -d . cmd /k “cd src/Local/LocalAPI/LocalAPI && dotnet watch run”

As you see , I have

code .

to run VSCode,

npm run start –watch

to run Angular and

dotnet watch run

to run dotnet.

Second one:

wt new-tab -p “Windows PowerShell” -d . ;split-pane -p “Windows PowerShell” -d . cmd /k “cd src && devcontainer open .”

The second it just works with devcontainer  – if you are interested in sources, see https://github.com/ignatandrei/BlocklyAutomation/tree/main/src/.devcontainer 

With those, I can start my developer environment fast ( how fast, that depends on my PC)

Developer choices Web => Windows application

So I have an Angular application – basically, a macro for your WebAPI – see https://ignatandrei.github.io/BlocklyAutomation/ . I want to transform into an Windows Application and have also deployed to Windows Store.

Attempt 1 : embed as a resource a .NET Core WebAPP and deploy to Windows Store. Not really –  the validation part of Store realizes this trick

Attempt2 : Electron – must rewrite window.prompt : https://github.com/electron/electron/issues/472

Attempt3 : Let’s see Blazor – https://github.com/dotnet/MobileBlazorBindings/  – however , it has just WPF, not UWP .

Attempt3: WebView2 –  https://github.com/MicrosoftEdge/WebView2Samples . It has an UWP. However, I realize that it does not have redirect … so routes in Angular will be difficult – if not intercepting 404 and serving the same index.html.

Attempt4: PWA : https://docs.microsoft.com/en-us/windows/uwp/publish/pwa/overview – yes, but my app it was Angular 12. Pass to 13 ( and force some unresolved dependencies ) take me the rest of the  day .

So I am at PWA now.

And Angular with https://www.pwabuilder.com/ is pretty impresive

What I do not have yet: ASP.NET Core downloading as Windows Store application with PWA and Controllers …

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.