Franklin.jl: iterating over elements of a vector in HTML

This is my Franklin.jl-powered website. I have several mathematical simulation pages and while they do differ in some ways, a lot of their code is more or less shared and I am trying to reduce the redundancy between them. This is the original HTML code of my Chen attractor article:

<!DOCTYPE html>
<html>
    <head>
        <title>Chen equations solver</title>
        <!--We need a few external JS libraries to provide LaTeX and plot rendering. -->
        {{ insert head_solver.html }}
        <script src="/libs/common/generateTableXYZ.js"></script>
    </head>
    <body>
        <h1>Chen equations solver</h1>
        <div>
            This webpage uses the <a href='/RKF45/' link='_blank'>Runge-Kutta-Fehlberg fourth-order method with fifth-order error checking (RKF45)</a> to approximate the solution to <a href='https://en.wikipedia.org/wiki/Multiscroll_attractor' link='_blank'>Chen equations</a>:

            \[
            \begin{aligned}
            \dfrac{dx}{dt} &= a (y-x) \\
            \dfrac{dy}{dt} &= x(c-a-z) + cy \\
            \dfrac{dz}{dt} &= xy - b z.
            \end{aligned}
            \]

        </div>

        <!--A form for users to enter in all the parameters of the problem-->
        <form name="requiredData">
            <table>
                <tr>
                    <th>Parameter</th>
                    <th>Value</th>
                    <th>Explanation</th>
                </tr>
                <tr>
                    <td><label for="a">\(a\):</label></td>
                    <td><input type="Number"
                        id="a"
                        name="a"
                        value="40"></td>
                    <td>Problem parameter.</td>
                </tr>
                <tr>
                    <td><label for="b">\(b\):</label></td>
                    <td><input type="Number"
                        id="b"
                        name="b"
                        value="3"></td>
                    <td>Problem parameter.</td>
                </tr>
                <tr>
                    <td><label for="c">\(c\):</label></td>
                    <td><input type="Number"
                        id="c"
                        name="c"
                        value="28"></td>
                    <td>Problem parameter.</td>
                </tr>
                <tr>
                    <td><label for="t0">\(t_0\):</label></td>
                    <td><input type="Number"
                        id="t0"
                        name="t0"
                        value="0"></td>
                    <td>Starting time for the simulation in seconds (s).</td>
                </tr>   
                <tr>
                    <td><label for="tf">\(t_f\):</label></td>
                    <td><input type="Number"
                        id="tf"
                        name="tf"
                        value="120"></td>
                    <td>End time for the simulation in seconds.</td>
                </tr>
                <tr>
                    <td><label for="x0">\(x_0\):</label></td>
                    <td><input type="Number"
                        id="x0"
                        name="x0"
                        value="-0.1"></td>
                    <td>Value of \(x\) at \(t_0\).</td>
                </tr>
                <tr>
                    <td><label for="y0">\(y_0\):</label></td>
                    <td><input type="Number"
                        id="y0"
                        name="y0"
                        value="0.5"></td>
                    <td>Value of \(y\) at \(t_0\).</td>
                </tr>
                <tr>
                    <td><label for="z0">\(z_0\):</label></td>
                    <td><input type="Number"
                        id="z0"
                        name="z0"
                        value="-0.6"></td>
                    <td>Value of \(z\) at \(t_0\).</td>
                </tr>
                {{ insert attractor_data.html }}
            </table>
        </form>

        <!--Buttons-->
        {{ insert attractor_button_table.html }}

        <!--Where the table and plot goes-->
        {{ insert attractor_output.html }}
        {{ insert page_foot_general.html }}
    </body>
</html>

I’ve been asking ChatGPT for tips on how to achieve my goal of modularizing this code. I know ChatGPT is far from perfect on this, but I don’t want to pester this discourse too much with my questions and for many questions, I do find ChatGPT sufficient with a bit of trial and error.

