Giraffe: Changes to Razor files don't seem to come through when the app is running.

Created on 4 Apr 2017  路  9Comments  路  Source: giraffe-fsharp/Giraffe

When editing the Index.cshtml while the app is running, no changes seem to have any effect. The html output stays the same.

Steps to reproduce

  • Create a new app with dotnet new giraffe
  • Edit the Index.cshtml by adding some styling
@model yellow.Models.Message

<!DOCTYPE html>
<html>
<head>
    <title>Giraffe</title>
    <link href="https://fonts.googleapis.com/css?family=Lato:300" rel="stylesheet">
    <style>
        body {
            background-color:#f1c40f;
            color:#2c3e50;
            font-family: 'Lato', sans-serif;
        }

        h3{
            font-size: 4rem;
            font-weight: 300;
            text-align: center;
            /*border:1px solid blue;*/
        }
    </style>
</head>
<body>
    <div>
        <h3>@Model.Text</h3>
    </div>
</body>
</html>
  • run the app
  • uncomment the css
  • reload browser

The title doesn't seem to have a border.
I'm not sure if this is a Razor Engine issue or caching inside Giraffe.

feature request

All 9 comments

Hi, thanks for reporting this. That is an interesting issue, not sure how to fix it though because after some initial investigation it seems like it is a level of caching in the razor engine. I'll have a closer look over the next few days and see what options I have to provide a better experience.

I don't think it's the razor. I noticed similar problems with the htmlEngine as well.
If the razorView has a different model, the page does get a proper refresh.

let getText () =
    let guid = Guid.NewGuid().ToString()
    sprintf "Hello, Giraffe world! %s" guid 

let webApp = 
    choose [
        GET >=>
            choose [
                route "/" >=> razorView "Index.cshtml" { Text = getText () } ;
            ]

Is this an F# thing that if you call functions without arguments or without different argument values the result of the function is cached? Because I'm guessing in strict way calling the function with the same arguments should return the same value.

Are we missing a warbler function somewhere?

Hi, I think it is both. In the case of the html engine you need a warbler, because functions in F# are eagerly evaluated and in the case of razor a warbler will help to evaluate the function every time, but the razor engine will still compile a view only once and changing the cshtml while the application is running will not have an effect.

Try this example:

let warbler f a = f a a

let getName() = Guid.NewGuid().ToString()

let webApp = 
    choose [
        GET >=>
            choose [
                route  "/razor"  >=> razorHtmlView "Person" { Name = getName() }
                route  "/razor2" >=> warbler (fun _ -> razorHtmlView "Person" { Name = getName() })
            ]
        setStatusCode 404 >=> text "Not Found" ]

When you run this application then /razor will always return the same page, no matter what. In contrast /razor2 will return a different GUID every time, but both routes will not display any changes made to the cshtml while the app is running.

You're right. The warbler solves the execution issue but razor will still cache the changes.
Perhaps the razor issue should be tackled by configuring DotNetWatch to restart if a *.cshtml changes.

Today I also learned that the html engine should be a function instead of a value.

let index =
    let currentTime = DateTime.Now.ToString("HH:mm:ss.ffffzzz")
    let text = sprintf "Hello %s" currentTime
    html [] [
        head [] []
        body [] [
            h3 [] (rawText text)
        ]
    ]

let indexTwo() =
    let currentTime = DateTime.Now.ToString("HH:mm:ss.ffffzzz")
    let text = sprintf "Hello %s" currentTime
    html [] [
        head [] []
        body [] [
            h3 [] (rawText text)
        ]
    ]

Even with the warbler, the first index result will only be executed once. Which makes sense.

Yeah that's correct. So to sum this up, what do we think are the actions to take away here?

  • Extend the SampleApp with the DotNetWatch to restart on *.cshtml changes
  • Add a warbler function to the API
  • Document that the HtmlEngine views should be functions when it should not be static

Anything else?

Sounds about right. I'll see what I can do and try to come up with a pull request.

Hi, so this seems to work, even without a warbler, because it restarts the app on every cshtml change. You must start the app with dotnet watch run though for it to work.

Indeed, you only need the warbler if your model is dynamic.
Ex:

[<CLIMutable>]
type Person =
    {
        Name : string;
        CurrentTime: DateTime;
    }
route  "/razor"      >=> razorHtmlView "Person" { Name = "Razor"; CurrentTime = DateTime.Now }
@model SampleApp.Models.Person
@{ 
    Layout = "_Layout";
}

<div>
    <h3>Hello, @Model.Name</h3>
    <p>Current time: @Model.CurrentTime</p>
</div>
<div>
    @{
        await Html.RenderPartialAsync("Partial");
    }
</div>

Thanks for helping me with this project so much! I think this issue can be closed now :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nicolocodev picture nicolocodev  路  4Comments

forki picture forki  路  8Comments

Lanayx picture Lanayx  路  4Comments

graforlock picture graforlock  路  5Comments

dustinmoris picture dustinmoris  路  5Comments