Bind string value to checkbox checked property - javascript

I would like to bind string value to checked property. Web Service returns non boolean values like "yes"-"no" or "0"-"1" but I dont' know how to manipulate them.
In documentation appear:
For checkboxes, KO will set the element to be checked when the parameter value is true, and unchecked when it is false. If you give a value that isn’t actually boolean, it will be interpreted loosely. This means that nonzero numbers and non-null objects and non-empty strings will all be interpreted as true, whereas zero, null, undefined, and empty strings will be interpreted as false.
When the user checks or unchecks the checkbox, KO will set your model property to true or false accordingly.
I understand the above explanation but... How can I change the default behavior of binding to translate string values to boolean values?
var viewModel = function () {
var self = this;
self.username = ko.observable("william wallace");
self.email = ko.observable("ww#mailbox.com");
self.terms = ko.observable("false");
self.send = function (data) {
console.log(ko.toJSON(data));
};
}
var vm = new viewModel();
ko.applyBindings(vm);
Full sample:
http://jsfiddle.net/rferreiraperez/d5yb1krt/

Using a writable dependent observable can be very useful here, particularly for conversions/validation. You could create a computed observable that can act as the conversion layer to your observable. Your observable can still hold the value you expect to use ("yes" or "no" values) and the observable can help convert values to and from the desired types.
The computed observable can be created using an extender to help make it reusable.
ko.extenders.converter = function (target, options) {
var name = options.name || 'converted',
fromValue = options.fromValue || passthrough, // from target value (read)
toValue = options.toValue || passthrough; // to target value (write)
target[name] = ko.dependentObservable({
read: function () {
return fromValue(target());
},
write: function (value) {
target(toValue(value));
}
});
return target;
function passthrough(value) { return value; }
};
With this, you can create your functions to convert the values and extend the observable.
function BooleanConverter(trueValue, falseValue, name) {
this.name = name || 'converted';
this.fromValue = function (value) {
switch (value) {
case trueValue: return true;
case falseValue: return false;
}
}.bind(this);
this.toValue = function (value) {
return !!value ? trueValue : falseValue;
}.bind(this);
}
// ...
self.terms = ko.observable("no").extend({
converter: new BooleanConverter("yes", "no")
});
With the terms extended to have a converted value, you can bind to that in your view.
<p>
<input type="checkbox" name="termsCheckBox" data-bind="checked: terms.converted" />
<label for="termsCheckBox">accept terms and conditions</label>
</p>
fiddle

Related

Validate breeze complex-type without validate the entire entity

When you want to validate breeze-entity you write:
this.entityAspect.validateEntity()
But what about if I want to fire validations only for complex-type, without fire the entire-entity validations?
complexType.complexAspect not have method validateEntity.
So, what should I do?
Edit after I saw Jay answer:
I tried to use method validateProperty.
But the result was that it always returns true, becouse it not check each one of the properties.
So, I tried to call method validateProperty several-times, each time for other field of the complexType. It give me boolian-result of valid/not valid, but not update the validation-errors.
Here is the code that I tried after I saw Jay answer, but it is not help:
validateSingleField(myComplexProertyName);
first version of validateSingleField function: (the result was that it always returns true, becouse it not check each one of the properties)
function validateSingleField(object, fieldName) {
var entityAspect = object.entityAspect;
var objectType = object.entityType;
var prop = objectType.getProperty(fieldName);
var value = object.getProperty(fieldName);
if (prop.validators.length > 0) {
var context = { entity: entityAspect.entity, property: prop, propertyName: fieldName };
if (entityAspect._validateProperty(value, context)) {
return true;
}
return false;
}
}
second version:(It give me boolian-result of valid/not valid, but not update the validation-errors.)
function validateSingleField(object, fieldName) {
var aspect = object.entityAspect || object.complexAspect;
var entityAspect = object.entityAspect || object.complexAspect.getEntityAspect();
var objectType = object.entityType || object.complexType;
var prop = objectType.getProperty(fieldName);
if (prop.isComplexProperty) {
var isOk;
objectType.getProperties().forEach(function (p) {
isOk = isOk && validateSingleField(object[fieldName](), p.name)//writing 'object[fieldName]()' - is for send the entire complexType of the entity
});
return isOk;
}
else {
{
var value = object.getProperty(fieldName);
if (prop.validators.length > 0) {
var context = { entity: entityAspect.entity, property: prop, propertyName: fieldName };
if (entityAspect._validateProperty(value, context)) {
return true;
}
return false;
}
}
}
}
There is no separate method to validate a complex type because the validation results are all part of the 'parent' entity. Complex type properties are considered part of the entity, not independent entities.
What you can do is call validateProperty on the 'complex' property of the parent entity.

