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

<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;
}
}
}

Related

ngIf check if object is empty [done]

i have a list name "day".
export class Day{
id:number;
name: string;
items: Object;
}
i want to display "Hello" when items is empty. I try to used *ngIf but it doesn't work.
This is my code.
<div class=" list" *ngFor="let day of days">
<div class="list-name">
<strong>
{{day.name}}
</strong>
<div *ngIf= "day.items === 0">
Hello
</div>
</div>
<div *ngFor="let item of day.items">
<div class="column" [style.background-image]="'url(' + item.photoPath + ')'">
<h4 class="item-name">{{item.name}}</h4>
</div>
</div>
</div>
You have to use length to check the array size
<div *ngIf= "day.items?.length === 0">
Try this
<div *ngIf= "day.items.length === 0">
You can use <div *ngIf= "day.items == null || day.items.length === 0"> for check items is null or empty.
Demo https://stackblitz.com/edit/angular-scssrw
As I seem items is not an array. It is an object. Using ngFor over an Object would resuslt in an error.
Secondly, you should use .length over an array to get the count of Objects. so it should be as,
<div *ngIf= "day?.items?.length === 0">
You can simply make use of conditional statement expression inside your interpolation without using 'ngIf'.
...
<strong>
{{ (day.item && day.item?.length) ? day.name : day.name + 'Hello' }}
</strong>
...
Actually the question is about finding the length of object that is only possible using Object.keys(myObj).length. In Angular you can use it as:
in .ts file:
objectKeys = Object.keys;
in html file:
<div *ngIf="objectKeys(myObj).length > 0"></div>
Here is the reference:
https://stackblitz.com/edit/angular-object-length-xzxajl?file=app%2Fapp.component.html

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

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?

knockout condition in HTML page

Below is my HTML binding to display the records. I, have apply knockout js to perform the condition check as you can see the IF statement.
I want to use count++ as a knockout variable and perform the condition check.
The above code also not working for me.
Please can anyone let me know how to check the condition in knockout.
<div data-bind="foreach: GuidelinesQuestionList" id="problemcollapse">
<div data-bind="foreach: $data.SectionsSet">
<div class="primaryCaseContainer">
<div class="questionHeader" data-bind="text: $data.Heading , attr:{onClick: 'variableName.CollapseExpandCustom.ToggleSection(\''+$data.Uid.replace(/[^\w\s]/gi, '')+'\')'}"></div>
<div data-bind="attr: {id: $data.Uid.replace(/[^\w\s]/gi, '')}">
#{int count = 0;}
<div class="questionContainer" data-bind="foreach: $data.ProblemsSet">
<div data-bind="if: $data.Identified">
#{count++;}
<div>
<br><br>
<div data-bind="attr: {id: 'goalsReplaceDiv'+$data.Uid.replace(/[^\w\s]/gi, '')}"></div>
</div>
</div>
</div>
#if (count == 0)
{
<div id="divNoRecordsMessage">
<div>No Records !! </div>
</div>
}
</div>
</div>
</div>
</div>
You are mixing C# razor code with Knockout bindings. The count variable will not increment in your loop because it's evaluated before being returned to the client. Your loop is being rendered on the client.
Instead of doing that, make your divNoRecordsMessage show/hide based on a KO binding.
Something like this:
<div data-bind="visible: conditionForNoRecords">
No Records
</div>
But you should really make a custom filter for the ProblemSet array, something like this:
self.filteredProblemsSets = ko.computed(function() {
return ko.utils.arrayFilter(this.ProblemsSet(), function(item) {
return item.Identified;
});
}, viewModel);
You could then skip your if condition in the view and you would be able to easily display "No messages" when the array is empty.

Knockout.js using open ended tags in if statement

I have the following code...
<div data-bind="foreach:profiles">
<!-- ko if: $index() % 3 === 3 -->
<div class="form-group">
<!-- /ko -->
<div class="col-xs-3 col-md-1 no-padding-left">
<img class="img-circle text-center" src="\Content\images\fake_profile_pics\md.png" alt=".." style="opacity: 1.9" />
<h5 class="text-center"><span data-bind="text:Points"></span><span> points</span></h5>
</div>
<div class="col-xs-9 col-md-3">
<span class="display-block" data-bind="text:Division"></span>
<span class="display-block"><span data-bind="text:NominationsWritten"></span><span> Nominations Written</span></span>
</div>
<!-- ko if: $index() % 3 === 1 -->
</div>
<!-- /ko -->
</div>
I am trying to create a row of objects, 3 objects wide, that all start at the height of the lowest element of the row above it. The problem with this code is that knockout cannot find an end tag to the first div, and breaks without errors. If you place an end tag within the first 'ko if' statement everything works fine.
Is this possible to do using the knockout commenting method, or is there a more accepted way of tackling this problem with knockout?
You don't show your view model, but for this kind of problem, I think it is much easier to use a computed property in your view model and bind to that rather than mix a lot of view model logic into your view. For example, if you have this:
function ViewModel() {
var self = this;
self.someStuff = ko.observableArray();
//... other props
}
I'd just add a computed property like this:
function ViewModel() {
var self = this;
var numCols = 3;
self.someStuff = ko.observableArray();
self.columns = ko.computed(function () {
var source = self.someStuff();
if (source && source.length) {
var cols = [];
for (var i=0; i < source.length; i+=numCols) {
cols.push(source.slice(i,numCols));
}
return cols;
}
});
//... other props
}
Note: you might be able to come up with better ways to partition the array.
Then you can just bind it like:
<div data-bind="foreach:columns">
<div class="form-group" data-bind="foreach:$data>
<div>
<!-- bind whatever properties you want here -->
</div>
</div>
</div>
And you keep that ugly logic out of your view. And you computed property will be reevaluated any time your observable array changes.
Rather than the inline if why not always render the wrapping div with the conditional class?
<div data-bind="css: { 'form-group': $index() % 3 === 3 }">
...
</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.

Categories