Knockout-ES5 not working - javascript

I am trying to use Knockout-ES5 along side Knockout in my project. But it seems to not working at all. Here is a very simple sample taken from the official Knockout tutorials. Here is my markup code
<p>First name: <input data-bind="text: firstName" /></p>
<p>Last name: <input data-bind="text: lastName" /></p>
And here is my js file
$(function () {
var obj = {
firstName: 'Bert',
lastName: 'Bertington'
}
ko.track(obj);
obj.firstName.subscribe(function(v){
alert(v);
});
});
The line obj.firstName.subscribe(function) crashes with the exception
"Object doesn't support property or methode 'subscribe' (IE10). I cannot seem to understand why this error. Here are the js files am using:
<script src="scripts/jquery-2.1.1.js"></script>
<script src="scripts/knockout-2.debug.js"></script>
<script src="scripts/weakmap.js"></script>
<script src="scripts/knockout-es5.js"></script>
I have tried various combinasions: e.g
ko.track(obj);
ko.applyBindings(obj);
but still nothing. Any suggestion toward the right direction would be very much appreciated. Thank you/

I have not used ko es5 but had a quick look at their documentation.
Looks like you need to use a method to get hold of the underlying observable:
Accessing the observables
If you want to access the underlying
ko.observable for a given property, e.g., so that you can subscribe to
receive notifications when it changes, use ko.getObservable:
ko.getObservable(someModel, 'email').subscribe(function(newValue) {
console.log('The new email address is ' + newValue); });
http://blog.stevensanderson.com/2013/05/20/knockout-es5-a-plugin-to-simplify-your-syntax/

Related

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>

KnockoutJs validation error message from .html file

I have a question regarding KnockoutJs html5 validation.
If i wanted to include my error message on the page when it was loaded, like say i am doing a frontend module for my webshop and my error messages comes from tags.
In knockoutJS 2.2.1 i would be able to do this like so:
<span data-bind="validationMessage: firstName, text: 'Your error.'"></span>
<input data-bind='value: firstName, valueUpdate: "input"' required pattern="^[A-Za-z]{1,255}$" />
this would work fine and i could paste my errortag into the html page from the html file.
As seen in this fiddle.
http://jsfiddle.net/elbecita/gt228dgm/4/
However the problem is now that running the same code under knockoutjs 3.0 f.ex. the error message would be "This field is required."
So now i cant overwrite the custom message anymore without doing .extend(), is this intended and am i using this entirely wrong?
A gif to illustrate the issue: http://puu.sh/kHJH3/92a2708c93.gif
I found a similar problem on stackoverflow some of the solutions just seemed overkill but maybe there was a reason?
Setting error message in html with knockout validation this didnt have any accepted answers,so my question is really how do i paste my error message in the html?
Any input is much appreciated, thank you.
*(if i missed any information please let me know, first post long time lurker)
Validations can have multiple independent messages, so it makes some sense that the contents of the message are taken out of your hands. You choose where they display, but don't have to worry about what they say. I can see that you might want to intercept the standard message and turn it into a custom message. You can do that, but it's not built into validation (as far as I know).
viewModel.errors = ko.validation.group(viewModel);
var messageSubstitutes = {
'Invalid.': 'All letters',
'This field is required.': 'Try some letters'
};
viewModel.errMsg = ko.computed(function () {
var errs = viewModel.errors();
if (errs.length === 0) return null
var errMsg = errs[0](),
result = messageSubstitutes[errMsg];
return result;
});
and the HTML is just:
<legend data-bind="if:errMsg">Error: <span data-bind="text:errMsg"></span>
</legend>
<label>First name:
<input data-bind='value: firstName, valueUpdate: "input"' required pattern="^[A-Za-z]{1,255}$" />
</label>
Updated Fiddle

Changing input value with javascript - what went wrong?

I have a text with an input field. I want the field to start as blank, and when clicked upon, set the input's text to its correct value (saved in the "name" field, for instance).
If I do it this way, it works fine:
Buy <input type="text" name="eggs" onclick="this.value=this.name;"> tomorrow.
However, if I try to clean the DOM and move the function to a separate javascript file, it stops working:
HTML:
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
JS:
function showname(el) {
el.value = el.name;
}
function showname(el){
el.value = el.name;
}
.closeform{
width: 70px;
}
.closeform input {
width: 70px;
}
.closeform button {
width: 70px;
}
Buy
<span class="closeform">
<input type="text" name="eggs" onclick="showname(this);">
</span>
tomorrow.
I'm very new to Javascript - what am I missing here?
You say in your question:
However, if I try to clean the DOM and move the function to a separate javascript file, it stops working
Let's say you have 2 actual files in the same folder:
myscript.js contents:
function showname(el) { el.value = el.name; }
index.html contents:
<!DOCTYPE html>
<html><head><title>Demo</title>
<script src="myscript.js"></script>
</head><body>
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
</body></html>
OR
<!DOCTYPE html>
<html><head><title>Demo</title>
</head><body>
Buy <input type="text" name="eggs" onclick="showname(this);"> tomorrow.
<script src="myscript.js"></script>
</body></html>
That should work perfectly...
However, in the comments you say:
I tried it with Fiddle - maybe the problem is in Fiddle interface.
That is where your problem was....
There is no separate javascript-file in jsfiddle.
The three code-blocks (html, js, css) get merged into one file.
Right-click the result-window in jsfiddle and look at the generated file.
Then notice the options (top right corner) from jsfiddle: by default the code is wrapped in an onload-method (suiting to the library you selected or window.onload if you are not using a library).
You can however place the script in the head or body, thereby not wrapping your code inside a function's scope (which then closes over the containing identifiers).
See http://jsfiddle.net/wf55a5qb/ for a working example.
The reason your example stack-snippet worked here on StackOverflow is that it's snippet-editor does not wrap the javascript codeblock in a (onload-like) function (when it combines the three code-blocks).
Having said and explained this, I do encourage you to set your events (Using obj.addEventListener/obj.attachEvent or the direct elm.onevent) from the/a script once the elements (that your script manipulates, place script as last element of the html-body) or page (using window.onload/etc) has loaded.
I posted this to clear up what actually went wrong so you don't make false models in your head about how javascript works (like "an external script runs in it's own scope" which no-one claimed but might be an assumption you might make) whilst still learning it!
Everything in JavaScript has a scope. Where you are defining your function, it is not visible to the input so the input doesn't know that function even exists. You can use window to make the function visible to it:
<input type="text" name="eggs" onclick="window.showname(this);"/>
window.showname = function (el)
Fiddle
I don't recommend global functions though. So then what else?
You can use the onclick function in JavaScript. To find elements in JavaScript, you use selectors. I'm using getElementById() this will get an element by it's id. A list of selectors are here
<input id="my_input" type="text" name="eggs"/>
Then in JavaScript:
document.getElementById('my_input').onclick = function () {
//Use this to refer to the element
this.value = this.name;
};
Fiddle
When doing this. Make sure all your code is wrapped in a window.onload. This will make sure the code is run at the right time:
window.onload = function () {
//Your code
};
JSFiddle automatically puts your code in this.

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' } }">

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

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);
});

Categories

Resources