I asked, for instance, how do I include HTML code with some parameters. ChatGPT told me that I should use {{ insert:file.html param1=... param2=... }}. I tried this and it failed. The only option was to define parameters in a Markdown file in my Franklin site. So I copy-pasted this code into a Markdown file between ~~~ blocks. This sort of worked, it produced a functioning webpage, but it also introduced some styling bugs (e.g. my table for parameter inputs was originally right aligned and with CSS styling I’ve only managed to get it centre aligned (when I want left alignment)). But one commonality between these webpages is that they all include parameters, but the precise parameters do differ a little between the simulation pages. So I was hoping I could define a vector of strings called “params” in my Markdown file and have:

<tr>
                    <td><label for="{{ params.el}}">\({{ params.el}}\):</label></td>
                    <td><input type="Number"
                        id="{{ params.el}}"
                        name="{{ params.el}}"
                        value="{{ el }}"></td>
                    <td>{{ el_explanation }}</td>
                </tr>

created in my HTML file based on element of params (which I’ve called params.el). The {{ el }} would be from where I add @def el = "VALUE" in my Markdown file. For example, say I had @def params = ["a", "b", "c"] then I would like this added to my Markdown file during rendering:

                  <tr>
                    <td><label for="a">\(a\):</label></td>
                    <td><input type="Number"
                        id="a"
                        name="a"
                        value="{{ a }}"></td>
                    <td>{{ a_explanation }}</td>
                  </tr>
<tr>
                    <td><label for="b">\(b\):</label></td>
                    <td><input type="Number"
                        id="b"
                        name="b"
                        value="{{ b }}"></td>
                    <td>{{ b_explanation }}</td>
                  </tr>
<tr>
                    <td><label for="c">\(c\):</label></td>
                    <td><input type="Number"
                        id="c"
                        name="c"
                        value="{{ c }}"></td>
                    <td>{{ c_explanation }}</td>
                  </tr>

. The values of a, b, c, a_explanation, b_explanation and c_explanation would be defined in my Markdown file, of course. I tried achieving this by adding this to utils.jl

 function render_form(params::Vector{String})
     inputs = join(["<tr><td><label for=\"$p\">\\($p\\):</label></td>\n<td><input name=\"$p\" id=\"$p\" name=\"$p\" value=\"{{$p}}\"/></td>\n<td>{{ $p_explanation }} </td>\n</tr>" for p in params], "\n")
     return "<form>\n$inputs\n</form>"
end

which was based on what ChatGPT suggested. And, of course, I tried to replace the a, b and c parameter input lines of my HTML file with {{ render_form(params) }} with those variable definitions previously mentioned. But it caused serve() of Franklin.jl to crash with this error

⋮ shutting down LiveServer… ✓
ERROR: Franklin.PageVariableError("An error (of type 'UndefVarError') occurred when trying to evaluate 'utils.jl' in a page variable assignment.")
Stacktrace:
 [1] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
   @ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:681
 [2] serve (repeats 2 times)
   @ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined] 
 [3]
   @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
 [4] serve()
   @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
 [5] top-level scope
   @ REPL[1]:1

