Is it possible to pass array value with square brackets for binding handler?, ie:
<div data-bind="validator: [{class: RequiredValidator}, {class: EmailValidator}]"></div>
It works fine for one object:
<div data-bind="validator: {class: RequiredValidator}"></div>
Class value is not observable, just javascript object.
It throws Message: Unexpected token ) error.
Or I need some other syntax? I could wrap it with object, but prefer not.
I took snapshot of project with this issue, available here: http://balin.maslosoft.com/array-validators/dev/validator.php
Open console, and object validators will show configuration, while array will fail.
Here is fiddle with minimal example: http://jsfiddle.net/piotr/fu8d0hm3/
It works for these. Might the problem be in your binding handler?
ko.bindingHandlers.validator = {
init: function(el, va) {
var value = va();
console.debug(value);
}
};
vm = {
something: ko.observable('hi')
};
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="validator: ['one']"></div>
<div data-bind="validator: [something()]"></div>
<div data-bind="validator: [{class: something()}, {class:'whatever'}]"></div>
All you need to set a VALID value to the key you are passing . As in you case RequiredValidator is not defined so keep that in quotes to resolve the issue .
view:
<div data-bind="validator: [{class: 'RequiredValidator'}, {class: 'EmailValidator'}]"></div>
viewModel:
ko.bindingHandlers.validator = {
init: function(element, valueAccessor) {
console.log(valueAccessor()); //check console window for o/p
}
}
ko.applyBindings();
check sample here
It turned out that it was problem with knockout-es5 modification for two-way bindings.
Original plugin is not affected. I've created pull request to address this issue.
The problem was binding preprocessing, which produced invalid code if array value was passed.
Related
I found this tutorial for a knockout utilty that filters through an array and creates a new filtered version of the array.
http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html
From there, I understand that the this.filter in "this.filter().toLowerCase();" is the ko.observable bound to the input box in the view.
I tried to integrate this into my code.
I am aware I need more changes. The method "ko.utils.stringStartsWith" is not supported any longer
I am getting the error "Uncaught TypeError: this.kofilter is not a function"
I am not sure what that means, is there something wrong with the data binding?
This is my JS code
this.filteredItems = ko.computed(function() {
console.log(this)
var filter = this.kofilter().toLowerCase();
if (!filter) {
return self.venueList();
} else {
return ko.utils.arrayFilter(this.venueList(), function(venues) {
return ko.utils.stringStartsWith(venues.name().toLowerCase(), filter) ;
});
}
}, this.venueList);
};
And this is the HTML
<br><input placeholder = "Search..." data-bind="value: kofilter, valueUpdate: 'afterkeydown'">
I'm willing to bet it's yet again a problem with this. For instance I don't think you want to pass this.venueList to the computed as the context object, but rather just this.
If you have a self variable, stick to that, and don't use this. Otherwise make sure you pass the correct this argument to all the function calls.
I found the issue, I was missing self.kofilter = ko.observable('');
Does Kendo k-ng-model support two-way binding, and if not, what is the best approach to simulate it?
A bit of context:
In angular, when an update to ng-model is done, all interested parties are updated (e.g. view is refreshed). In kendo, when using k-ng-model, I cannot find a way of doing the same, where I would want to set the value from the controller / directive directly.
Here is an example: Coding Dojo
And, just in case, the code as well:
<input kendo-auto-complete
k-ng-model="vm.kendoSelection"
ng-model="vm.angularSelection"
k-data-source="vm.countryNames"
k-data-value-field="'name'"
k-data-text-field="'name'" />
sample controller:
angular.module("MyApp", [ "kendo.directives" ])
.controller("Controller", function(){
// just some data source
this.countryNames = [
{ name: "Albania" },
{ name: "Andorra" }
];
// how to set the k-ng-model here, so that it is also propagated properly (like on
// the view, and that other events like k-on-change work)?
this.kendoSelection = { name: "Albania" };
});
EDIT:
Even after the answer from Dion Dirza, the k-on-change is not firing (altough it is a solution in good direction)
Example with k-on-change
Dion Dirza
As documentation said, that k-ng-model should store or return actual type of widget value. Therefore to make your variable kendoSelection work, you should define it as actual type of widget value, which in this case is array type for auto-complete widget.
Change your code
vm.kendoSelection = [{ name: "Albania" }];
Because you are storing object array here so your variable should be array of object. Put a caution here that your object should contains a property that defined as widget data-value-field which it will be used for comparison with current data source.
I am having some trouble trying to get knockout templates to work.
I want to use a select list that allows a person to select a value which in turns shows the template.
The template needs to have its own viewmodel properties which are different between each.
I have created a jsfiddle to show the whole thing
I have 2 very basic templates however when I try running the page I get an error. The code is not production codes its simple throw away stuff so naming conventions are not perfect :)
Error: Unable to process binding "foreach: function (){return contacts }" Message: Unable to process binding "template: function (){return { name:contactTypeId} }" Message: Unknown template type: 1
The template do exist
<script type="text/html" id="1">
<span> Family Template </span>
<input placeholder="From Mum or Dads side"/>
</script>
<script type="text/html" id="2">
<span> Friend Template </span>
<input placeholder="Where did you meet your friend"/>
</script>
I am trying to select the template via a select
<select class="form-control" data-bind="options: $root.contactTypes,
optionsText: 'type',
optionsValue:'id',
value:contactTypeId,
optionsCaption: 'Please Select...'"></select>
2 questions.
Why can it not find the template when I select it from the dropdown?
How would I bind the template to have its own model to allow me to save properties.
Update
Thanks to Georges answer below I have the template binding working. Turns out you can't use an int as an ID for a template without calling to
I have updated my model
self.contactTypeTemplateModel = ko.computed(function () {
return self.contactTypeId === 2 ? someModelWithWhereDidYouMeet : someOtherModel
});
var someModelWithWhereDidYouMeet = {something:ko.observable()};
var someOtherModel = {something:ko.observable()};
It maybe due to no sleep but I can't get this to work. The console is telling me "something is not defined"
Granted my naming is not good. I have also updated the fiddle
The problem for question #1 seems to be that you're passing in a number where it expects a string. For whatever reason, it's not being automatically coerced. This solves it.
template: { name: contactTypeId().toString() }
Even better, create a computed and add a reasonable prefix.
templateName = ko.computed(function() { return "contact-type-" + contactTypeId() })
As for passing in different models. The template binding supports the data property. Your data property can be a computed based on contactTypeId as well.
So you do your template binding with
template: {name: contactTypeTemplateName(), data: contactTypeTemplateModel() }
Where
self.contactTypeTemplateModel = ko.computed(function() {
return self.contactTypeId() === 2 ? someModelWithWhereDidYouMeet
: someOtherModel })
I should also mention, that unless you reuse these templates independently from each other in lots of places, I wouldn't recommend templates for this. I would just use an if binding.
Ok, so let me preface this by saying I'm completely new to Ember. I'm having an interesting time just trying to get a basic binding to work. Here's my relevant code:
App.js
var App = Ember.Application.create({
rootElement: '#emberApp'
});
And routes.js:
App.Router.map(function () {
});
App.IndexRoute = Ember.Route.extend({
model: function () {
return { Foo: 3 };
}
});
And then here is my HTML document:
<div id="emberApp"></div>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/handlebars.js"></script>
<script src="~/Scripts/ember-1.4.0.js"></script>
<script src="~/Scripts/EmberApp.js"></script>
<script src="~/Scripts/Routes.js"></script>
<script type="text/x-handlebars" data-template-name="index">
<div>The current foo value: {{Foo}}</div>
{{input valueBinding=Foo}}
</script>
The intended result is that when I type in the input field created, the value that is bound in the template changes at the same time. It should be fairly simple right? I have to be missing something.
The template correctly renders The current foo value: 3, and it renders a text field. However, typing anything into this field does nothing to the above binding. I have tried marking it with a valueBinding tag, as well as switching to Ember.TextField instead of a input helper. I've also tried making a custom Ember.Object.extend class and returning that as the model for the index route.
What am I missing in order to bind the text box value to the {{Foo}} value in the template?
EDIT - Ok, I've figured out it's because of the capitalization of the variable: foo works, but not Foo. Why is this?
I'm expecting to receive JSON results similar to this:
{
RemoteUserId: 0,
UserPhotoUrl: '....',
RemoteUserName: 'Bob',
}
I'm assuming this means I need to 'hide' these values by making controller wrappers for each element? ie:
remoteUserId: function() {
return this.get('RemoteUserId');
}.property()
I'm afraid you've been bitten by one of Embers naming conventions which is normally awesome as it usually means things just work, but occasionally will bite you if you're not aware of it.
Ember expects that Classes or Namespaces are capitalized and that instances are lowercase. When Ember sees the Foo property used in a binding it assumes it's a namespace and will then look for a global variable called Foo instead of a controller property.
When you use {{Foo}} in a template the behavior is slightly different as Ember will first check the current context (the controller) to see if the property exists there. If it does it uses that value, otherwise it will assume it's a namespace and look for it globally. Bindings don't work like templates due to performance concerns as you don't want to have to check two locations for a value in a binding that could be updated very frequently (like a textbox being typed in).
This is why you can use Foo in the template and it works:
<script type="text/x-handlebars" data-template-name="index">
<!-- This works! -->
<div>The current foo value: {{Foo}}</div>
</script>
But when you try to use Foo as part of a binding it won't work:
<script type="text/x-handlebars" data-template-name="index">
<!-- This doesn't work as Ember thinks Foo is global (i.e., a namespace) -->
{{input valueBinding=Foo}}
</script>
Your best bet is to just follow ember conventions and make sure all your property names start with a lowercase character. However, if you want to continue using properties in your controllers that start with a capital character then you will need to explicitly tell Ember that the property is from the controller and is not global when you try to use it in a binding:
<script type="text/x-handlebars" data-template-name="index">
<!-- Tell Ember Foo is in the controller which is what we want-->
{{input valueBinding=controller.Foo}}
</script>
Here is a Fiddle demonstrating everything written above:
http://jsfiddle.net/NQKvy/881/
Its because in Ember properties should start with a lowercase letter! Uppercase letters are globals.
so u could simple convert your JSON bevore import it into ember:
var decapitalize = function(source) {
var result = {};
for(var prop in source) {
result[prop.substr(0,1).toLowerCase() + prop.substr(1)] = source[prop];
}
return result;
};
I'm trying to use a template binding with its "data" bound to an observable. However, $data for controls inside the template is receiving "value of the observable" (and not the observable itself.)
Since I get the value, and not the observable, I'm unable to set up a 2-way binding if I'm using a template. If I set up a direct binding, the same code works without issue.
Here's a jsfiddle that highlights my problem: http://jsfiddle.net/8cDLw/
HTML
Working Example: <div id="workingExample">
<select data-bind="options: _.range(0,24), hour: MyDate"></select>
Selected Value is: <span data-bind="text: MyDate" />
</div>
<br/>Non-Working Example: <div id="notWorkingExample" data-bind="template: { name: 'hour-template', data: MyDate }"></div>
<script type="text/html" id="hour-template">
<select data-bind="options: _.range(0,24), hour: $data" />
Selected Value is: <span data-bind="text: $data" />
</script>
JavaScript
ko.bindingHandlers.hour = {
init: function(element, valueAccessor, addBindingsAccessor) {
var $el = $(element);
var curDate = valueAccessor();
if (!ko.isObservable(curDate)) {
console.log("Failure: Input Not an observable object. Data type found: " + typeof curDate + ", value: " + curDate);
}
$el.val(ko.utils.unwrapObservable(curDate).getHours());
ko.utils.registerEventHandler(element, "change", function() {
var currentDate = valueAccessor();
var hour = $el.val();
var date = ko.utils.unwrapObservable(currentDate);
date.setHours(hour);
if (ko.isObservable(currentDate))
currentDate(date);
else
console.log("Cannot update value. Input not an observable.");
});
}
};
var viewModel = function()
{
this.MyDate = ko.observable(new Date("2013-11-08T06:27:00.000Z"));
}
ko.applyBindings(new viewModel(), document.getElementById("notWorkingExample"));
ko.applyBindings(new viewModel(), document.getElementById("workingExample"));
Note the 2 divs declared: "workingExample" and "notWorkingExample" are being bound to a new instance of same viewModel (which contains one observable: a hardcoded date.)
workingExample binding is a regular custom binding receiving an observable... in this case, to manipulate "hour" part of a date (which works... i.e. if I change dropdown value, it updates the "hour" in datetime field.)
In NotWorkingExample, I'm passing the observable as "data" to a template (and trying to then use that observable witha binding identical to workingExample.)
I'm clearly missing something here, because NotWorkingExample, from my perspective, should be functionally equivalent to workingExample... the only difference being that instead of directly binding the value, I'm passing the value through template -> "data" binding.
The idea here is to have another <select> in template to have a 2-way "minute" binding as well etc. to essentially build a custom control using the template.
Can you please point out what I'm doing wrong?
EDIT:
Looking at my example, realized that I was unnecessarily complicating things:
Here's a much simpler jsFiddle: http://jsfiddle.net/3kkC5/
Any thoughts on either implementations? Thanks.
when using the data option to a template, KO automatically unwraps it for you and grabs a dependency, so the template updates when the observable data changes.
KO 3.0 does have a new $rawData context variable that is intended to give you the original observable in cases where KO has unwrapped it as part of creating a new context. However, in this case the template binding already unwraps the data option itself. This appears to be a case that we could improve in KO, as ideally $rawData would be the right choice here.
So, in your specific case, you can choose to pass an object literal with your observable as a property like:
template: { name: 'mytmpl', data: { myDate: MyDate } }
Then, you can reference the observable using myDate or whatever you want to call the property.
Here is a sample: http://jsfiddle.net/rniemeyer/a6vtD/
Just as a side note, in your fiddle you had some cases of <span /> and <select />. This can cause issues in your bindings, as they are not self-closing tags in HTML5. You will want to use <span></span> and <select></select>.