Knockout: Change css class based on value of observable - javascript

I use foreach on an observable array:
<div id="mainRight" data-bind="foreach: notifications">
<div class="statusRow">
<div class="leftStatusCell">
<div class="leftStatusCellColor" data-bind="css: availabilityCssClass($data.availability)"></div>
</div>
<div class="topRightStatusCell" data-bind="text: sip"></div>
<div class="bottomtRightStatusCell ellipsisSingleline" data-bind="text: note"></div>
</div>
</div> <!== end mainRight ==>
As you can see, I pass the current value of availability to the function availabilityCssClass, which compares the value to some predefined strings. Depending on the matching string, it returns a class name.
self.availabilityCssClass = ko.computed(function (value) {
var availability = value;
if (availability === "Busy" || "DoNotDisturb" || "BeRightBack")
return "leftStatusCellColorOrange";
else if (availability === "Away" || "Offline")
return "leftStatusCellColorRed";
else
return "leftStatusCellColorGreen";
});
This is my model. The data comes from an external data source.
function Notification(root, sip, availability, note) {
var self = this;
self.sip = ko.observable(sip);
self.availability = ko.observable(availability);
self.note = ko.observable(note);
};
self.notifications = ko.observableArray();
However, it doesnt work as is. When the computed function is not commented out, the foreach does not iterate over the data and the div is empty. But I can see that the viewModel is not empty.

You cannot pass value into computed in such way. It is better to add this computed to Notification view model and use self.availability property:
function Notification(root, sip, availability, note) {
var self = this;
self.sip = ko.observable(sip);
self.availability = ko.observable(availability);
self.note = ko.observable(note);
self.availabilityCssClass = ko.computed(function() {
var availability = self.availability();
if (["Busy", "DoNotDisturb", "BeRightBack"].indexOf(availability) != -1) return "leftStatusCellColorOrange";
else if (["Away", "Offline"].indexOf(availability) != -1) return "leftStatusCellColorRed";
else return "leftStatusCellColorGreen";
});
};
Your if statement wasn't correct, so I fixed the logic. Here is working fiddle: http://jsfiddle.net/vyshniakov/Jk7Fd/

You just need to make availabilityCssClass a function. As you've written it, it's not a computed observable since it has no observable dependencies.
self.availabilityCssClass = function (value) {
var availability = value;
if (availability === "Busy" || "DoNotDisturb" || "BeRightBack")
return "leftStatusCellColorOrange";
else if (availability === "Away" || "Offline")
return "leftStatusCellColorRed";
else
return "leftStatusCellColorGreen";
};

The CSS binding wants a object literal with the name of the CSS class as member name and the value true or false depeding on you want to remove or add the class
data-bind="css: { 'css-class-name': true }"
edit: Hmm, they have changed the css binding in 2.2 ;)

Related

How to check for an empty object in an AngularJS view