AngularJS ignoring a key in an watched object or overriding the $watch listener

I'm deep watching a property that is bound to multiple controls:
$scope.$watch('config', function(){}, true);
the config itself contains various parameters:
scale
point
aggregates
current
I want to ignore changes to scale when it is changed by a specific control and a specific function.
Is there a way to ignore a specific property or override the watch is specific cases?
For now this is what i'm doing:
The dataChange now fires only on certain changes, in this case when other properties,
not zoom are changing.
In order to disable the dataChange for a specific zoom case i just assigned it to the rest of the cases.
I'm using Switch and not if/else just because it's more descriptive and easily extendable for more cases.
$scope.$watch('config', function(n,o,scope){
$scope.config = n;
if (n != o) {
switch(true){
case n.zoom != o.zoom:
break;
default:
$scope.dataChange($scope.dataTable);
};
}
}, true);
I don't like any of these answers. The first parameter of $watch is what to watch, which accepts the property name as a string, or a function to return the value. Simply use a function & return the value you want to watch. Here I use lodash JS library to $watch a new object that is based on the real object, but with the property stripped:
$scope.$watch(function() {
return _.omit($scope.config, 'scale');
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
Without Lodash [blacklist properties]:
$scope.$watch(function() {
var myConfig = Angular.copy(config);
delete myConfig.scale;
return myConfig;
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
Without Lodash [whitelist properties]:
$scope.$watch(function() {
return {
point: $scope.config.point,
aggregates: $scope.config.aggregates,
current: $scope.config.current
};
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
In my opinion the other answers are not the "Angular way". This approach is not only more succinct than the other answers which are messy, but also avoids performing a redundant object comparison when the $watch fires. Keep in mind the other answers incur the cost of object comparison twice, once for the $watch itself in the Angular code, then you incur the cost of your "home made" object comparison in your callback function. My approach ensures the object comparison is only incurred once, in the Angular code, by stripping the unwanted property before feeding the object into the $watch for comparison.
Not as far as I know, but a simple check would do the trick:
$scope.$watch('config', function(newValue, oldValue){
if (newValue.scale == oldValue.scale) {
// ignore this
return;
}
// continue...
}, true);
Better solution can be that function;
$scope.equalsAdvanced=function (sourceObject, targetObject, ignoredProperties)
{
// direct compare if there is no ignored properties
if (!ignoredProperties || (angular.isArray(ignoredProperties) && ignoredProperties.length<=0)) {
return angular.equals(sourceObject, targetObject);
}
// take the original ignored property list to a new variable
var ignoredPropertyList=ignoredProperties;
// make it array if it is not
if (!angular.isArray(ignoredPropertyList)) {
var list = [];
list.push(ignoredPropertyList);
ignoredPropertyList = list;
}
// compare property list
for (propertyName in sourceObject) {
if (ignoredPropertyList.indexOf(propertyName) >= 0)
continue;
var sourceValue = sourceObject[propertyName];
var targeValue = targetObject[propertyName];
if (!angular.equals(sourceValue, targeValue))
return false;
}
return true;
};
You can check the example code on fiddle:
http://jsfiddle.net/tursoft/DpEwV/4/
This can be the better than previous;
CODE:
// service
myApp
.service("utils", function()
{
self=this;
// watchAdvanced =====================
self.$watchAdvanced = function ($scope, exp, ignoredProperties, callback)
{
$scope.$watch(exp, function (newValue, oldValue) {
if (self.equalsAdvanced(newValue, oldValue, ignoredProperties))
return;
callback(newValue, oldValue);
}, true);
}
// equalsAdvanced =====================
self.equalsAdvanced=function (sourceObject, targetObject, ignoredProperties)
{
// direct compare if there is no ignored properties
if (!ignoredProperties || (angular.isArray(ignoredProperties) && ignoredProperties.length<=0)) {
return angular.equals(sourceObject, targetObject);
}
// take the original ignored property list to a new variable
var ignoredPropertyList=ignoredProperties;
// make it array if it is not
if (!angular.isArray(ignoredPropertyList)) {
var list = [];
list.push(ignoredPropertyList);
ignoredPropertyList = list;
}
// compare property list
for (propertyName in sourceObject) {
if (ignoredPropertyList.indexOf(propertyName) >= 0)
continue;
var sourceValue = sourceObject[propertyName];
var targeValue = targetObject[propertyName];
if (!angular.equals(sourceValue, targeValue))
return false;
}
return true;
};
});
USAGE:
utils.$watchAdvanced($scope, "user", ["_State", "ID"], function(newValue, oldValue)
{
$scope.changeCount+=1;
$scope.logs.push($scope.changeCount + ": User object is changed!");
}, true);
Source Code on Fiddle:
http://jsfiddle.net/tursoft/5rLfr/2/

JS - Change value of all instances of object

My instances of some object has some value called selected and method select(). When method select() is fired, I want object's selected value to be set true, but selected value of all other instances of this object to be false - how to do it?
In other words - How to change value of all instances of some object?
var Puzzel = function() {
this.selected = false;
};
Puzzel.prototype = {
select: function{
this.selected = true;
//how to set selected = false on every other instance of Puzzel
}
}
If you can rely on getters/setters (see compatibility) then the below will work.
This approach has constant overhead when selecting or checking selection, and constant memory overhead.
var Selectable = function () {
// Define your constructor normally.
function Selectable() {
}
// Use a hidden variable to keep track of the selected item.
// (This will prevent the selected item from being garbage collected as long
// as the ctor is not collectible.)
var selected = null;
// Define a getter/setter property that is true only for the
// item that is selected
Object.defineProperty(Selectable.prototype, 'selected', {
'get': function () { return this == selected; },
// The setter makes sure the current value is selected when assigned
// a truthy value, and makes sure the current value is not selected
// when assigned a falsey value, but does minimal work otherwise.
'set': function (newVal) {
selected = newVal ? this : this == selected ? null : selected;
}
});
// Define a select function that changes the current value to be selected.
Selectable.prototype.select = function () { this.selected = true; };
// Export the constructor.
return Selectable;
}();
You would need to keep track of those instances. Here's one way of doing it:
(function() {
var instances = [];
window.MyClass = function() {
instances.push(this);
// rest of constructor function
};
window.MyClass.prototype.select = function() {
for( var i=0, l=instances.length; i<l; i++) instances[i].selected = false;
this.selected = true;
};
})();

How to define a custom binding who use previous value to determine class in Knockout?

I need to bind a table with knockout, and I would like the table cell to get a different css class if the new value is higher or lower of the previous.
I have in mind different possibilities, such as storing the previous value in the bindingContext and have a function which returns the right class, but is it possible to add a custom binding handler which receives the previous value and the new value?
Although Jeff's and Sławomir's answers would work, I found an alternative that doesn't need any change to the view model nor relies on altering the DOM element object.
function subscribeToPreviousValue(observable, fn) {
observable.subscribe(fn, this, 'beforeChange');
}
ko.bindingHandlers['bindingWithPrevValue'] = {
init: function (element, valueAccessor) {
var observable = valueAccessor();
var current = observable();
console.log('initial value is', current);
subscribeToPreviousValue(observable, function (previous) {
console.log('value changed from', previous, 'to', current);
});
}
};
Naturally, that will only work if the bound property is an observable.
I looked into knockout source and I suppose that you can't access previous value inside update method of bindingHandler but you can store it inside element
ko.bindingHandlers['bindingWithPrevValue'] = {
update: function (element, valueAccessor) {
var prevValue = $(element).data('prevValue');
var currentValue = valueAccessor();
$(element).data('prevValue', currentValue());
// compare prevValue with currentValue and do what you want
}
};
What you could do is create an extender to extend the observables that you wish to track the previous values of. You could then inspect the previous value to do as you wish.
Just pass in the name of the property that will hold the previous value.
ko.extenders.previousValue = function (target, propertyName) {
var previousValue = ko.observable(null);
target[propertyName] = ko.computed(previousValue);
target.subscribe(function (oldValue) {
previousValue(oldValue);
}, target, 'beforeChange');
return target;
};
Then to use it:
function ViewModel() {
this.value = ko.observable('foo').extend({ previousValue: 'previousValue' });
}
var vm = new ViewModel();
console.log(vm.value()); // 'foo'
console.log(vm.value.previousValue()); // null
vm.value('bar');
console.log(vm.value()); // 'bar'
console.log(vm.value.previousValue()); // 'foo'
In your case, you could probably use something like this:
function TableCell(value) {
this.value = ko.observable(value).extend({ previousValue: 'previousValue' });
this.cssClass = ko.computed(function () {
// I'm assuming numbers
var current = Number(this.value()),
previous = Number(this.value.previousValue());
if (current < previous)
return 'lower';
else if (current > previous)
return 'higher';
}, this);
}

Was variable x ever true?

In Javascript, is there a good way to check if a variable was ever a true, (or any value), in the entire session? The best way I can think of right now is to perform a regular check like this, recording the truthiness in another variable:
if (variable){
variablewasevertrue = true;
}
Then when I want to know if the original variable was ever true, I check if the new variablewasevertrue is true or undefined. There's nothing more graceful like if (variable was ever true){ is there? That doesn't seem very Javascript-y.
No there is no if (variable was ever true) facility in the language. Variables store values, not history.
Intercepting values as they're assigned and checking is the only way to do it. If the variable is really a property (e.g. a global variable is a property of the global object) you can intercept changes easily using setters.
So to have a history keeping global variable you could do
var hasEverBeenTruthy = false;
(function () {
var value;
Object.defineProperty(window, "myGlobal", {
"get": function () { return value; },
"set": function (newval) {
if (newval) { hasEverBeenTruthy = true; }
value = newval;
}
});
})();
This will work on modern browsers, and there are __defineSetter__ variants on many older browsers.
Variables store value, not a history of a memory location. If you want to do something like this, I would suggest you use an Object of some sort:
var x = {
value: false,
history: [],
set: function(val){
history.push(this.value);
this.value = val;
},
wasEver: function(val){
return this.history.indexOf(val) >= 0;
}
};
Then you can use the object like so:
x.set(false);
x.value; //returns false
x.set(true);
x.value; //returns true
x.wasEver(true); // returns true
x.wasEver(false); //returns true
x.wasEver("hello"); //returns false
This gives each object it's own history (as in, it can check multiple values, not just one - as with the getter/setter stuff mentioned in other answers), and is guaranteed to work in any scope, as all functionality is contained within the defined object.
No, except that you could use a getter and setter like this, which delegates the setting of a variable so that you can check whether it is to set at one time:
var value,
wasevertrue = false;
window.__defineSetter__('test', function(v) {
value = v;
wasevertrue = wasevertrue || (v === true);
});
window.__defineGetter__('test', function() {
return value;
});
Now,
test = false; // wasevertrue === false
test = true; // wasevertrue === true
test = false; // wasevertrue === true
Better yet would be putting this in a closure because you can now just set value = true as a workaround to the setter.
no - there is no state tracking on variables. it is only whatever its current value is. beyond that its your own custom implementation using property-like methods for state tracking.
Have another variable called "wasevertrue = false." Anywhere you set "variable" immediately follow it with a check that sees if variable == true. If it is, set wasevertrue = true.
You can't use a single scalar variable to track history, but you could use an array. It's not ideal, but it's an alternative:
function setVar(arr, value) {
arr.unshift(value);
return arr;
}
function getVar(arr) {
return arr[0];
}
function checkHist(arr, histValue) {
var ii;
for (ii = 0; ii < arr.length; ii++) {
if (arr[ii] === histValue) {
return true;
}
}
return false;
}
var myVar = [];
myVar = setVar(myVar, true);
myVar = setVar(myVar, false);
alert(checkHist(myVar, true)); // alerts "true"

Categories

Resources