caused by: Franklin.PageVariableError("An error (of type 'UndefVarError') occurred when trying to evaluate 'utils.jl' in a page variable assignment.")
Stacktrace:
  [1] set_vars!(vars::OrderedCollections.LittleDict{…}, assignments::Vector{…}; isglobal::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:480
  [2] set_vars!
    @ ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:462 [inlined]
  [3] process_mddefs(blocks::Vector{Franklin.OCBlock}, isconfig::Bool, pagevar::Bool, isinternal::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/mddefs.jl:66
  [4] convert_md(mds::String, pre_lxdefs::Vector{…}; isrecursive::Bool, isinternal::Bool, isconfig::Bool, has_mddefs::Bool, pagevar::Bool, nostripp::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:129
  [5] convert_md (repeats 2 times)
    @ ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:23 [inlined]
  [6] process_config()
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:15
  [7] fd_fullpass(watched_files::@NamedTuple{…}, join_to_prepath::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:227
  [8] fd_fullpass
    @ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:210 [inlined]
  [9] fd_loop(cycle_counter::Int64, ::LiveServer.SimpleWatcher, watched_files::@NamedTuple{…})
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:362
 [10] #261
    @ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:136 [inlined]
 [11] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
    @ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:671
 [12] serve (repeats 2 times)
    @ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined]
 [13]
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
 [14] serve()
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
 [15] top-level scope
    @ REPL[1]:1

caused by: UndefVarError: `utils` not defined in `Franklin`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
  [1] top-level scope
    @ :0
  [2] eval
    @ ./boot.jl:430 [inlined]
  [3] eval
    @ ~/.julia/packages/Franklin/X68Y3/src/Franklin.jl:1 [inlined]  
  [4] set_vars!(vars::OrderedCollections.LittleDict{…}, assignments::Vector{…}; isglobal::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:478
  [5] set_vars!
    @ ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:462 [inlined]
  [6] process_mddefs(blocks::Vector{Franklin.OCBlock}, isconfig::Bool, pagevar::Bool, isinternal::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/mddefs.jl:66
  [7] convert_md(mds::String, pre_lxdefs::Vector{…}; isrecursive::Bool, isinternal::Bool, isconfig::Bool, has_mddefs::Bool, pagevar::Bool, nostripp::Bool)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:129
  [8] convert_md (repeats 2 times)
    @ ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:23 [inlined]
  [9] process_config()
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:15
 [10] fd_fullpass(watched_files::@NamedTuple{…}, join_to_prepath::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:227
 [11] fd_fullpass
    @ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:210 [inlined]
 [12] fd_loop(cycle_counter::Int64, ::LiveServer.SimpleWatcher, watched_files::@NamedTuple{…})
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:362
 [13] #261
    @ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:136 [inlined]
 [14] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
    @ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:671
 [15] serve (repeats 2 times)
    @ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined]
 [16]
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
 [17] serve()
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
 [18] top-level scope
    @ REPL[1]:1
Some type information was truncated. Use `show(err)` to see complete types.

And its suggestions for fixing this haven’t really been helpful. So I am hoping someone here may have a solution for this issue. If it helps my config.md does specify @def utils = utils.jl which was also suggested by ChatGPT, as before that my changes to utils.jl had no effect.

Any ideas how to fix this?

1 Like

Is not the best way to get AI help because it’s a how question, rather than a what question. Here is how I’d frame it.

I have a static website produced in the Julia language using the Franklin.jl. Packages. Many of my pages share common elements, which I’ve handled with partials. However, there are variable elements that I want to parameterize so that I can produce more of the content programmatically. As an example, I have a page that produces a bullseye map taking as arguments the name of a city and its longitude and latitude. I have a script that produces an HTML file form the following. What are my options to do this within Franklin with as much of the boilerplate isolated in asset-type files?

using Census
using DataFrames, DataFramesMeta

Capital        = "Boston"
Capital_Coords = "42° 21′ 37″ N, 71° 3′ 28″ W"
include("src/dms_to_decimal.jl")
pal = ("'Red', 'Green', 'Yellow', 'Blue', 'Purple'",
    "'#E74C3C', '#2ECC71', '#3498DB', '#F1C40F', '#9B59B6'",
    "'#FF4136', '#2ECC40', '#0074D9', '#FFDC00', '#B10DC9'",
    "'#D32F2F', '#388E3C', '#1976D2', '#FBC02D', '#7B1FA2'",
    "'#FF5733', '#C70039', '#900C3F', '#581845', '#FFC300'")
centerpoint = dms_to_decimal("$Capital_Coords")
from = Capital
file_path = "_assets/$Capital.html"
bands = "50, 100, 200, 400"
band_colors = pal[4]
bullseye = """
<!DOCTYPE html>
<html>
<head>
  <title>Leaflet Template</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <style>
    body, html {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
    }
    .flex-container {
        display: flex;
        align-items: flex-start;
        width: 100%;
        height: 100%;
    }
    #map {
        flex: 1;
        height: 100vh;
        margin: 0;
    }
    .tables-container {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        padding: 20px;
    }
    table {
        border-collapse: collapse;
        width: 200px;
    }
    th, td {
        border: 1px solid black;
        padding: 8px;
        text-align: right;
    }
    .legend {
        padding: 6px 8px;
        background: white;
        background: rgba(255,255,255,0.9);
        box-shadow: 0 0 15px rgba(0,0,0,0.2);
        border-radius: 5px;
        line-height: 24px;
    }
</style>
</head>
<body>
<div class="flex-container">
  <div id="map">
  </div>
  <div class="tables-container">
  </div>
</div>
<script>
var mapOptions = {
   center: [$centerpoint],
   zoom: 7
};
var map = new L.map('map', mapOptions);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors',
    maxZoom: 19
}).addTo(map);

var marker = L.marker([$centerpoint]);
marker.addTo(map);
marker.bindPopup('$from').openPopup();

function milesToMeters(miles) {
   return miles * 1609.34;
}

var colors = [$band_colors];
var radii = [$bands].map(Number);

radii.forEach(function(radius, index) {
    var circle = L.circle([$centerpoint], {
        radius: milesToMeters(radius),
        color: colors[index],
        weight: 2,
        fill: true,
        fillColor: colors[index],
        fillOpacity: 0.05,
        interactive: false
    }).addTo(map);
    console.log('Added circle:', radius, 'miles');
});

var legend = L.control({position: 'bottomleft'});
legend.onAdd = function (map) {
    var div = L.DomUtil.create('div', 'legend');
    div.innerHTML = '<strong>Miles from center</strong><br>';
    radii.forEach(function(radius, i) {
        div.innerHTML +=
            '<i style="background:' + colors[i] + '; width: 18px; height: 18px; float: left; margin-right: 8px; opacity: 0.7;"></i> ' +
            radius + '<br>';
    });
    return div;
};
legend.addTo(map);

// Add resize handler to ensure map fills container after window resize
window.addEventListener('resize', function() {
    map.invalidateSize();
});
</script>
</body>
</html>
"""

open(file_path, "w") do file
    write(file, bullseye)
end
run(`open $file_path`)

Here is the response I get from Claude’s Sonnet 4. I haven’t tested them, but based on my experience with Franklin, each looks feasible and if I were in your shoes I’d try one and go back for implementation details. For me, the most common source of distress from using AI tools is following it too long down dead-end paths that I set in motion with how questions, rather than using this style.

For integrating parameterized HTML content like your bullseye map into Franklin.jl while keeping boilerplate isolated, you have several options:

Option 1: Franklin Function with HTML Template

Create a Franklin function that generates the HTML inline:

# In your Franklin page or utils.jl
function bullseye_map(capital::String, coords::String, palette_idx::Int=4)
    centerpoint = dms_to_decimal(coords)
    bands = "50, 100, 200, 400"
    band_colors = pal[palette_idx]
    
    return """
    <div id="map-$capital" style="height: 600px; width: 100%;"></div>
    <script>
    // Your Leaflet code here with interpolated variables
    var map_$capital = L.map('map-$capital', {
        center: [$centerpoint],
        zoom: 7
    });
    // ... rest of map code
    </script>
    """
end

Then in your Franklin page:

# Boston Map
{{bullseye_map "Boston" "42° 21′ 37″ N, 71° 3′ 28″ W"}}

Option 2: Asset Template + Franklin Variables

Create a template file _assets/map_template.html:

<!DOCTYPE html>
<html>
<head>
  <title>Map for CITY_NAME</title>
  <!-- Your CSS here -->
</head>
<body>
<script>
var mapOptions = {
   center: [CENTER_COORDS],
   zoom: 7
};
// Replace CITY_NAME, CENTER_COORDS, BAND_COLORS, etc.
</script>
</body>
</html>

Then create a Franklin function:

function generate_map_page(capital, coords, palette_idx=4)
    template = read("_assets/map_template.html", String)
    centerpoint = dms_to_decimal(coords)
    
    html = replace(template, 
        "CITY_NAME" => capital,
        "CENTER_COORDS" => centerpoint,
        "BAND_COLORS" => pal[palette_idx],
        "BANDS" => "50, 100, 200, 400"
    )
    
    write("_assets/$(capital)_map.html", html)
    return """<iframe src="../assets/$(capital)_map.html" width="100%" height="600px"></iframe>"""
end

Option 3: Franklin Plugin Pattern

Create a more sophisticated system using Franklin’s plugin capabilities:

# In _utils/maps.jl
struct BullseyeMap
    capital::String
    coords::String
    palette::Int
    bands::String
    zoom::Int
end

function hfun_bullseye(params)
    capital = params[1]
    coords = params[2]
    palette = length(params) >= 3 ? parse(Int, params[3]) : 4
    
    map = BullseyeMap(capital, coords, palette, "50, 100, 200, 400", 7)
    return render_map(map)
end

function render_map(map::BullseyeMap)
    # Your rendering logic here
    centerpoint = dms_to_decimal(map.coords)
    # Return the HTML string
end

Usage in Franklin pages:

{{bullseye Boston "42° 21′ 37″ N, 71° 3′ 28″ W" 4}}

Option 4: External Asset Generation

Keep your current approach but integrate it with Franklin’s build process:

# In config.md or _utils/
function generate_all_maps()
    cities = [
        ("Boston", "42° 21′ 37″ N, 71° 3′ 28″ W"),
        ("NYC", "40° 42′ 46″ N, 74° 0′ 21″ W"),
        # ... more cities
    ]
    
    for (capital, coords) in cities
        # Your existing code here
        generate_bullseye_map(capital, coords)
    end
end

Then reference the generated files in Franklin:

<iframe src="../assets/Boston.html" width="100%" height="600px"></iframe>

Recommendation

For your use case, Option 1 (Franklin function with inline HTML) provides the best balance of:

  • Keeping templates in Julia code (maintainable)
  • Direct integration with Franklin’s templating
  • Easy parameterization
  • No external file management

The key is moving your HTML template into a Julia function that returns the interpolated HTML string, then calling that function from your Franklin pages using the {{function_name params}} syntax.

Thank you for your thoughtful answer! I do need a little help though. I tried implementing Option 1 for my site. I used this code in utils.jl:

 function render_form(params::Vector{String}, params_val::Vector{Float64}, params_desc::Vector{String})  
  HTML = """"""
  for p in 1:length(params)
    param_name = params[p];
    param_val = params_val[p];
    param_desc = params_desc[p];
     HTML *= """
     <tr>
        <td><label for="$param_name">\\($param_name\\):</label></td>
        <td><input type="Number"
                        id="$param_name"
                        name="$param_name"
                        value="$param_val"></td>
                    <td>$param_desc</td>
    </tr>
     """
  end

  return HTML
end

When I run this in Julia’s JIT, I get: exactly what I expect to get, the required HTML. But when I include this in my page: {{ render_form params params_val param_desc }}, where these variables are all defined in my Markdown file, no HTML is generated. I noticed that I was getting this warning │ A block '{{render_form ...}}' was found but the name 'render_form' does │ not correspond to a built-in block or hfun nor does it match anything │ defined in 'utils.jl'. It might have been misspelled. Then I looked into the docs of Franklin.jl and I see that parameter lists are sent to hfuns as a vector of strings. Plus, you need to prefix these functions with hfun_ which explains why when I copy-pasted your option 1 code it failed with the warning │ A block '{{bullseye_map ...}}' was found but the name 'bullseye_map' does │ not correspond to a built-in block or hfun nor does it match anything │ defined in 'utils.jl'. It might have been misspelled.. But this leaves me with a pretty significant problem, even with these adaptations I can’t use option 1 for my purposes as my inputs aren’t really conducive to being treated as a vector of strings. I thought maybe if my hfun had a single argument, it wouldn’t be treated as a vector of strings, so I wrote this:

function hfun_render_form(params::NamedTuple)
  params_list = keys(params)[1:end-1];
  HTML = """"""
  for p in 1:length(params_list)
    param_name = params_list[p];
    param_val = params[param_name];
    param_desc = params.desc[p];
    HTML *= """
    <tr>
        <td><label for="$param_name">\\($param_name\\):</label></td>
        <td><input type="Number"
                        id="$param_name"
                        name="$param_name"
                        value="$param_val"></td>
                    <td>$param_desc</td>
    </tr>
     """
  end

  return HTML
end

(My definition line for params is @def params = (a = 2, b=1, c=-0.5, desc=["Problem parameter", "Problem parameter", "Problem parameter"]) in case you are wondering what it looks like).

but when I included it in my Markdown file with {{ render_form params }} I received errors related to params being treated as a vector of strings.

I did also try:


function hfun_render_form(params::Vector{String})
  indx_arr = 1:length(params)
  params_list_end = indx_arr[params .== "stop"][1];
  params_val_end = indx_arr[params .== "stop"][2];
  params_desc_end = indx_arr[params .== "stop"][3];
  params_list = params[1:(params_list_end-1)];
  params_val = params[(params_list_end+1):(params_val_end-1)]
  params_desc = params[(params_val_end+1):(params_desc_end-1)]
  HTML = """"""
  for p in 1:length(params_list)
    param_name = params_list[p];
    param_val = params_val[p];
    param_desc = params_desc[p];
    HTML *= """
    <tr>
        <td><label for="$param_name">\\($param_name\\):</label></td>
        <td><input type="Number"
                        id="$param_name"
                        name="$param_name"
                        value="$param_val"></td>
                    <td>$param_desc</td>
    </tr>
     """
  end

  return HTML
end

As I thought I’d try to make the most of hfun’s argument types. But even this failed me. I received the error:

Stacktrace:
  [1] getindex
    @ ./essentials.jl:916 [inlined]
  [2] hfun_render_form(params::Vector{String})
    @ Main.Utils_15 /mnt/d/GitHub/mine/websites/fusion809.github.io/utils.jl:20
  [3] top-level scope
    @ none:1
  [4] eval
    @ ./boot.jl:430 [inlined]
  [5] convert_html_fblock(β::Franklin.HFun)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/html/functions.jl:14
  [6] process_html_qblocks(hs::String, qblocks::Vector{Franklin.AbstractBlock}, head::Int64, tail::Int64)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/html/html.jl:122
  [7] process_html_qblocks(hs::String, qblocks::Vector{Franklin.AbstractBlock})
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/html/html.jl:102
  [8] convert_html(hs::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/html/html.jl:22
  [9] map
    @ ./tuple.jl:357 [inlined]
 [10] map
    @ ./tuple.jl:358 [inlined]
 [11] write_page(output_path::String, content::String; head::String, pgfoot::String, foot::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/write_page.jl:108
 [12] write_page
    @ ~/.julia/packages/Franklin/X68Y3/src/manager/write_page.jl:75 [inlined]
 [13] convert_and_write(root::String, file::String, head::String, pgfoot::String, foot::String, output_path::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/write_page.jl:215
 [14] process_file_err(case::Symbol, fpair::Pair{String, String}, head::String, pgfoot::String, foot::String, t::Float64)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:153
 [15] process_file(::Symbol, ::Pair{String, String}, ::String, ::Vararg{Any})
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:104
 [16] fd_fullpass(watched_files::@NamedTuple{…}, join_to_prepath::String)
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:265
 [17]
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:126
 [18] serve()
    @ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
 [19] top-level scope
    @ REPL[27]:1
Some type information was truncated. Use `show(err)` to see complete types.

Line 20 of utils.jl is the params_list_end line. But I am confused as this exact function runs perfectly fine from the Julia JIT when params is ["a", "b", "c", "stop", "2", "1", "0.5", "stop", "Problem parameter", "Problem parameter", "Problem parameter", "stop"] which is exactly what I’m defining paramas as in my Markdown file.

It seems like in {{ render_form params }} in my HTML file, params is being left unfilled, so params is just the string “params”. What can I do to fix this?

Never mind. I merely needed to use locvar("params") to load my page’s params variable.

1 Like