I am using knockout and I want to compare if a value is unavailable or not. Is there a way in JavaScript/knockout I can do this?
<!-- ko if: User().details()[0] != undefined -->
<p>Your defined</p>
<!--/ko-->
I have tried undefined but that doesn't work. Anybody know what I can compare it with so it checks if the value is unavailable or not?
You need to compare the typeof the value with 'undefined'. For example:
<!-- ko if: typeof User().details()[0] !== 'undefined' -->
<p>Your defined</p>
<!--/ko-->
Please see the MDN documentation for this type of check.
And a snippet that illustrates this:
var x = {
y: 50
};
document.getElementById('output').innerHTML = 'x.y is undefined: ' + (typeof x.y === 'undefined') + '; x.z is undefined: ' + (typeof x.z === 'undefined');
<div id="output"></div>
well instead of checking weather something is undefined and If you are in a dilemma always use with (container-less in your scenario) which servers the exact purpose .
View :
<!-- ko with:$data.User1 -->
<p data-bind="text:$data"> defined</p>
<!--/ko-->
<!-- ko if:$data.User -->
<p>Your defined</p>
<!--/ko-->
View Model:
var viewModel = {
User: ko.observable('charlie')
};
ko.applyBindings(viewModel);
In view you can see i intentionally used with:user1 still your code wont break it simply stop looking further down . If you try the samething for if you get undefined error .
Well if you really want to go ahead with if then try to use a computed and return true/false based on the conditions you impose . make view cleaner
Working sample fiddle
Related
react-empty
<div data-reactroot>
<!-- react-empty: 3 -->
<!-- react-empty: 26 -->
</div>
What is this node ? Why can it render to a React Component ? How to do like this?
This is actually part of React's internal support for things like null:
// A perfectly valid component
() => null
// Also a perfectly valid component
const MyComponent = React.createClass({
render() {
if (this.props.hidden) return null;
return <p>My component implementation</p>;
}
});
Note that with React >= 16, you won't see <!-- react-empty: X --> anymore
Look at this part of React code which is create this:
var nodeValue = ' react-empty: ' + this._domID + ' ';
if (transaction.useCreateElement) {
var ownerDocument = hostContainerInfo._ownerDocument;
var node = ownerDocument.createComment(nodeValue);
ReactDOMComponentTree.precacheNode(this, node);
return DOMLazyTree(node);
} else {
if (transaction.renderToStaticMarkup) {
// Normally we'd insert a comment node, but since this is a situation
// where React won't take over (static pages), we can simply return
// nothing.
return '';
}
return '<!--' + nodeValue + '-->';
}
},
So basically if your component return null, it will create a comment which showing this element is empty, but React take care of that for you by putting a comment there like <!-- react-empty: 3 --> all JavaScript frameworks try to get use of comment in the DOM, to show they handle the code, similar one is ng-if in angular for example...
For my first stackoverflow question. Please pardon my general lack of understanding. I'm working on that.
I am auto-generating a table based on user identified dates that have different record values for each day provided. I am referencing a .JSON metagen/metadata file that defines the rows and columns, yet I cannot seem to find a way to filter out the results that are being passed as empty objects. I want to delete the rows without results. I believe I need to create a or reference a viewmodel function inside a binding, but everything I have tried so far has failed.
I have a bindings file that reads in the data and calculates the fields as they are queried based on the record names. The records are then created with knockout foreach statements and the values are taken from a Subgrid() function found in my bindings file. The data is read in as number format.
The View
<!-- ko foreach: $data.cols -->
<th data-bind="text:$data.label === undefined ? $data.value:$data.label"></th>
<!-- /ko -->
</tr>
</thead>
<tbody>
<!-- ko foreach: $data.rows -->
<tr>
<td data-bind="text:$data.label === undefined ? $data.value:$data.label"></td>
<!-- ko foreach: $parent.cols -->
<td data-class="subgridRow"></td>
<!-- /ko -->
</tr>
<!-- /ko -->
The Viewmodel
/*global define,sandbox */
define([
'scalejs.sandbox!subgrid',
], function (
sandbox
) {
'use strict';
return function (node) {
var observable = sandbox.mvvm.observable,
merge = sandbox.object.merge,
text = observable(node.text);
sandbox.flag.invoke('loadUwf');
return merge(node, {
text: text }
The return portion of the bindings file.
//remove values for missing LoB records and return the value
loop1: for (var obj in exdata){
for (var key in exdata[obj]){
if (exdata[obj][key]=== parentValue || ctx.$parent.value === "TOTAL"){
if( !text ){text = 0;}
if (ctx.$parent.value === "TOTAL"){
return {text: recalc.total.toFixed(round) + ' ' + unit,}}
else{return {text: recalc.val.toFixed(round) + ' ' + unit,}
break loop1;}
Using Knockout.js, is there a way to have an element's original content show if the observable bound to it is undefined?
<p data-bind="text: message">Show this text if message is undefined.</p>
<script>
function ViewModel() {
var self = this;
self.message = ko.observable();
};
ko.applyBindings(new ViewModel());
</script>
I know there are workarounds using visible, hidden or if but I find those too messy; I don't want the same element written out twice, once for each condition.
Also, I don't want to use any sort of default observable values. Going that route, if JS is disabled then nothing shows up. Same for crawlers: they would see nothing but an empty <p> tag.
To summarize, I want to say "Show this message if it exists, otherwise leave the element and its text alone."
The reasoning behind this is that I want to first populate my element using Razor.
<p data-bind="text: message">#Model.Message</p>
And then, in the browser, if JS is enabled, I can do with it as I please. If, however, there is no JS or the user is a crawler, they see, at minimum, the default value supplied server side via Razor.
You can simply use the || operator to show a default message in case message is undefined. Plus put the default text as content:
<p data-bind="text: message() || '#Model.Message' ">#Model.Message</p>
If javascript is disabled, the binding will be ignored and you will have the content displayed instead.
JSFiddle
Try this out
<p data-bind="text: message"></p>
<script>
function ViewModel() {
var self = this;
self.text = ko.observable();
self.message = ko.computed(function(){
if(self.text() != undefined){
return self.text();
}else{
return "Show this text if message is undefined.";
}
});
};
ko.applyBindings(new ViewModel());
</script>
You can set a default value like that in a number of ways, simplest way is to set the default observable value, as your JS file will be incorporated in to your HTML and will be able to access #Model.Message, so you can set a default value:
self.message = ko.observable(#Model.Message);
Here are a few other variations:
var viewModel = {
message: ko.observable(),
message1: ko.observable('Show this text if message is undefined.')
};
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h2>Option 1</h2>
<i>Set default value of observable: self.message1 = ko.observable(#Model.Message);</i>
<p data-bind="text: message1"></p>
<h2>Option 2</h2>
<i>Check for undefined and replace</i>
<p data-bind="text: message() ? message : message1"></p>
<h2>Option 3</h2>
<i>Use KO if syntax to display content if defined/undefined</i>
<!-- ko if: message() -->
<p data-bind="text: message"></p>
<!-- /ko -->
<!-- ko ifnot: message() -->
<p data-bind="text: message1"></p>
<!-- /ko -->
My first stack overflow question....
I'm trying to chain an all statements in protractor but I'm getting the error.
TypeError: Object [object Object] has no method 'all'
I'm looking at the API code on the following page
http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.all
Which indicates that you can use element.all(locator).all(locator)
it gives this as an example
var foo = element.all(by.css('.parent')).all(by.css('.foo'))
my code seems to be very similar, and I'm confused why I'm getting this error. I've tried structuring the code exactly like they have it on the API example. I've also tried doing element.all(locator).element.all(locator).
My GOAL here is to take a Ng-repeat of AREFS; find the one that has the text equal to r_string (which is a string generated earlier and added to the page; expect that element to exist; click that element;
Some Attempts:
var parent = element.all(by.repeater('labgroup in LabGroupService.allLabGroups'));
var child = parent.all(by.xpath('//option[text() = \'' + r_string + '\']'));
expect(child.count()).toBe('1');
and
var elem = element.all(by.repeater('labgroup in LabGroupService.allLabGroups')).all(by.xpath('//option[text() = \'' + r_string + '\']'));
expect(elem.count()).toBe('1');
Finally here is a snippet of the HTML i'm working with.
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/43">1kvub4wgCvY9QfA</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/47">3PNsny8lUMlMwBw</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/42">c3NOI7Z3933ui3a</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
Edit----------------------------------------------------------------------------------------
I'm starting to wonder if this is a version error or maybe a protractor error. In an attempt to debug I've literally included the source code from the API page.
<div id='id1' class="parent">
<ul>
<li class="foo">1a</li>
<li class="baz">1b</li>
</ul>
</div>
<div id='id2' class="parent">
<ul>
<li class="foo">2a</li>
<li class="bar">2b</li>
</ul>
</div>
and the example from the source page.
var foo = element.all(by.css('.parent')).all(by.css('.foo'))
expect(foo.getText()).toEqual(['1a', '2a'])
I'm still getting that same error.
TypeError: Object [object Object] has no method 'all'
Edit 2-------------------------------------------------------------------------------
I managed to solve this issue by adding a 'data-class = labgroup-link' into the actual html code and by using this protractor code.
element.all(by.css('[data-class="labgroup-link"]')).filter(function(elem, index) {
return elem.getText().then(function(text) {
return text === r_string;
});
}).then(function(filteredElements) {
expect(filteredElements[0].isPresent()).toBe(true);
filteredElements[0].click();
ptor.sleep(100);
});
Solution ----------------------------------------
Had to upgrade protractor to get the latest API.
Protractor >= 1.3.0
Should work given: https://github.com/angular/protractor/blob/f7c3c370a239218f6143a/lib/protractor.js#L177
var foo = element.all(by.css('.parent')).all(by.css('.foo'));
Protractor < 1.3.0
ElementArrayFinder doesn't have an all method: https://github.com/angular/protractor/blob/master/docs/api.md#api-elementarrayfinder-prototype-get therefore:
TypeError: Object [object Object] has no method 'all'
Perhaps you want to
var foo = element(by.css('.parent')).all(by.css('.foo'));
// or shorter
var foo = $('.parent').$$('.foo');
Insetad of doing
var foo = element.all(by.css('.parent')).all(by.css('.foo'));
I'm using knockout for a single page app that does some basic calculations based on several inputs to then populate the value of some html . In an attempt to keep my html concise I've used an array of objects in my viewModel to store my form. I achieved the basic functionality of the page however I wish to add a 'display' value to show on html that has formatted decimal points and perhaps a converted value in the future.
I'm not sure of a 'best practices' way of accessing the other values of the object that I'm currently 'in'. For example: If I want my display field to be a computed value that consists of the value field rounded to two decimal places.
display: ko.computed(function()
{
return Math.round(100 * myObj.value())/100;
}
I've been reading through the documentation for knockout and it would appear that managing this is a common problem with those new to the library. I believe I could make it work by adding the computed function outside of the viewmodel prototype and access the object by
viewModel.input[1].value()
However I would imagine there is a cleaner way to achieve this.
I've included a small snippet of the viewModel for reference. In total the input array contains 15 elements. The HTML is included below that.
var ViewModel = function()
{
var self = this;
this.unitType = ko.observable('imperial');
this.input =
[
{
name: "Test Stand Configuration",
isInput: false
},
{
name: "Charge Pump Displacement",
disabled: false,
isInput: true,
unitImperial: "cubic inches/rev",
unitMetric: "cm^3/rev",
convert: function(incomingSystem)
{
var newValue = this.value();
if(incomingSystem == 'metric')
{
//switch to metric
newValue = convert.cubicinchesToCubiccentimeters(newValue);
}
else
{
//switch to imperial
newValue = convert.cubiccentimetersToCubicinches(newValue);
}
this.value(newValue);
},
value: ko.observable(1.4),
display: ko.computed(function()
{
console.log(self);
}, self)
}
]
};
__
<!-- ko foreach: input -->
<!-- ko if: $data.isInput == true -->
<div class="form-group">
<div class="col-sm-6">
<label class="control-label" data-bind="text: $data.name"></label>
</div>
<div class="col-sm-6">
<div class="input-group">
<!-- ko if: $data.disabled == true -->
<input data-bind="value: $data.value" type="text" class="form-control" disabled>
<!-- /ko -->
<!-- ko if: $data.disabled == false -->
<input data-bind="value: $data.value" type="text" class="form-control">
<!-- /ko -->
<!-- ko if: viewModel.unitType() == 'imperial'-->
<span data-bind="text: $data.unitImperial" class="input-group-addon"></span>
<!-- /ko -->
<!-- ko if: viewModel.unitType() == 'metric' -->
<span data-bind="text: $data.unitMetric" class="input-group-addon"></span>
<!-- /ko -->
</div>
</div>
</div>
<!-- /ko -->
<!-- ko if: $data.isInput == false -->
<div class="form-group">
<div class="col-sm-6">
<h3 data-bind="text: $data.name"></h3>
</div>
</div>
<!-- /ko -->
If you want to read/ write to & from the same output, #Aaron Siciliano's answer is the way to go. Else, ...
I'm not sure of a 'best practices' way of accessing the other values of the object that > I'm currently 'in'. For example: If I want my display field to be a computed value that consists of the value field rounded to two decimal places.
I think there's a misconception here about what KnockoutJS is. KnockoutJS allows you to handle all your logic in Javascript. Accessing the values of the object you are in is simple thanks to Knockout's context variables: $data (the current context, and the same as JS's this), $parent (the parent context), $root(the root viewmodel context) and more at Binding Context. You can use this variables both in your templates and in your Javascript. Btw, $index returns the observable index of an array item (which means it changes automatically when you do someth. wth it). In your example it'd be as simple as:
<span data-bind="$data.display"></span>
Or suppose you want to get an observable w/e from your root, or even parent. (Scenario: A cost indicator that increases for every item purchased, which are stored separately in an array).
<span data-bind="$root.totalValue"></span>
Correct me if I'm wrong, but given that you have defined self only in your viewmodel, the display function should output the whole root viewmodel to the console. If you redefine a self variable inside your object in the array, self will output that object in the array. That depends on the scope of your variable. You can't use object literals for that, you need a constructor function (like the one for your view model). So you'd get:
function viewModel() {
var self = this;
self.inputs = ko.observableArray([
// this builds a new instance of the 'input' prototype
new Input({initial: 0, name: 'someinput', display: someFunction});
])
}
// a constructor for your 15 inputs, which takes an object as parameter
function Input(obj) {
var self = this; // now self refers to a single instance of the 'input' prototype
self.initial = ko.observable(obj.initial); //blank
self.name = obj.name;
self.display = ko.computed(obj.fn, this); // your function
}
As you mentioned, you can also handle events afterwards, see: unobtrusive event handling. Add your event listeners by using the ko.dataFor & ko.contextFor methods.
It appears as though KnockoutJS has an example set up on its website for this exact scenario.
http://knockoutjs.com/documentation/extenders.html
From reading that page it looks as though you can create an extender to intercept an observable before it updates and apply a function to it (to format it for currency or round or perform whatever changes need to be made to it before it updates the ui).
This would probably be the closest thing to what you are looking for. However to be completely honest with you i like your simple approach to the problem.