In the controller's scope I have something like:
$scope.card = {};
In the view I must check if my object is still an empty literal, {}, or if it contains some data in some fields.
I tried this:
ng-show="angular.equals({}, card)"
and
ng-show="!angular.equals({}, card)"
but it didn't work.
Are there any better ways? How do you check if an object is empty or if it contains some fields?
You can use: Object.keys(card).length === 0
But make sure you use it from a method of the controller as the Object is not available in the view, like:
$scope.isObjectEmpty = function(card){
return Object.keys(card).length === 0;
}
Then you can call the function from the view:
ng-show="!isObjectEmpty(card)"
Use json filter and compare with '{}' string.
<div>
My object is {{ (myObject | json) == '{}' ? 'Empty' : 'Not Empty' }}
</div>
ng-show example:
<div ng-show="(myObject | json) != '{}'"></div>
Try this:
angular.equals({}, $scope.card)
Create a function that checks whether the object is empty:
$scope.isEmpty = function(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
};
Then, you can check like so in your html:
ng-show="!isEmpty(card)"
http://plnkr.co/edit/u3xZFRKYCUh4D6hGzERw?p=preview
Because angular is not available from the scope, you can pass it to your controller scope.
$scope.angular = angular;
please try this way with filter
angular.module('myApp')
.filter('isEmpty', function () {
var bar;
return function (obj) {
for (bar in obj) {
if (obj.hasOwnProperty(bar)) {
return false;
}
}
return true;
};
});
usage:
<p ng-hide="items | isEmpty">Some Content</p>
Via from : Checking if object is empty, works with ng-show but not from controller?
This will do the object empty checking:
<div ng-if="isEmpty(card)">Object Empty!</div>
$scope.isEmpty = function(obj){
return Object.keys(obj).length == 0;
}
You should not initialize your variable to an empty object, but let it be undefined or null until the conditions exist where it should have a non-null/undefined value:
$scope.card;
if (someCondition = true) {
$scope.card = {};
}
Then your template:
ng-show="card"
You can use plain javascript Object class to achieve it,
Object class has keys function which takes 1 argument as input as follows,
Object.keys(obj).length === 0
You can achieve it in 3 ways,
1) Current controller scope
2) Filter
3) $rootScope
1) First way is current controller scope,
$scope.isObjEmpty = function(obj){
return Object.keys(obj).length === 0; }
Then you can call the function from the view:
ng-show="!isObjEmpty(obj)" if you want to show and hide dom dynamically &
ng-if="!isObjEmpty(obj)" if you want to remove or add dom dynamically.
2) The second way is a custom filter. Following code should work for the object & Array,
angular.module('angularApp')
.filter('isEmpty', [function () {
return function (obj) {
if (obj == undefined || obj == null || obj == '')
return true;
if (angular.isObject(obj))
return Object.keys(obj).length != 0;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
};
}]);
<div ng-hide="items | isEmpty"> Your content's goes here </div>
3) The third way is $rootScope,
create a plain javascript function and add it in $rootScope server it will accessible default in all scopes and UI.
function isObjEmpty (obj){
return Object.keys(obj).length === 0; }
$rootScope.isObjEmpty = isObjEmpty ;
Create a $scope function that check it and returns true or false, like a "isEmpty" function and use in your view on ng-if statement like
ng-if="isEmpty(object)"
I have met a similar problem when checking emptiness in a component. In this case, the controller must define a method that actually performs the test and the view uses it:
function FormNumericFieldController(/*$scope, $element, $attrs*/ ) {
var ctrl = this;
ctrl.isEmptyObject = function(object) {
return angular.equals(object, {});
}
}
<!-- any validation error will trigger an error highlight -->
<span ng-class="{'has-error': !$ctrl.isEmptyObject($ctrl.formFieldErrorRef) }">
<!-- validated control here -->
</span>
I had to validate an empty object check as below
ex:
<div data-ng-include="'/xx/xx/xx/regtabs.html'" data-ng-if =
"$parent.$eval((errors | json) != '{}')" >
</div>
The error is my scope object, it is being defined in my controller as $scope.error = {};
A good and effective way is to use a "json pipe" like the following in your HTML file:
<pre>{{ yourObject | json }}</pre>
which allows you to see clearly if the object is empty or not.
I tried quite a few ways that are showed here, but none of them worked.
If you plan on using a lot of the methods and properties of Object in your Angular JS app, why not use:
angular.module('core').run(function ($rootScope) {
$rootScope.Object = window.Object;
});
Then in your view, you could do something like:
<div ng-if="Object.keys(fooBar).length > 0"></div>
You have to just check that the object is null or not. AngularJs provide inbuilt directive ng-if. An example is given below.
<tr ng-repeat="key in object" ng-if="object != 'null'" >
<td>{{object.key}}</td>
<td>{{object.key}}</td>
</tr>

Bind string value to checkbox checked property

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

Knockoutjs subscribe event , ko.computed execution timing issue

