I'm trying to use knockout to do a pretty basic binding but am having trouble accessing the $item variable from jquery.tmpl. I keep getting "$item is not defined" when I apply the bindings.
I've done this before so I know it can be done but I can't figure out why it is not working in this case. What's interesting is if I remove templateOptions:{parentItem: $item}, then everything works as expected.
I've included the following files
<script type="text/javascript" src="Extension/resources/jquery.1.6.1.js"></script>
<script type="text/javascript" src="Extension/resources/jquery.tmpl.js"></script>
<script type="text/javascript" src="Extension/resources/knockout-1.2.1.js"></script>
My template and bindings look like this
<script type="text/html" id="itemTemplate">
<span data-bind="text:title"></span>
</script>
<div class="filterResults">
<span data-bind="text:message"></span>
# of items: <span data-bind="text:contentItems().length"></span>
<table cellspacing="0">
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: $item}
}"> </tr>
</table>
</div>
And I do the binding using:
//viewModel contains a contentItems observableArray
ko.applyBindings(viewModel, $('.filterResults')[0]);
Ok, looks like I can answer this one myself. The problem seems to be that the part where I did the data-bind and was trying to access the jquery.tmpl variable $item is not actually using jquery.tmpl
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: $item}
}"> </tr>
That is not inside a template so there is no $item variable to access. $item is associated with jquery.tmpl, not knockoutjs. The way I got around the issue was to just put my view model in there instead.
<tr data-bind="template: { name: 'itemTemplate', foreach: contentItems,
templateOptions:{parentItem: viewModel}
}"> </tr>
And in the actual templates, (itemTemplate in this example), I can access the $item variable.
Related
I have a basic Rails app with a nested association and I am doing a standard 'line items' type view where I am dynamically adding and removing rows from a template tag. I have a select that triggers a AJAX call to replace all the nested row selects (the context changed). The expenditure controller handles the overall form and the nested-form controller is used on other pages to handle the adding and removal of rows:
<div data-controller="expenditure nested-form">
<select data-target="expenditure.budget" data-action="change->expenditure#update_related"></select>
<table>
<thead></thead>
<tbody>
inserted rows...
<tr>
<td>
<select data-target="expenditure.budgetItemSelect"></select>
</td>
</tr>
<template data-target="nested-form.template">
<tr data-new_record="true">
<td>
<select data-target="expenditure.budgetItemSelect"></select>
</td>
</tr>
</template>
</tbody>
</table>
</div>
It works fine. I can add and remove rows and if I change the expenditure.budget select all the expenditure.budgetItemSelect targets get updated EXCEPT for the one inside the template. It's as if it's missing from the entire scope of the controller. I had them nested before but now have them in the same div data-controller="expenditure nested-form" to double check and it still doesn't work. Checked spelling and even tried removing the data-target="nested-form.template". No errors in the browser console. Am I missing something obvious here?
UPDATE
Hmmm... it seems that the template tag is read only and NOT part of the DOM which is why this fails.
I managed a hack where I replaced the contents of the entire template but that seems to break the controller that adds / deletes the rows 🤦♂️.
UPDATE 2
I found a workaround - If someone can improve this code I will accept this as a better answer.
It seems to be an issue with the <template> tag in HTML5.
I have a workaround but it's ugly.
<div data-controller="expenditure nested-form">
<select data-target="expenditure.budget" data-action="change->expenditure#update_related"></select>
<table>
<thead></thead>
<tbody>
inserted rows...
<tr>
<td>
<select data-target="expenditure.budgetItemSelect"></select>
</td>
</tr>
<template id="expenditure_items_template">
<tr data-new_record="true">
<td>
<select data-target="expenditure.budgetItemSelect"></select>
</td>
</tr>
</template>
<script type="text/template" data-target="nested-form.template" id="expenditure_items_template_script">
</script>
</tbody>
</table>
</div>
Here is what I did in my controller:
// find the template
var template = document.getElementById("expenditure_items_template");
// load the template contents
var new_template = document.importNode(template.content, true);
// replace the select with my new content (off screen)
new_template.getElementById('expenditure_expenditure_items_attributes_NEW_RECORD_budget_item_id').innerHTML = select.innerHTML;
// clear the new script place holder
document.getElementById("expenditure_items_template_script").innerHTML = "";
// set the new updated template into the script tag
document.getElementById("expenditure_items_template_script").appendChild(new_template);
I basically have two templates - one <template> which holds the raw HTML and the second <script> that works with Stimulus.
Hi i am quite new to angular programming and I had a question on hwo to include angular script templates into a table
Basically I have a base directive table that displays data, but I want anyone who uses that base directive to provide a template of how they want the detail row to look like. I thought the best way to do this was from an "angular script template"
This fiddle describes what I want to do:
http://jsfiddle.net/g0b1xk9s/1/
Basically in the fiddle I was wondering if it was possible to display the template with id (template1) where I have the code
<div ng-include src="template1">
</div>
Is this possible or should I find another way to do this?
Thank you for the help
Sure you can and script tags are documented as a valid template approach
<div ng-include src="'template'"></div>
<script type="text/ng-template" id="template">
<table border="1">
<tr ng-repeat="note in ctrl.notes">
<td>{{note.label}}</td>
</tr>
<tr ng-repeat-end>
<td>Done: {{note.done}}</td>
</tr>
</table>
</script>
DEMO
Suppose we have the following layout:
<tbody data-bind="foreach: items">
<tr>
<td data-bind="with: $parent.inplaceEditorVm">
<span data-bind="text: $parent.$data.OwnrPrefs"></span>
</td>
</tr>
</tbody>
How can we access the properties of the current foreach item in the context of the with binding?
I.E. In the example above, what do we need to write in a data-bind expression for the span element to get the value of the OwnrPrefs of the current foreach item?
When I'm using $parent.$data.OwnrPrefs like in the above example, it throws:
TypeError: Unable to get property 'OwnrPrefs' of undefined or null
reference
And when I'm trying to use $data.OwnrPrefs, the value of this expression is resolved to undefined, since the $data inside the scope of the with binding is the inplaceEditorVm object, not the current foreach item.
Bindings such as with and foreach create new binding contexts. The outer/original context, ie the one outside the with, is available as $parent - this is what $parent refers to, it's not (directly) related to your viewmodel structure, but rather the bindings on the page.
In your case, you can do:
<tbody data-bind="foreach: items">
<tr>
<td data-bind="with: $parent.inplaceEditorVm">
<span data-bind="text: $parent.OwnrPrefs"></span>
</td>
</tr>
</tbody>
I'm using knockout to dynamically loading content into parts of the page, using the HTML binding.
the problem is that the html I want to bind has to do call a function onclick and I need the information about the target and the data that knockout easily send.
something like this:
myFunction($parent, $data)
HTML:
<table>
<tbody data-bind="foreach: rows" >
<tr>
<td data-bind="html: rowValue">this will be a link</td>
</tr>
</tbody>
</table>
Later I set the value to be a link with a knockout binding inside:
rowValue("<a href='#' data-bind=click:alert('hello')" + result.Data + "</a>");
Please check the fiddle here to see the full working code.
You can see the difference between the 2 lines I wrote, if I do a javascript onclick it works, but obviously ko is missing a late binding.
I've seen many questions about this but can't find one with a definitive answer.
I want to do this with KO, how can this be accomplished?
with templates maybe?
KO applies bindings when you call ko.applyBindings.
So if you modify the dom after applyBindings has been called. KO won't be aware of the new dom element.
You can use template this way :
<table>
<tbody data-bind="foreach: sitesTable.rows" >
<tr data-bind="foreach: row">
<td data-bind="template: 'myTemplate' "></td>
</tr>
</tbody>
</table>
<br/>
click here
<script id="myTemplate" type="text/html">
click
</script>
edit by Maurizio. Use this fiddle as the other linkseems to be broken:
See fiddle
I have some small template strings, which will be rendered through Mustache.js on the same page.I need not create seperate html files for templates.
Options for storing the templates :
Storing in javascript variables : Hackish multiline strings, lots of escaping of quotes.
Storing as innerHTML of hidden divs.
I tried method#2, but it does not seem to work correctly.
Fiddle:
http://jsfiddle.net/RHwnq/2/
<html>
<head></head>
<body>
<div id='templates' class='hide' align="">
<div id='tableTemplate'>
<table class="table">
{{#name_list}}
<tr><td> {{name}} </td></tr>
{{/name_list}}
</table>
</div>
</div>
<script>
var template = $('#tableTemplate').html();
console.log(template);
</script>
</body>
</html>
This logs :
{{#name_list}}
{{name}}
{{/name_list}}
<table class="table"><tbody><tr><td></td></tr></tbody></table>
Instead of :
<table class="table">
{{#name_list}}
<tr><td> {{name}} </td></tr>
{{/name_list}}
</table>
This might be to due to some markup correction by the browser.
What are other good tricks to store HTML templates within an HTML page ?
I store them in a script tag, so they don't get rendered, like this:
<script id="abc-template" type="text/html">
<h1>{{name}}</h1>
</script>
You can then reference them like this:
var template = $('#abc-template').html();
var html = Mustache.to_html(template, data);
$('#abc-div').html(html);
Using <script> tags works great for this:
<script id="tableTemplate" type="text/html">
<table class="table">
{{#name_list}}
<tr><td> {{name}} </td></tr>
{{/name_list}}
</table>
</script>
It's actually a drop-in replacement, it will work with your var template = $('#tableTemplate').html();
Depending on your IDE, you may or may not get HTML syntax highlighting within <script></script> blocks. I use a div block as you did, but add
#templates {display: none;}
to the CSS.
I'd use HTML "template" tag.
<template id="tmp">Some HTML here</template>
See this example:
https://jsfiddle.net/cityremade/xwLht8vc/