knockout <!-- ko if --> not working - javascript

So i have this code in my ui:
<!-- ko if: $data.Volume.locator.volume_labels.containername === $parent.DockerContainer.Names[0].split('/')[1] -->
<div class="ctrdisk" data-bind="css: { diskup: $data.Volume.runtime_state.RuntimeState == 'clean', diskdown: $data.Volume.runtime_state.RuntimeState != 'clean'}">
<span data-bind="text: $data.Volume.locator.volume_labels.containername === $parent.DockerContainer.Names[0].split('/')[1]"></span>
</div>
<!-- /ko -->
This somehow shows me the div class inside, even when the if statement is not true.
to test this i print the result of this statement inside of a span as yo can see.
i see both true and false.
any idea why this isn't working?

Related

How to condition appearance of div in Knockout JS based on value from $root?

I want to show/hide a div in my code based on a certain value. Using Knockout JS context debugger I found the property I need to compare in $root context. The property path I get from developer console is :
$root_toJS.items.mainItems[0].itemDescription.productId
I tried several methods, basically different variations of the 'ko if' in the following code, but nothing works:
<!-- ko if: $root.items.mainItems[0].itemDescription.productId != 1 -->
<div class="action-row">
<a href="#" data-bind="click: execute" class="btn-primary fiori3-btn-primary">
<span data-bind="text: name"></span>
</a>
</div>
<!-- /ko -->
Is there any way I can acces the value at the specified path in a 'ko if' condition?
Thank you
when you're referring to your name variable I'm assuming it's in the context of your itemDescription, so you have to make sure you're using the exact location of it also.
In my 2nd example I'm using a foreach loop to go over all the mainItems. Pay attention to the as: mainItem alias where I don't need to enter the entire thing anymore, you could also use $data but that only complicates it imo.
class ViewModel {
constructor() {
this.items = {
mainItems: [{
itemDescription: {
productId: 1,
name: 'item one',
}
}, {
itemDescription: {
productId: 2,
name: 'item two',
}
}]
};
}
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h5>this breaks any generic approach but is as your example:</h5>
<!-- ko if: $root.items.mainItems[0].itemDescription.productId !== 1 -->
<div>
<span data-bind="text: $root.items.mainItems[0].itemDescription.name"></span>
</div>
<!-- /ko -->
<!-- ko if: $root.items.mainItems[1].itemDescription.productId !== 1 -->
<div>
<span data-bind="text: $root.items.mainItems[1].itemDescription.name"></span>
</div>
<!-- /ko -->
<h5>this embraces it what is probably more what you'd want:</h5>
<!-- ko foreach: { data: $root.items.mainItems, as: 'mainItems' } -->
<!-- ko if: mainItems.itemDescription.productId !== 1 -->
<div>
<span data-bind="text: mainItems.itemDescription.name"></span>
</div>
<!-- /ko -->
<!-- /ko -->

How to change value of an array in knockout.js?

<div class="row">
<div class="col-md-6" data-bind="visible: application.candidateMostRecentAcademic" style="display:none;">
<span>Education:</span>
<!-- ko if: application.candidateMostRecentAcademic != null -->
<!-- ko with: application.candidateMostRecentAcademic[0] -->
<span data-bind="text: $data.degreeTypeName"></span>
<span data-bind="text: $data.institutionName"></span>
<span data-bind="text: $data.dateToYear"></span>
<!-- /ko -->
<!-- /ko -->
</div>
</div>
I was going to make the the column visible only if application.candidateMostRecentAcademic (array) exists.
But I also wanted to add a condition for an array that has 0 length (which is not null).
So when I tried to do,
<div class="col-md-6" data-bind="visible: application.candidateMostRecentAcademic.length != 0" style="display:none;">
it gave me an error saying that it cannot access the "length" of a null object. So, what I am trying to do is, I want to set an array of length 0 to null so that it can just be invisible just like the null object.
How can I do this with knockout data-binding?
If I understand you correctly, you want your if statement to pass if the Array is not null and if it's length is not equal to 0?
If this is the case, you could do this in your template:
<div class="col-md-6" data-bind="visible: application.candidateMostRecentAcademic && application.candidateMostRecentAcademic.length !== 0">
Add another member in viewmodel called isColumnVisible and use this member to show and hide column.
var viewModel={
isColumnVisible=true;
loadData:function(){
// code to load data;
// application= something
if(application.candidateMostRecentAcademic ===null){
this.isColumnVisible=false;
}
}
}

Knockout multiple buttons expanding their corresponding div

I am trying to show and hide a div section based on the corresponding buttons that are created through foreach loop. At the moment, whenever I click the button it shows all div sections rather than the one the button is under. I am quite new to knockout and I have spent many hours trying different methods and tutorials to resolve this issue but still unsuccessful.
This is the view section:
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="firstDiv">
<!-- ko if: $root.filteredAvailabilities().length > 0 -->
<!-- ko foreach: $root.filteredAvailabilities -->
<div class="secondDiv">
<div class="thirdDiv">
<div class="fourthDiv">
<div class="fifthDiv">
<!-- ko with: Items -->
<div class="sixthDiv">
<!-- ko if: !$root.viewPrices() -->
<input class="actionButton" type="button" data-bind="upperCaseValue: $parents[1].ViewPrices, click: $root.ViewPrices" />
<!-- /ko -->
<!-- ko if: $root.viewPrices() -->
<input class="actionButton" type="button" data-bind="upperCaseValue: $parents[1].HidePrices, click: $root.HidePrices" />
<!-- /ko -->
</div>
<!-- /ko -->
</div>
<!-- ko if: $root.viewPrices() -->
<!-- ko foreach: Rooms -->
<div class="seventhRoomDiv">
<table class="roomPriceTable">
<tr>
<td><span class="roomPriceTableRoomLabel" data-bind="text: Room.Name"></span></td>
</tr>
</table>
</div>
<!-- /ko -->
<!-- /ko -->
</div>
</div>
<!-- ko if: $root.viewPrices() -->
<div class="eighthBottomDiv">
<input class="actionButton chooseRoomButton" type="button" data-bind="upperCaseValue: $parent.ChooseRoom, click: $root.ChooseRoom" />
</div>
<!-- /ko -->
</div>
<!-- /ko -->
<!-- /ko -->
</div>
In the view model all its doing is setting viewPrices to true:
/// <summary>
/// View the prices.
/// </summary>
self.ViewPrices = function ()
{
self.viewPrices(true);
};
I just want the corresponding seventhDivRoom to display after I click the button that's attached to it rather than displaying all.
Before expanding:
After expanding - Expanded all three rather than the second one only:
EDIT
I have tried using Rafael Companhoni example and apply it to my versions, but I am coming across some difficulties to display the div. I have added
self.ShowRoomPrice = ko.observable(false);
to the view model. Then added
availability.ShowRoomPrice = false;
to the availability callback which is similar to how you created the observable array. Furthermore I have added
self.Test = function (availability){
availability.ShowRoomPrice = !availability.ShowRoomPrice
model.availability(availability);
};
Finally the view looks like this
<!-- ko if: ShowRoomPrice === true -->
<input class="actionButton" type="button" data-bind="upperCaseValue: 'Show / Hide Prices', click: $root.ChooseHotel" />
<!-- /ko -->
It does change the state of ShowRoomPrice between true and false but the div does not appear. Is there something that's still missing?
You are using the property 'viewPrices' which is in the root view model to decide if the divs should be rendered. In the example below I've added a simple view model with an observableArray of FilteredAvailabilty objects. It demonstrates how you could use each sub-view model 'viewPrices' property in the 'if' binding:
var FilteredAvailability = function (viewPrices, items, rooms) {
var self = this;
self.viewPrices = ko.observable(viewPrices);
self.Items = ko.observableArray(items);
self.Rooms = ko.observableArray(rooms);
}
var ViewModel = function() {
var self = this;
// initialize with some data
self.filteredAvailabilities = ko.observableArray([
new FilteredAvailability(true, items, rooms),
new FilteredAvailability(true, items, rooms),
new FilteredAvailability(false, items, rooms),
]);
};
And then in the HTML
<div>
<!-- ko if: filteredAvailabilities().length > 0 -->
<!-- ko foreach: filteredAvailabilities -->
<div>
<!-- ko if: viewPrices -->
<div>Depends only on each the property 'viewPrices' of each item</div>
<!-- /ko -->
</div>
<!-- /ko -->
<!-- /ko -->
</div>

Knockout save variable inside template

I have a jquery knockout template which renders a foreach.
Inside each item, i am using the same function many times (for css binding and for visibility of other child elements)
Is it possible that instead of calling the same function many times for each item in the foreach, to temporary save it and then re-use it inside the template?
PS: i know i can set the visibility of the <i class="fa " /> tags using css selectors, but this doesn't answers the question.
<script type="text/html" id="properties-template">
<!-- ko foreach: Groups -->
<!-- saving the result in a variable instead of calling it so many times -->
<!-- var isValid = isGroupValid($data); !-->
<div class="group" data-bind="css: { 'valid': isGroupValid($data) }">
<div class="iconContainer">
<!-- ko if: $root.isGroupValid($data) === false -->
<i class="fa fa-square-o"></i>
<!-- /ko -->
<!-- ko if: $root.isGroupValid($data) === true -->
<i class="fa fa-check-square"></i>
<!-- /ko -->
</div>
</div>
<!-- /ko -->
</script>
As $data is itself the viewmodel, you could just have a computed observable there which does whatever logic isGroupValid currently does. So I assume your view model at the moment looks something like
function ViewModel(){
this.isGroupValid = function(data){
// some logic returning boolean
}
}
Here's a live example demonstrating the "before": http://jsfiddle.net/hbSj7/
change it to this:
function ViewModel(){
this.isGroupValid = ko.computed(function(){
// some logic returning boolean
// just use "this" where you used to use "data"
}, this);
}
Then just change your template to, eg/
<div class="group" data-bind="css: { 'valid': isGroupValid() }">
<div class="iconContainer">
<!-- ko if: !isGroupValid() -->
<i class="fa fa-square-o"></i>
<!-- /ko -->
<!-- ko if: isGroupValid() -->
<i class="fa fa-check-square"></i>
<!-- /ko -->
</div>
</div>
Here's a live example having changed to this method: http://jsfiddle.net/ug9ax/
The important difference is in your current method the function gets executed every time you call it, with the changed method the computed only gets re-evaluated if anything it depends on changes, like other observable properties in your viewmodel.

adding a knockout if statement in view page

I wanted to add an if statement using knockout in my view page to display an item or not and this is what i have but i am not sure if i have the proper syntax:
<!--ko if: $idx.ViewModel.isGroup = false -->
<span id="Reading" class="column_title">#ViewBag.Title</span>
<!-- ko -->
In my javascript file i have:
$idx.GetGroups={
ByTime: function(url){
Ajax.Get({
....
$idx.ViewModel.isGroup = ko.observable(window.location.href.toLowerCase().indexOf("groupproject") > 0);
});
}
}
Is this the right syntax for checking knockout value in the view?
You don't need to compare to false, its a boolean expression like :
<!--ko if: !$idx.ViewModel.isGroup() -->
<span id="Reading" class="column_title">#ViewBag.Title</span>
<!-- ko -->
In your case, use ifnot
<!--ko ifnot: $idx.ViewModel.isGroup() -->
<span id="Reading" class="column_title">#ViewBag.Title</span>
<!-- ko -->
(http://knockoutjs.com/documentation/ifnot-binding.html)
to solve your problem you can test the evaluated value... like below
<!--ko if: !$idx.ViewModel.isGroup() -->
<span id="Reading" class="column_title">#ViewBag.Title</span>
<!-- ko -->
if you don't put the "()" you will converting a function in boolean...

Categories