I want to set different value to self.selectedTitleId() in knockoutjs when self.selectedQueryId changes, so i have added a subscribe to selectedQueryId.
I have another computed variable self.text which format the self.selectedTitleId with other variables.
My problem is , when i change the selectedQueryId value from UI, computed function gets called first, followed by subscribe call. Because of this, the text that i am trying to display always holds the previous selection value.
I want to hold the self.text computed function execution until selectedTitleId.subscribe function is completed so that self.selectedTitleId has current value.
Can someone help me? Thanks for your time!
Below is the html component which is used to bing selectedTitleId value with UI. backend js always shows the 'backendName' as value, even though i tried to set a different value using self.selectedTitleId("newValue").
html:
var sformat = (function() {
var pattern = /\{\{|\}\}|\{(\d+)\}/g;
return function () {
var parameters = arguments;
if(parameters[0]) {
console.log(parameters[0])
return parameters[0].replace(pattern, function (match, group) {
var value;
if (match === "{{")
return "{";
if (match === "}}")
return "}";
value = parameters[parseInt(group, 10) + 1];
return value ? value.toString() : "";
});
}
};
});
function test() {
return sformat.apply(this, arguments);
}
self.selectedTitleId = ko.observable('');
self.text = ko.computed(function () {
console.log("inside text function")
if (self.selectedTitleId && self.selectedQueryId()) {
console.log(self.selectedTitleId)
self.displayField = test(self.selectedTitleId, self.selectedQueryId(),self.queryValue());
}else if(self.selectedTitleId && self.selectedQueryId() && self.queryGreaterValue() && self.queryLesserValue()){
self.displayField = test(self.selectedTitleId, self.selectedQueryId(),self.queryValue(),self.queryGreaterValue(),self.queryLesserValue());
}
return self.displayField;
});
self.selectedQueryId.subscribe(function (newValue) {
$.getJSON("json/queries.json", function (allData) {
var mappedData = $.map(allData, function (item) {
if(item.DisplayName == "Price"){
if(newValue == "range") {
self.selectedTitleId(item.RangeBackEndFieldName);
console.log("range");
console.log(item.RangeBackEndFieldName); //Prints new string
console.log(self.selectedTitleId()); //Print old value-
}else if(newValue == "$gt:" || newValue == "$lt:"){
self.selectedTitleId(item.BackendFieldName);
});
}
}
});
});
});
Unless there is something else you are not telling us, it doesn't make sense for selectedTitleId to be a ko.computed. Just use a regular observable:
self.selectedTitleId = ko.observable();
self.selectedQueryId.subscribe(function (newValue) {
$.getJSON("json/queries.json", function (allData) {
var mappedData = $.map(allData, function (item) {
if(item.DisplayName == "Price"){
if(newValue == "range") {
self.selectedTitleId(item.RangeBackEndFieldName);
});
}else if(newValue == "$gt:" || newValue == "$lt:"){
self.selectedTitleId(item.BackendFieldName);
});
}
}
});
});
});
Now when selectedTitleId is changed in your callback, it should trigger text to re-evaluate.
The problem with your original wasn't that it was updating text first, it was that it wasn't re-evaluating when you changed selectedTitleId. See here:
if (self.selectedTitleId() && self.selectedQueryId()) {
This means your computed property is dependent on both selectedTitleId and selectedQueryId, updating either will cause the function to run again. But in your original code, you completely replaced self.selectedTitleId with an entirely new function, but your computed is still dependent on the old one (which is unchanged).

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.

function not visible in function expressions, how to fix this?

I have a function expression like this :
var inputChecker = function(field) {
return function() {
if(field === '' || field === 'undefined' || field === null) {
return false;
}
return true;
}
}
that I want to use in several different function expressions :
(function($) {
if(inputChecker(x)) {}
})(jQuery);
(function($) {
})(jQuery);
But the problem is inputChecker is not visible in these function expressions when it's declared out of their bodies ? I don't understand why? Isn't inputChecker supposed to be global ?
Dystroy's answer is definitely simpler. But if you want it your way...
The return value of the inputChecker is a function, not boolean. If you want to call the returned function, use () expression:
var fn = inputChecker(x); // gets the function
fn(); // calls the returned function
or shorter
inputChecker(x)();
In your code
(function($) {
if(inputChecker(x)()) {
// custom code here if x is defined
}
})(jQuery);
Note: if you want to check if variable is not undefined, strip the apostrophes - undefined is constant, not string
if(field===undefined)
What you wrote is a function factory. It doesn't return a boolean but a function able to check a property.
This kind of functions is sometimes useful but :
you're here, in the returned function, checking the value of the property received by the factory. As this value can't change (it's embedded in the closure), the produced function holds no more information than just true or false. So it's useless.
you're calling inputChecker(x) as if it was a boolean instead of a function.
So what you probably want is simply
var checkInput = function(field) {
if(field === '' || field === 'undefined' || field === null){
return false;
}
return true;
}
But if you really want to generate different checking functions, dependent on another value, you could use the function factory pattern like this:
var x = true;
var checkInput = (function (x) {
if (x === true) {
return function(field) {
if(field === '' || field === 'undefined' || field === null){
return false;
}
return true;
}
} else {
return function(field) {
//evaluate field differently
}
}
}(x));
Now, dependig on what x is, one or another function will be assigned to checkInput.

Categories

Resources