jQuery templates plugin: how to create two-way binding? - javascript

I started using jQuery templates plugin (the one Microsoft created), but now I face this problem: the template is for a bunch of forms bound to an array of objects; when I change something on one of the forms, I want the bound object to update and I can't figure out how to automate that.
Here's a simple example (real life template and object are much more complex) :
<!-- Template -->
<script type="text/html" id="tmplTest">
<input type="text" value="${textvalue}"/>
</script>
<!-- object to bind -->
<script type="text/javascript">
var obj = [{textvalue : "text1"},{textvalue : "text2"}]
jQuery("#tmplTest").tmpl(obj)
</script>
This will populate two textboxes, each bound to a value from corresponding object. Now, if I change a value in one of the textboxes, I need to update corresponding data object's value. Any idea how to do that?

jQuery template doesn't actually implement two-way data binding, but another Microsoft developed jQuery plugin does.
This Scott Guthrie post actually covers both the tmpl plug in and Data Linking plugin. Scroll down to "Support for Client Data-Linking" where Scott goes into detail on how the Data Linking plug in works.
However, for two way data binding, i find the knockoutjs extension to be much better and cleaner. The declarative syntax keeps the markup clean and the custom data binding overrides allow for a multitude of applications. Finally the mapping plugin is pretty great for processing JSON from the server into binding. Finally knockoutjs also has bindings based on tmpl plugin as well.
Good luck with your problem.
EDIT: updated Code Example
Scrips required:
<script src="/Scripts/jquery-1.5.0.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.tmpl.js" type="text/javascript"></script>
<script src="/Scripts/knockout.js" type="text/javascript"></script>
<script src="/Scripts/knockout.mapping.js" type="text/javascript"></script>
Then here is the meat and potatoes
<!-- div for loading the template -->
<div data-bind='template: { name: "tmplTest", foreach: viewModel.list }'>
</div>
<!-- your template -->
<script type='text/html' id='tmplTest'>
<div>
<span data-bind='text: textvalue, uniqueName: true'></span>
<input data-bind='value: textvalue, uniqueName: true, valueUpdate:"afterkeydown"'/>
</div>
</script>
<script type='text/javascript'>
var viewModel = ko.mapping.fromJS(
{
list:[ { textvalue: "text1" },
{ textvalue: "text2"} ]
});
$(function() {
ko.applyBindings(viewModel);
});
</script>

You could write your own data link. Note: I needed the index to make this work, so I added your data into an array, and put an each in the template. If someone knows how to get the index without using an each please add :) jsfiddle link
<script type="text/html" id="tmplTest">
{{each inputs}}
<input type="text" class="datalink" data-key="textvalue" data-index="${$index}" value="${$value.textvalue}"/>
{{/each}}
</script>
<div id="output"></div>
$.extend(window, {
data:[
{inputs: [{textvalue:"text1"},{textvalue:"text2"}]}
]
});
$('#output').append($("#tmplTest").tmpl(window.data));
$('#output .datalink').live('change', function(){ // update object on change
var $this = $(this),
d = $this.data();
window.data[0].inputs[d.index*1][d.key] = $this.val();
console.log(window.data[0].inputs);
});

Related

make javascript variable update live with text input

hey this is an edited version of my first post:
Pretty much what I want to do is have a text input box that stores its contents in a javascript variable. The catch is I want it to happen live, so the variable will update automatically say, ever .2 seconds without the user needing to press a submit button. Thanks
Are you looking for the two way data binding? Something like this: https://codepen.io/manishiitg/embed/ZYOmbB?
<html>
<head>
<script src="https://code.angularjs.org/1.3.8/angular.min.js"></script>
<script>
var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', function ($scope) {
$scope.text = '';
});
</script>
</head>
<body ng-app='MainApp'>
<div ng-controller='MainCtrl'>
<div>
Change Text Here:
<input type='text' ng-model='text' />
</div>
<div>
<p>Text: {{text}}</p>
</div>
</div>
</body>
<html>
You can achieve that with Angular, React and Vue.js framework, even with jQuery. Perhaps you should try one of the frameworks that solves your needs quickly.
If you choose Angular, I recommend you to make the tutorial steps, specially this part: https://angular.io/tutorial/toh-pt1#two-way-binding

Knockoutjs error: You cannot apply bindings multiple times to the same element

