Posts for Tag: angular

F# - Web applications with Angular and Giraffe

tl;dr - If you want a barebones .NET web app written in F# with Angular and Giraffe all hooked up already then clone my giraffe-ng repo on GitHub as a starting point. If you want to know the steps involved in setting this up, then read on.

The Angular CLI is a powerful and easy way to build rich web applications. However in some cases you might want to use something other than the NodeJS backend that it provides by default. This is a little guide to show how an Angular application can be created with a backend powered by F# using the Giraffe framework

To begin with we need to ensure that Angular CLI and .NET Core SDK 2.2 are installed and in our PATH:

$ dotnet --version
2.2.101
$ ng --version
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 7.0.4
Node: 9.11.2
OS: linux x64
Angular: 
... 

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.10.4
@angular-devkit/core         7.0.4
@angular-devkit/schematics   7.0.4
@schematics/angular          7.0.4
@schematics/update           0.10.4
rxjs                         6.3.3
typescript                   3.1.3

First create the folder, the  .NET project and install the Giraffe and Microsoft.AspNetCore.App packages:

  $ mkdir giraffe-ng
  $ cd giraffe-ng 
  $ dotnet new console -lang F#
  $ dotnet add package Giraffe --version 3.4.0
  $ dotnet add package Microsoft.AspNetCore.App --version 2.2.0 

We'll now create a simple landing page using Giraffe's own view engine just to test everything is working.

  let index = 
      html [] [
          head [] [
              title [] [ str "Giraffe!" ]
          ]
          body [] [
              h1 [] [ str "Hello!" ]
              p [] [ str "A test of Giraffe and .NET Core"]
          ]
      ]
  
  let webApp =
      choose [ route "/" >=> (index |> renderHtmlDocument |> htmlString) ]
  
  let configureApp (app : IApplicationBuilder) =
      app.UseGiraffe(webApp)
               
  let configureServices (services : IServiceCollection) =
      services.AddGiraffe() |> ignore
  
  []
  let main _ =
      WebHostBuilder()
          .UseKestrel()
          .Configure(Action configureApp)
          .ConfigureServices(configureServices)
          .Build()
          .Run()

We can test that this works nicely by going to http://localhost:5000 - the reason the port is important will be apparent later:

Looks good, so now we can setup our frontend, so we'll use the Angular CLI:

$ ng new frontend

And we'll check out http://localhost:4200 to make sure it's working:

What we want to do is take the Angular application and serve it with out F#\Giraffe app via the route "/app" - so that viewing http://localhost:5000 will serve the Angular app that was previously served by NodeJS on http://localhost:4200. We'll start off by making sure our application is setup to find all its resources under the /app route.

$ ng build --base-href /app/
We'll next change our app so that it'll serve this route:
let configureApp (app : IApplicationBuilder) =
  app.UseStaticFiles(
      StaticFileOptions(
              FileProvider = new PhysicalFileProvider(
                      Path.Combine(Directory.GetCurrentDirectory(), "frontend", "dist")),
                      RequestPath = PathString("/app")))
      .UseGiraffe(webApp)

We now need to make sure the files in the frontend/dist directory is copied to the .NET application's bin directory when it's built by adding the following to our .fsproj file:
  <ItemGroup>
    <Content Include="frontend/dist/*.*">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

Next we can change the definition of our index view so that our Angular scripts and application root are loaded:

  let ngApp = tag "app-root"  [] []
  
  let ngScripts = 
      [ "runtime.js"; "polyfills.js"; "styles.js"; "vendor.js"; "main.js" ]
      |> List.map (function js -> script [ _type "text/javascript"; _src js ] [] )
  
  let index = 
      html [ _lang "en" ] [
          head [] [
              meta [ _charset "utf-8"; _name "viewport"; _content "width=device-width, initial-scale=1" ] 
              title [] [ str "Angular + Giraffe" ]
              ``base`` [ _href "/app/" ]
              link [ _rel "icon"; _type "icon"; _href "favicon.ico" ]
          ]
  
          body [] 
              (ngApp :: ngScripts)
      ]

Now if we run the Giraffe app and navigate to http://localhost:5000 we should see our Angular application:

And there we have it - we set up an application with an F# and Giraffe powered backend and an Angular 7 frontend. Extending this to have the Angular app fetch data from a Giraffe service is pretty straight-forward. As an example if we want our page to display a list of users it has requested from the backend, we can add the following to our Program.fs:

type User = { login:string; email:string }

let userList:(User list) = [ 
  { login = "sean"; email = "sean@example.com" }
  { login = "lucka"; email = "lucka@example.com" }
  { login = "alfie"; email = "alfie@example.com" }
  { login = "ivy"; email = "ivy@example.com" }
]

Then we can add a new “users” route to our webapp to serve this data:

let webApp =
  choose [
      route "/"       >=> (index |> renderHtmlDocument |> htmlString) 
      route "/users" >=> (json userList)
  ]

Then in our main app.component.ts we'll add a type to represent the User

interface User {
    login: string;
    email: string;
}

And we can now modify our AppComponent so that it hits this service

  export class AppComponent implements OnInit {
    title = 'frontend';
  
    public users: User[] = [];
  
    constructor(private http: HttpClient) {
    }
  
    ngOnInit(): void {
      this.http.get('/users').subscribe(users => {
        this.users = users;
      });
    }
  }

And we can modify the component's template to render this

<ul>
    <li *ngFor="let user of users">
      <a href="mailto:{{user.email}}">{{user.login}}</a>
    </li>
</ul>
So there we are, an Angular frontend served by Giraffe and F#! Just for reference the completed app is available on GitHub under my "giraffe-ng" repo.