I'm printing out rows of a grid with Knockout js. I'd like my view to apply a CSS class based on wether or not the index of the iteration is divisible by 2.
This is my binding statement, called for each row:
<data-bind="css: { $index % 2 === 0 ? 'grid_row' : 'grid_a_row' }">
Two problems arises:
The use of % seems to be prohibited inside of an attribute
declaration. My IDE (VS2012) doesn't recognize anything which comes
after % as part of the string (I can see that by the difference in
the color)
Is this the correct way to apply one class or the other in
Knockout? Or do I have to write $index % 2 === 0 two times (one for each class)?
Which solutions are applicable?
Your syntax is slightly wrong. You have to use $index() % 2 === 0 and you are using the css binding incorrect. This will work:
<div data-bind="css: { grid_row: $index() % 2 === 0, grid_a_row: $index() %2 === 1 }">
http://jsfiddle.net/PYsc2/
Yes, you are correct:
Is this the correct way to apply one class or the other in
Knockout? Or do I have to write $index % 2 === 0 two times (one for each class)?
Your CSS binding syntax is incorrect, it should be:
css: { 'name-of-css-rule': function(){}, 'another-css-rule': function(){} }
In your case, you can make an observable like:
viewModel.myBooleanFunction = ko.computed(function() {
return this.foo() % 0 === 0 ? "even" : "odd";
}, viewModel);
and in your CSS binding declare it like this:
data-bind="css: myBooleanFunction"
Those are dynamic CSS rules:
From: http://knockoutjs.com/documentation/css-binding.html
Or if you prefer to use attr binding, you could use the coalesce operator like this.
data-bind="attr: { class: ($index() % 2 == 0) ? 'grid_row' : 'grid_a_row' }"
Please note that it will overwrite the class attribute completely and any other previously declared class will be lost.
Related
I've been combing through docs and I can't seem to find a simple example of a conditional in gatsby that is NOT a renderer condition.
I'm looking to compare within a mapped object, which is handled in the render method: (basic pseudo code)
class someTemplate extends Component {
render() {
const someobject = this.props.data.someobject
return (
<div id="page-wrapper">
{someobject.map((layout, i) => {
return (
<div className={(i === 0 ? (`slideshow-item shown`) : (`slideshow-item`) )}>
{if(i === 1)}
show something
{else if(i === 2)}
show something else
{else}
show default
{/if}
</div>
)
})
}
</div>
)
}
}
So the ternary you see for the className works fine. But as an example I may have 15 items in the loop and I want to make sure I set classes for the first 3 items, for example. In my findings, I see a lot of people giving examples of conditional rendering outside the return statement, but I do not want to make the whole chunk of code conditional for a few simple classes.
Is this possible in gatsby.js or do I really need to break things apart into components to achieve something so simple?
Such conditions don't have anything to do with Gatsby itself. You are now in the JSX syntax world. You can write them like this:
<div className={i === 0 ? `slideshow-item shown` : `slideshow-item`}>
{i === 1 && <span>show something</span>}
{i === 2 && <span>show something else</span>}
{i > 2 && <span>show default</span>}
</div>
Also note that you need to return a node - thus the <span> element in the above example (it could be any other valid node).
EDIT: Instead of <span> element a React.Fragment can be used to avoid extra DOM elements:
{i === 1 && <>show something</>}
I'm new to using Knockout and am doing a very basic implementation that changes the color with a observable. Is there a cleaner way to write the following code?
<div class="selected" data-bind="style: { background: fullHexCode(mainScreenNavigationSelector()) !== false ? fullHexCode(mainScreenNavigationSelector()) : 'white' }"></div>
I have this in multiple spots on my page and they all use different params for the fullHexCode() function. It looks extremely messy. Any help would be great, thanks!
It looks like the logic depends on another observable so you could use a computed observable -- in the snippet below the backgroundColor computed observable depends on the mainScreenNavigationSelector observable.
That's just a simple example, you'll have to adjust it to your specific situation.
var MyViewModel = function () {
this.mainScreenNavigationSelector = ko.observable(false);
this.backgroundColor = ko.computed(function() {
return this.mainScreenNavigationSelector() ? 'green' : 'red';
}, this);
this.toggleColor = function() {
this.mainScreenNavigationSelector(!this.mainScreenNavigationSelector());
}
}
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="selected" data-bind="style: { 'background-color': backgroundColor }">
TEST
</div>
<button data-bind="click: toggleColor">Toggle Color</button>
You may deduplicate your HTML code by defining methods in your viewmodel. Named computeds are even better as they are naturally memoized, that is evaluated only once if used repeatedly in your HTML.
You may also factorize heavy expressions as with: or let: bindings in the parent node. For example: <div class='some-container' data-bind="let: { v: mainScreenNavigationSelector() }">... bindings based on v here... </div>.
Notice: let is better than with for this purpose. But it's a new binding which will be available in the next release of KO. You can polyfill it with a custom binding.
When JS expressions can't be avoided in your HTML code, try to make them as slick as possible. For example:
<div data-bind="style: {
background: fullHexCode(mainScreenNavigationSelector()) || 'white'
}"></div>
In Javascript, logical operator doesn't return true or false but the actual value of the last evaluated arguments. So:
a || b return a if not "falsy", otherwise b
a && b return b if a not "falsy", otherwise a
The last idiom is useful in KO bindings because contrary to Angular, KO bindings are regular JS expressions. They fail if some null/undefined occurs in a dot sequence (like a.b if a is undefined).
So instead of some tertiary operator hell like data-bind="text: object != null ? (object.a != undefined ? object.a : 'None') : 'None'", just write data-bind="text: object && object.a || 'None'"
Also [] and {} are not falsy, and it's actually a good thing. It allows to write things like data-bind="foreach: foo.optionalArray || ['default', 'stuff']"
However, Number(0) is false. So beware of something like data-bind="with: object.id && 'saved' || 'new'" which may not work as expected if 0 is a valid object id.
Also this last trick. If data-bind="text: name" fails because name is undefined , then "data-bind="text: name || 'anonymous'" will still fail, but "data-bind='text: $data.name || 'anonymous'" will work as expected. As a convention, I write $data.attribute instead of attribute to convey the info about dealing with a optional attribute.
(Using KnockoutJs 2.0.0)
I have a list of phone numbers in my viewmodel. Each phone number has a type (home, work, mobile, etc). I want to display an icon (based on a fontawesome class) next to each phone number.
If I hardcode the icons in the css binding, everything works:
<tbody data-binding="foreach: phoneList">
<tr>
<td><span data-bind="css: {'icon-home' : TypeId() == 1, 'icon-building': TypeId() == 2, ... , 'icon-phone': TypeId() >= 7></span></td>
...
</tbody>
I wanted to replace the hardcoded list with a call to a function. I initially tried adding the function to the parent but had no success, so then I tried adding the function directly to the phone object itself both as a function and as a ko.computed() -- but neither of these work for me.
I've dummied up some code here that demonstrates the problem. If you inspect the span element of the table items, you'll see that it almost appears as if the data-biding is treating the returned string as an array of characters and setting the class based on indexes rather than treating the returned string as a class.
I'm sure this is something completely obvious, but I've been beating my head to no avail.
A computed observable should work just fine. The problem is what what you're returning from that computed observable. You need to return the definition of classes in the same format as the hard-coded version:
me.getClass = ko.computed(function() {
return me.typeId() == 1 ? { 'mobile': true } : { 'business': true };
});
See the updated version here: http://plnkr.co/edit/qDjgMlZpXHjn5ixY3OCt
Or, you could define a custom binding to clean up the computed function a bit, though it should be noted that in this case all classes will be replaced by the output of the binding. This is probably not necessary in Knockout 3.0.0, as alluded to in the comments and other answers.
Binding:
ko.bindingHandlers.setClass = {
update: function(element, valueAccessor, allBindings) {
var value = ko.utils.unwrapObservable(valueAccessor());
element.className = value;
}
};
Observable:
me.setClass = ko.computed(function() {
return me.typeId() == 1 ? "mobile" : "business";
});
HTML:
<td data-bind="setClass: setClass, text: typeId"></td>
A version using a custom binding is here: http://plnkr.co/edit/ryaA4mIf7oh5Biu8bKj0?p=info
Fix
Example
I updated your version of KO to 3.0.
Next, I changed your ko.computed binding for getClass from:
me.getClass = ko.computed(function() { return me.typeId == 1 ? "mobile" : "business"; });
to:
me.getClass = ko.computed(function() { return this.typeId() == 1 ? "mobile" : "business"; }, me);
Note
There may be a way to do this with KO 2.0, but I couldn't find documentation for previous versions. I imagine the issue is related to syntax if the feature exists.
An alternate way to do this is use an attr data-bind, instead of using a custom binding handler to set the class on the element.
So, you would still need to use a computed to set the observable:
me.setClass = ko.computed(function() {
return me.typeId() === 1 ? "mobile" : "business";
});
Then use an attr binding to set the class on the html element:
<td data-bind="attr: { class: setClass }, text: typeId"></td>
Data come from db , and this html element is a ng-repeat
{{count}}
My Question
How can i show if else condition that,
If count is null (nothing count value) I would show 0 (count value should be zero) , else show original count value
I hope there are solution like {{count if '' else ''}}
You could have have logic in your view with {{count || 0}} or {{ count ? count : 0 }} but this makes maintainability more difficult and duplicates logic.
A better option would be to create a custom filter. This will allow you to define you condition in a single place and makes it re-usable in many directives but also testable.
.filter('isEmpty', function() {
return function (data) {
return (data === null || data === undefined) ? 0 : data;
}
});
And you would use it like
{{item | isEmpty}}
See fiddle: http://jsfiddle.net/fhfsex4v/2/
EDIT: Here's the docs for filter that will explain what its doing and also how to pass optional extra parameters into your filters and other cool stuff.
https://docs.angularjs.org/guide/filter
How about {{ count ? count : 0 }}
If I have an expression {{ x }} and x is undefined or null, then how can I display a placeholder for it?
I provided one solution in my answer, but I would like to know what other ways there are.
Maybe, also for placeholder for promises.
{{ counter || '?'}}.
Just pure javascript. || can be used as default value. Since it would be different empty messages in each, a generalized directive would not be suitable for many cases.
If you want to apply a different class to empty ones, that's also built-in:
<div ng-class="{empty: !counter}" ng-bind="counter || ?"></div>
I would do it like this, but maybe there is a better way:
angular.module('app').filter('placeholdEmpty', function(){
return function(input){
if(!(input == undefined || input == null)){
return input;
} else {
return "placeholder";
}
}
});
and then use {{ x | placeholdEmpty}}
I do it with ng-show, like this:
<strong>{{username}}</strong>
<span class="empty" ng-show="!username">N/A</span>
Sure, it adds a lot more elements to my view that I might be able to handle differently. I like though how easy it is to clearly see where my placeholder/empty values are, and I can style them differently as well.
Implement default filter:
app.filter('default', function(){
return function(value, def) {
return (value === undefined || value === null? def : value);
};
});
And use it as:
{{ x | default: '?' }}
The advantage of the filter solution over {{ x || '?'}} is that you can distinguish between undefined, null or 0.
Implementing default-ish filters works, but if you're using only numbers you can use angular's own number filter
If the input is null or undefined, it will just be returned. If the
input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or
'-∞' is returned, respectively. If the input is not a number an empty
string is returned.
{{ (val | number ) || "Number not provided"}}