I'm trying to automatically populate an element in an existing web page that I can't change and that page uses Knockoutjs. The input element looks like this more or less:
<input maxlength="8" id="xxx" data-bind="textInput: otcInput" type="tel">
Then I use Knockoutjs to attempt to unbind the textInput and populate the input element dynamically with whatever value I need, so I do:
ko.cleanNode($('#xxx'));
ko.applyBindings({
otcInput: ko.observable("123") // populate myself
});
However, this leads to the error You cannot apply bindings multiple times to the same element ... the question is why? I'm already cleaning the node ... or am I not? is there a way using knockoutjs to see whether there are dangling bindings or leftovers that get in the way while trying to execute my "overriding" ko.applyBindings?
I have also tried other ways to set the input value via JQuery sendkeys plugin without success i.e.
$('#xxx').sendkeys('123'); // nothing happens
I also tried:
$('#xxx').unbind();
$('#xxx').off();
$('#xxx').sendkeys('123'); // but again nothing happens
You're passing a jQuery object to cleanNode. Just like with applyBindings, it has to be a DOM element, not a jQuery object. So:
ko.cleanNode($('#xxx')[0]);
// -------------------^^^
Example — this fails:
ko.applyBindings({
foo: ko.observable("one")
}, $("#test")[0]);
ko.cleanNode($("#test"));
ko.applyBindings({
foo: ko.observable("two")
}, $("#test")[0]);
<div id="test">
<div data-bind="text: foo"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
...but this (with the [0]) works:
ko.applyBindings({
foo: ko.observable("one")
}, $("#test")[0]);
ko.cleanNode($("#test")[0]);
ko.applyBindings({
foo: ko.observable("two")
}, $("#test")[0]);
<div id="test">
<div data-bind="text: foo"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
I have also tried other ways to set the input value
If that's your goal, you don't have to muck with the bindings (which probably would have undesirable effects), just:
$("#xxx").val("new value").trigger("change");
The trigger("change") is necessary to get KO to see the change and update the observable. (Or as it's a textInput binding, you might use input rather than change.)
Example — this fails:
// The previously-bound stuff:
var vm = {
foo: ko.observable("foo")
};
ko.applyBindings(vm, document.body);
// Prove the observable and input are in sync:
console.log("check1", vm.foo(), $("#test").val());
// Update the field
$("#test").val("updated").trigger("change");
// Prove the observable and input are still in sync:
console.log("check2", vm.foo(), $("#test").val());
<input id="test" type="text" data-bind="textInput: foo">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

AngularJS model-bind not working if the model and bind are set by jQuery

I am learning Angular JS and I am stuck at a pretty basic step.
Here is my test code:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
function enable()
{
$("#test_model").attr("data-ng-model", "test");
$("#test_bind").attr("data-ng-bind", "test");
};
</script>
</head>
<body>
<div data-ng-app="">
<div>
<input type=text id=test_model />
</div>
<div>
<p id=test_bind></p>
</div>
<button onclick=enable()>Enable</button>
</div>
</body>
</html>
Here, what I expect to happen is, when the Enable button is clicked, it should enable the bind, and so if anything is typed into the #test_model input, it should update #test_bind paragraph. But it is not working.
I can see with Firebug that the attributes (data-ng-model and data-ng-bind) are being updated when the Enable button is clicked, but it is not taking effect as it would if it were statically coded (hard-coding the model and bind parameters).
If I code it statically, then I notice that 3 new classes (ng-pristine, ng-untouched and ng-valid) are automatically added to #test_model and one new class (ng-binding) is added to #test_bind. So I changed my enable() function to this:
function enable()
{
$("#test_model").attr("data-ng-model", "test");
$("#test_bind").attr("data-ng-bind", "test");
$("#test_model").addClass("ng-pristine");
$("#test_model").addClass("ng-untouched");
$("#test_model").addClass("ng-valid");
$("#test_bind").addClass("ng-binding");
};
But still, not working.
What am I missing?
If you want to dynamically add Angular directives like ng-model, ng-bind etc.
you must create your own directive and use $compile.
You can use angular ng-show or ng-switch to switch showing DOM elements without bindings and elements with ng-model and other attributes that you need.

Role of data-role attribute in html markup

I am new to Kendo UI library. When going through the tutorial i found the following declaration
<input id="JoiningDate" data-role="datepicker"/>
could you please explain ,what is the role of data-role attribute?
It is called declarative initialization.
Inside this attribute ,You are specifying the type of Widget you want to use (in this case it is a datepicker widget).
You can either use regular markup
<input id="JoiningDate" />
<script type="text/javascript">
$(document).ready(function ()
{
$("#JoiningDate").KendoDatePicker();
});
</script>
(or)
<input id="JoiningDate" data-role="datepicker"/>
<script type="text/javascript">
$(document).ready(function ()
{
kendo.init($("#JoiningDate"));
});
</script>
In summary:
"The value of the role data attribute is the name of the widget in lower case e.g. "autocomplete", "dropdownlist" etc."
You can find info here:
http://docs.kendoui.com/getting-started/data-attribute-initialization#example---initialize-a-kendo-ui-widget-using-a-data-attributes

knockout js: parameters in templated bindings

Im trying to template some components often used in my project. To omit the introduced redundancy in html content. However i didnt figure it out if it is even possible to do so.
I have a template like:
<script type="text/x-jquery-tmpl" id="somefieldtemplate" >
<input name=" Prefix" type="text" data-bind="value: ${ $item.fieldName}SomeField" />
..... mor contents ...
</script>
The parameter bound to the input component shall be adjustable as someone may see via template options so i have an entry like
<div data-bind="template: { name: 'somefieldtemplate',
templateOptions:
{ fieldName:'somefield', displayName:'somefieldlabel' } }">
The error message in my console log is:
SyntaxError: Unexpected token {
I narrowed the problem down to the fact that if i remove $item.fieldName with its value it works.
Has anybody an enlightening solution to this problem maybe ?
Edit:
As information im currently using knockout.js in version: knockout-latest
I had the same problem and after struggling a lot, I solved it by moving to knockout 1.3 beta (now it's in RC) which doesn't use external templating engines. It doesn't support templateOptions either but that's not a problem. I just constructed a custom data for the template which contains the main data as a property plus other properties that contain the stuff I would have passed in templateOptions in 1.2. I passed it in the data parameter, and everything worked fine.
Try
data-bind="value: ${fieldName} + 'SomeField'"
Sadly i need to answer it myself. But i managed to get it working. Thanks to the tip of RP Niemeyer.
For everyone interested in this:
Observables are generated dynamically in the model someModel with a register component method. This observables have always names like <field>SomeField
Template:
<script type="text/x-jquery-tmpl" id="somefieldtemplate" >
<input name="${$item.fieldName}Prefix" type="text" data-bind="value: someModel[$item.fieldName + 'SomeField']" />
..... more contents ...
</script>
Template-Binding:
<div data-bind="template: { name: 'somefieldtemplate',
templateOptions:
{ fieldName:'somefield', displayName:'somefieldlabel' } }">

Categories

Resources