knockout html binding with another binding inside - javascript

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

Related

Nested StimulusJS scopes / controller issue / HTML 5 template tag

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.

display custom angular script template as table detail row

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

Vue.js block repeat table rows doesn't work in IE

I would like to repeat adding table rows using a template tag with vue.js, but it doesn't work in IE11. Here is the code.
<table border="1">
<tr>
<td rowspan="2">ID</td>
<td colspan="2">Name</td>
</tr>
<tr>
<td>Height</td>
<td>Weight</td>
</tr>
<template v-repeat="items">
<tr>
<td rowspan="2">{{id}}</td>
<td colspan="2">{{name}}</td>
</tr>
<tr>
<td>{{height}}</td>
<td>{{weight}}</td>
</tr>
</template>
</table>
Any help?
See http://vuejs.org/guide/components.html#Using_Components and the warning at the end of that section:
The table element has restrictions on what elements can appear inside
it, so custom elements will be hoisted out and not render properly. In
those cases you can use the component directive syntax:
<tr v-component="my-component"></tr>.
I found a solution that changed the <template> tag to a <tbody> tag. However there would be multiple <tbody> tags in a table, I hope this is the best solution in this case.
Make a long story short, This is HTML restrictions in IE, if you want compatibility, you will have to change your HTML structure.
I found an issue with similar question like yours here: https://github.com/vuejs/vue/issues/2404
Vue renders the template into real html before compiling it, so the same html restrictions apply for Vue templates, no matter how you define it.
IE does not support inside elements like , ..

jQuery With Mustache js Issue

I'm using jQuery and Mustache Js in a project.
I have the template in a script tag. The template requires some dynamic/variable html based on some data. Hence I load the template using jQuery, manipulate it, and then write it back into the script tag.
However, loading the template into a div(for manipulation), using jquery seems to alter the structure of the template (it moves things around), which invalidates the template.
How do i update the template dynamically and avoid this issue? (Any help / Pointers / etc)
Original Template
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody>{{#Users}}
<tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr>{{/Users}}</tbody>
</table>
Loading with jquery into another div causes this below(moves {{#Users}} {{/Users}} out of place)
{{#Users}} {{/Users}}
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody><tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr></tbody>
</table
The code below is how I'm going about it. Also this is a js fiddle link here
var $newDcsTemplate = $('<div/>').html($('#dcs-template').html()); // for manipulation
var original1 = $('#dcs-template').html()
var original2 = document.getElementById('dcs-template').innerHTML;
// manipulate the template here and replace content of script tag with it.
console.log(original1); // works fine
console.log(original2) // works fine
console.log($newDcsTemplate.html()) // messed up the template
Do it like this, with .html() and without </script>:
<table>
<thead>
<tr>
<td>FN</td>
<td>SN</td>
<td>OT</td>
</tr>
</thead>
<tbody>
<!-- {{#Users}} -->
<tr class="tr-border" data-row-id="{{DCSID}}">
<td class="td-border">{{FN}}</td>
<td class="td-border">{{SN}}</td>
<td class="td-border">{{OT}}</td>
</tr>
<!-- {{/Users}} -->
</tbody>
</table>
Your template is being modified by the DOM because you're inserting it into a <div> as "HTML", which it isn't. It's doing exactly what a browser would do if you gave it that markup. It's putting the {{# Users }} in the nearest available valid place to put it.
This is the same reason your template is originally stored in a <script> tag. You should always use <script> tags to hold your templates, and you should always use .text() to read and write them:
var $newDcsTemplate = $('<script/>').text($('#dcs-template').text());
console.log($newDcsTemplate.text())

KnockoutJS data-bind: click from javascript click

Let us say we have a table as so
<table>
<tbody data-bind="foreach: orderItems">
<tr class='datarow'>
<td data-bind="text: whatever"></td>
<td><a id='anchor' href='#' data-bind='click: $root.save' onclick='alert("a");'>Text</a></td>
</tr>
</tbody>
</table>
so when I do
document.getElementsById('anchor').click();
the alert triggers but $root.save doesn't. Any ideas?
Try This
<script type="text/javascript">
var viewModel = {
Save: function() {
alret("Hello");
}
};
ko.applyBindings(new MyViewModel());
</script>
<table>
<tbody data-bind="foreach: orderItems">
<tr class='datarow'>
<td data-bind="text: whatever"></td>
<td><a id='anchor' href='#' data-bind='click: $root.save' onclick='alert("a");'>Text</a></td>
</tr>
</tbody>
</table>
I'm seeing 2 problems with the code:
getElementsById - are you sure this method exists?
Each ID should only exist once on the page. When you want to find an element by it's ID, you can call getElementById (singular), and it will return an element. Because only one element should have a given ID, there is no getElementsById (plural) method.
Your foreach binding combined with a hardcoded 'anchor' id will result in multiple elements with an ID of anchor. This is a no-no. Perhaps use a class of 'anchor' instead of an id of 'anchor'.
<a class='anchor' href='#' data-bind='click: $root.save' onclick='alert("a");'>Text</a>
So my guess is that getElementsById is not returning anything to click on.
click()
Even if you were able to get an element back from the getElementsById, there is no click() method on those elements.
As an alternate, you can use jQuery to get the elements, and with a jQuery object you can call the click() method. Or you could take an element returned from getElementById, wrap it in jQuery, then call click().
$('.anchor').click();
Here is a working example: http://jsfiddle.net/tlarson/t4yZL/1/
<table>
<tbody data-bind="foreach: orderItems">
<tr class='datarow'>
<td data-bind="text: whatever"></td>
<td><a id='anchor' href='#' onclick='alert("a"); viewModel.save(ko.dataFor(this));'>Text</a></td>
</tr>
</tbody>
</table>
Assume that you stored your viewModel elsewhere.
ko.dataFor(element) - returns the data that was available for binding against the element
You may have a look at this link

Categories