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

Licences for .NET Core and Angular–part 24

I was curious about the licences that .NET Core and Angulare are using.

It was interesting to find that ng build (https://angular.io/cli/build) has a –extract-licenses flag- and creates 3rdpartylicenses.txt

For .Net Core I have found https://github.com/tomchavakis/nuget-license that creates a file with

dotnet dotnet-project-licenses -i NetTilt\NetTilt.WebAPI -o –outfile NetTilt\NetTilt.WebAPI\wwwroot\netcorelicences.txt -t

And I copy those , in the CI , to the root of the site.

You can see the final result at https://tiltwebapp.azurewebsites.net/AngTilt/about – the number of licenses is overwhelming.

Angular : https://tiltwebapp.azurewebsites.net/3rdpartylicenses.txt

( and it is missing core dependencies of Angular ….) NetCore : https://tiltwebapp.azurewebsites.net/netcorelicences.txt


Tools used

VS

VSCode

https://angular.io/cli/build

https://github.com/tomchavakis/nuget-license

TILT- Docker with Ductus.FluentDocker–part 23

I have already tests with Sqlite – however, it will be better to try tests with a real SqlServer .

One of the way to have a sql server is to have docker – but how to start docker with sql server any time ?

One of answers is Ductus.FluentDocker – https://www.nuget.org/packages/Ductus.FluentDocker – and this is the code to start SqlServer:

public override void StartDatabase()
{
    //string guid = Guid.NewGuid().ToString("N");
    string uniqueId = Interlocked.Increment(ref uniq).ToString(); //Guid.NewGuid().ToString("N");
    container =
new Builder()
.UseContainer()
.WithName("sql" + uniqueId)
.UseImage("mcr.microsoft.com/mssql/server:2022-latest")
.ExposePort(1433, 1433)
.WithEnvironment("SA_PASSWORD=<YourStrong@Passw0rd>", "ACCEPT_EULA=Y")
.WaitForMessageInLog("Starting up database 'tempdb'.", TimeSpan.FromSeconds(30))
.Build()
.Start();
    ConstructServiceProvider();

}
static int uniq = 0;

I needed also a Base class for consolidating code between sql server and sqlite

  1. generating DI with for both with different context

  2. The Steps are the same = so base class

  3. The tests are the same= so base class

So this is the base class:

public  abstract partial class RealDBTests: FeatureFixture
{
    [SetUp]
    public void Start()
    {
        StartDatabase();
    }
    [TearDown]
    public void Stop()
    {
        StopDatabase();
    }

    public abstract void StartDatabase();

    public abstract void StopDatabase();

    public abstract IServiceCollection AddDB( IServiceCollection sc);


    public void ConstructServiceProvider()
    {
        serviceProvider = AddDB(new ServiceCollection())
//more DI
    }
}

Also , GitHub actions supports docker – so now TILT has a complete testing also in SqlServer.

Tools used

Docker

Visual Studio

Ductus.FluentDocker

Friday links 490

 

  1. ethereal.email
  2. trekhleb/javascript-algorithms: Algorithms and data structures implemented in JavaScript with explanations and links to further readings
  3. future-architect/cheetah-grid: The fastest open-source data table for web.
  4. Reverse Engineering – EF Core | Microsoft Docs
  5. Source generator updates: incremental generators: Exploring .NET 6 – Part 9
  6. runtime/LoggerMessageGenerator.Roslyn4.0.cs at v6.0.0-rc.2.21480.5 · dotnet/runtime
  7. roslyn/incremental-generators.md at main · dotnet/roslyn
  8. Roslyn Quoter
  9. Database Providers – EF Core | Microsoft Docs
  10. 7.2.2 Scaffolding an Existing Database in EF Core
  11. Reverse Engineering – EF Core | Microsoft Docs
  12. Reverse Interviewing Your Future Manager and Team – The Pragmatic Engineer
  13. Don’t put data science notebooks into production
  14. What I wish I had known about single page applications – Stack Overflow Blog
  15. 15 Free Hosting Providers for Web Developers | by Niemvuilaptrinh | Dec, 2021 | Bits and Pieces
  16. Chrome Extension with Angular — from zero to a little hero
  17. (6) Andy Budd on Twitter: “A typical website visit in 2022 1. Figure out how to decline all but essential cookies 2. Close the support widget asking if I need help 3. Stop the auto-playing video 4. Close the “subscribe to our newsletter” pop-up 5. Try and remember why I came here in the first place” / Twitter
  18. Image Slider – Splide
  19. Automate Blockly – https://github.com/ignatandrei/BlocklyAutomation
  20. irbigdata/data-dockerfiles: a curated list of docker-compose files prepared for testing data engineering tools, databases and open source libraries.

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

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.