I have a handlebars template, that looks like this:
{{#each leagues}}
<tr>
<td>{{leagueName}}</td>
<td>{{season}}</td>
<td>
<form action="/groups/{{../id}}/teams/{{id}}/leagues/{{leagueId}}/season/{{season}}" method="POST">
{{#if ../groups}}
<select name="group" class="form-select">
{{#each ../groups}}
<option value="{{id}}">{{name}}</option>
{{/each}}
</select>
<input type="submit" value="Add" class="btn btn-primary mt-2">
{{else}}
<div class="d-flex align-items-center">
<p class="text-danger mb-0">No groups available</p>
<a href="/createGroup" class="btn btn-secondary ms-2">Create Group</a>
</div>
{{/if}}
</form>
</td>
</tr>
{{/each}}
My issue is that when I do the post, I want to the action URL to have both the id
of the league
, and the id
of the group
. Both are called "id". When I do {{../id}}
to access the group "id" that is outside the scope it still doesn't work.
This way, I'm getting, for example:
Cannot POST /groups//teams/123/leagues/321/season/1904
The group "id" is not getting set in the form action.
I'm passing the data to the template like this:
resp.render("leaguesByTeam", { leagues, groups })
Where the groups have the following format:
[
{ id: 0, userId: 0, name: 'g1', description: 'g1 desc', teams: [] },
{ id: 1, userId: 0, name: 'g2', description: 'g2 desc', teams: [] }
]
If useful, leagues have the following format:
{
id: '211',
leagueId: 26,
leagueName: 'International Champions Cup',
season: 2018
},
{
id: '211',
leagueId: 3,
leagueName: 'UEFA Europa League',
season: 2018
},
What you are trying to do is not going to work without some client-side JavaScript.
The action
attribute on each of your rendered forms - the endpoint URL to which the form data will be POSTed - is computed before the user has even loaded the document, let alone selected a group.
You require a way to dynamically build the action
URL whenever the user changes the selected group
via the <select>
menu.
This is where the client-side JavaScript will come in. You will need to loop through each <form>
element on your page (one for each league
) and bind a submit
event listener to it. In the callback to the listener, you will need a way to get all of the pieces (the id
values) that you will use to compose your action URL. I think a clean way to do this would be to use hidden <input>
elements for each id
(league
, team
and season
).
You would then dynamically set the value of the action
of the submitting form, right before it gets POSTed.
The code would look something like:
const rawTemplate = document.getElementById('Template').innerHTML;
const template = Handlebars.compile(rawTemplate);
const data = {
groups: [
{
id: 0,
userId: 0,
name: 'g1',
description: 'g1 desc',
teams: []
},
{
id: 1,
userId: 0,
name: 'g2',
description: 'g2 desc',
teams: []
}
],
leagues: [
{
id: '211',
leagueId: 26,
leagueName: 'International Champions Cup',
season: 2018
},
{
id: '211',
leagueId: 3,
leagueName: 'UEFA Europa League',
season: 2018
}
]
}
const output = template(data);
document.getElementById('Output').innerHTML = output;
// dynamic form handling to compose and set the action
// when the form is submitted
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', (event) => {
const theForm = event.target;
const teamId = theForm.querySelector('input[name="teamId"]').value;
const leagueId = theForm.querySelector('input[name="leagueId"]').value;
const season = theForm.querySelector('input[name="season"]').value;
const groupId = theForm.querySelector('select[name="group"]').value;
const action = `/groups/${groupId}/teams/${teamId}/leagues/${leagueId}/season/${season}`;
theForm.setAttribute('action', action);
// remove the following lines, they are for demo purposes
event.preventDefault();
document.getElementById('Action').innerText = action;
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js"></script>
<script id="Template" type="text">
{{#each leagues}}
<tr>
<td>{{leagueName}}</td>
<td>{{season}}</td>
<td>
<form method="POST">
<!-- hidden inputs to store action variables -->
<input name="teamId" type="hidden" value="{{id}}">
<input name="leagueId" type="hidden" value="{{leagueId}}">
<input name="season" type="hidden" value="{{season}}">
{{#if ../groups}}
<select name="group" class="form-select">
{{#each ../groups}}
<option value="{{id}}">{{name}}</option>
{{/each}}
</select>
<input type="submit" value="Add" class="btn btn-primary mt-2">
{{else}}
<div class="d-flex align-items-center">
<p class="text-danger mb-0">No groups available</p>
<a href="/createGroup" class="btn btn-secondary ms-2">Create Group</a>
</div>
{{/if}}
</form>
</td>
</tr>
{{/each}}
</script>
<table id="Output"></table>
<pre id="Action"><pre>