Home > database >  How to include Handlebars partial in a string? (add it to the innerHTML of a DOM Element)
How to include Handlebars partial in a string? (add it to the innerHTML of a DOM Element)

Time:09-27

Is there a way to get the "string version" of a handlebars partial to include it in the innerHTML of an HTML element?

For instance, imagine I have a ToDo list, and I want to add a task everytime I click the button "Add Task", like this:

todo_list.hbs

<div id="todo-list">

</div>
<button onclick="addTask">Add Task</button>

And that I have a handlebars partial in the file "task.hbs":

task.hbs

<h1 >The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>

<script>
    const button_delete_task = document.getElementById('delete-task');
    button_delete_task.addEventListener('click', deleteTask);
    
    function deleteTask () {
        // delete task code here
    }
</script>

My question is: How could I create a Task partial everytime the button "Add Task" is clicked? Something like this:

<div id="todo-list">

</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
    const todo_list = document.getElementById('todo_list');
    todo_list.innerHTML  = {{> Task title="A new task"}};
    // More code here...
}
</script>

I have also tried enclosing the partial with backticks (`{{> Task title="A new task"}}`), and quotes ("{{> Task title='A new task'}}") as well as read many posts on this subject, but all of them use handlebars.js, not express-handlebars.

I am using express.js for the backend, and therefore, express-handlebars as the view engine. In advance, thanks a lot for your help!

CodePudding user response:

I managed to solve the issue!

It turns out that enclosing the partial with backticks works! The problem was that my partial had <script></script> tags.

Imagine my task.hbs looked like this:

<div>
    <script></script>
</div>

then, the processed version of todo_list.hbs would look like this:

<div id="todo-list">

</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
    const todo_list = document.getElementById('todo_list');
    todo_list.innerHTML  = `<div>
    <script></script>    
    </div>`;
    // More code here...
}
</script>

This would be valid in a normal HTML file, but it looks like handlebars process the closing script tag that is inside the string (</script>) as a normal tag, and with it, closes the <script> tag of todo_list.hbs.

The solution I found was to not use <script> tags into my partial (not a beautiful solution, but works for me!) and instead, declare the javascript code in another file, and import it into todo_list.hbs using <script> tags with the src parameter like this:

todo_list.hbs

<div id="todo-list">

</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
    const todo_list = document.getElementById('todo_list');
    todo_list.innerHTML  = `{{> Task title="New task!"}}`;
    // More code here...
}
</script>

<!-- JAVASCRIPT CODE REQUIRED BY TASK PARTIAL -->
<script src="/foo/bar/partials/Task.js"></script>

Where Task.js is the file containing the javascript of the Task.hbs partial:

Task.js

const button_delete_task = document.getElementById('delete-task');
button_delete_task.addEventListener('click', deleteTask);
    
function deleteTask () {
    // delete task code here
}

And with this changes, Task.hbs would look like this:

Task.hbs

<h1 >The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>

CodePudding user response:

You are very close to getting this to work.

As you have noted, your Handlebars is executing on the server-side. In the case of your partial, you are trying to have it render within a script block. In order for the result to be valid JavaScript, you would need have quotes around the output of the partial so that it will be a valid JavaScript string. Therefore:

todo_list.innerHTML  = "{{>Task title='A new task'}}";

Which, when rendered, would result in:

todo_list.innerHTML  = "<h1>The task is: A new task</h1>";

It should be noted that quotes in your partial could be problematic. For example, if the <h1> in your partial had a class <h1 >, the resultant JavaScript would now be invalid because the quote after the = would be interpreted as the closing quote of the JavaScript string. Therefore, you would need to be sure to either escape the quotes in your partial or ensure they are different from those used to wrap your partial call (a single-quote ('), in this case.

todo_list.innerHTML  = "<h1 class=\"task\">The task is: A new task</h1>";

Additionally, you have an inconsistency with the id of your <div>. The tag has id="todo-list" (with a dash); but your JavaScript has document.getElementById('todo_list') (with an underscore). Those will need to be consistent.

Update

As @Sharif Velásquez Alzate noted in comments, the quotes will not work when the partial contains line-breaks because JavaScript strings cannot span multiple lines (unless each line ends with a \ to signify that the text continues to the next line. However, a template literal, using back-ticks, will support text with line-breaks. Therefore, a better solution is:

todo_list.innerHTML  = `{{>Task title='A new task'}}`;
  • Related