Data bind over in array in knockoutjs - javascript

I have an array as follows:
self.arrayObj : Array[2]
>0:Object
>Display1
->InnerObjects
>__proto
>1:Object
>Display2
-->InnerObjects
My interntion is to display "Display1" and "Display2" which are strings
I am doing the html binding as follows:
<div data-bind="foreach: self.arrayObj">
<span data-bind="text:$data[0]"></span>
</div>
How can I iterate over the array and display only texts?

In case your array is just an array of strings you should do the following:
<div data-bind="foreach: self.arrayObj">
<span data-bind="text:$data"></span>
</div>
In case your array is an array of objects which have for example a property 'Name', which is a string, then you do it like this. Knockout knows you're inside the foreach so it knows which element you're at while looping.
<div data-bind="foreach: self.arrayObj">
<span data-bind="text:Name"></span>
</div>

I would like to answer my own question.
It is not a simple thing when we want to bind the object with dynamic keys and values in the UI using Knockout js. If we have the fixed keynames then its easy.
What I did was , converted the json object to 2-D array :
In the .js file
var 2Darray = jsonObject.map(function(val) {
var keyname = Object.keys(val)[0];
var value = val[keyname];
return [keyname,value];
});
In the html file , we can bind it two times in a loop:
<div data-bind:"foreach:2Darray">
<div data-bind:"foreach: $data">
<div data-bind:"text:$data[0]">
<div data-bind:"foreach: $data[1]">
<div data-bind:"text:$data.val">
</div>
</div>
</div>

Related

ng-repeat error in loading list

I'm fetching data from firebase db and using the array of objects for ng-repeat. But though the console shows correct data, ng-repeat is not working as expected. Once in ten times, when i fire the server, ng-repeat works somehow.
I'm not able to figure out the error in loading.
controller -
.controller('dashboardHomeController',['$scope','$firebaseObject',
function($scope,$firebaseObject){
console.log($scope.prgList);
var programsRef = firebase.database().ref('/programs');
//fetching 50 latest programs added in db chronologically sorted
programsRef.limitToLast(50).on("value",function(snapshot){
var programsObj = snapshot.val();//setting variable to hold object of objects snapshot
var programsKeyArray = Object.keys(programsObj);//returns only keys
var programsArray = Object.values(programsObj);//returns array of values
var programsObjlength = programsKeyArray.length;
console.log(programsKeyArray,programsObjlength,programsArray);
//setting ng-repeat list prgList
$scope.prgList = programsArray;
console.log($scope.prgList);
});
}])
html -
<div class="col-sm-9">
<ul class="list-unstyled">
<li ng-repeat="prog in prgList">
<div class="row" style="border-bottom:1px solid #d7d7d7;padding-top:20px">
<div class="col-sm-2">
<img src="">
</div>
<div class="col-sm-8">
<h4>{{prog.prgHeading}}</h4>
<p>{{prog.prgSubHeading}}</p>
<p>{{prog.prgLikes}}</p>
</div>
<div class="col-sm-2" style="text-align:center">
<p>{{prog.prgStatus}}</p>
</div>
</div>
</li>
</ul>
</div>
console -
Array [ "-KolrsdHOBNavsAJZzZC", "-KolsZZeoP2hEsnG_iTd", "-Kolt9ds9SnzSmS71t88", "-KoltuJvBLSZyqsBRYnl", "-KoluUt6Vr5YlMOG4SHG" ] 5 Array [ Object, Object, Object, Object, Object ] app.js:247:17
Array [ Object, Object, Object, Object, Object ]
it seems angular digest cycle not triggered, you should try $scope.$apply()

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.

How can I dynamically render both javascript data and html within the same angular curly braces ({{ }})?

I am rendering different kinds of attributes within the same html element in the following manner:
Javascript:
var dataAttribute = {
value:function(){ return 1;}
}
var listAttribute = {
value:function(){ return "<div>My Arbitrary HTML
<div>1</div>
<div>2</div>
<div>3</div>
</div>";}
}
var attributes = [dataAttribute,listAttribute]
HTML:
<div ng-repeat="attribute in attributes"> {{ attribute.value() }} </div>
How do I get the html in the listAttribute to render as HTML and not as text, while still retaining the ability to render the normal data of the dataAttribute?
You just should not. Use ng-switch directive if you want to render different things based on properties of items in your collection. ( in worst case use series of ng-if inside your ng-repeat)
Do not invent another templating engine if you already using angular, you are just confusing yourself.
Little more explanation here. You already have code that generates that html somewhere. It really better by angular directives.
<div ng-repeat="attribute in attributes" ng-switch="attribute.type">
<div ng-switch-default> {{ attribute.value() }} </div>
<div ng-switch-when="table"><my-table data='attribute.value()'></my-table></div>
<div ng-switch-when="list"><my-list data='attribute.value()'></my-list></div>
</div>
And set of directives
.directive('myTable', myTable).directive('myList', myList)
will hold all the logic to produce html from the data.
I.E. don't mix layout and data in one structure.
The solution was a combination of #vittore and #YOU's answers:
Javascript:
var dataAttribute = {
value:function(){ return 1;},
type:'data'
}
var listAttribute = {
value:function(){ return "<list-directive></list-directive>";},
type:'list'
}
var attributes = [dataAttribute,listAttribute]
HTML:
<div ng-repeat="attribute in attributes" ng-switch="attribute.type">
<div ng-switch-default> {{ attribute.value() }} </div>
<div ng-switch-when="list" ng-bind-html="attribute.value()"></div>
</div>
Thanks!

How to Iterate over javascript Map using angular ng-repeat

I'm developing an Angualr application where we have a Map object (as shown below). The key and value of the map object (headerObj) are coming from user as input to the app,
var headerObj = new Map();
headerObj.set(key,value);
I'm iterating through them using foreach as shown below, the output is coming as expected
$scope.inputHeaders.forEach(function (headerkey, headervalue) {
console.log(headerkey, headervalue;
});
but I have to show this map values in UI, which again user can edit, so I have binded them
<li class="list-group-item" ng-repeat="header in inputHeaders">
<div ng-repeat="(key, value) in header">
{{key}} : {{value}}
</div>
</li>
I've googled and tried several ways, but nothing did help, so basically I've wanted to know how can I iterate over a map using forEach in angular?
Just to give more clarity my requirement is something like that: I need values to be passed to the server as key, value pair, only if I'm not wrong, suppose if I use object properties the key of the object will be fixed something like
{"key":"Content-Type","value":"application/x-www-form-urlencoded","$$hashKey":"003"}]
but my server is expecting something like
"Content-Type" => "application/x-www-form-urlencoded"
Created an plunkr edit http://plnkr.co/edit/t2g6Dl831HGyjD6uSdf3?p=preview
AngularJS 1.x does not know how to use Javascript iterators, so you'll have to convert the Map object to an Array first using Array.from().
Controller:
$scope.inputHeadersArray = Array.from($scope.inputHeaders);
View:
<li class="list-group-item" ng-repeat="header in inputHeadersArray">
{{header[0]}} : {{header[1]}}
</li>
you can use :
[...headerObj] or [...headerObj.entries()] to got two dimensions array. and iterate them.
or [...headerObj.keys()] and [...headerObj.values()] for regular array.
here are few changes in your code. http://plnkr.co/edit/gpc1mPsZrl2QVXbnWZKA?p=preview
app = angular.module('testDemo', []);
app.controller('submitCtrl',function($scope) {
$scope.header={};
$scope.inputHeaders=[];
$scope.addHeader = function() {
$scope.inputHeaders.push($scope.header);
$scope.header = {};
$scope.header.key='';
$scope.header.value='';
}
$scope.log=function(){
//alert('in log');
$scope.inputHeaders.forEach(function (key, value) {
console.log(key, value);
});
}
});
HTML:
<body ng-controller='submitCtrl'>
<div >
<input type="text" class="=form-control" ng-model="header.key" placeholder="Key">
<input type="text" class="=form-control" ng-model="header.value" placeholder="value">
<button class="btn btn-sucess" ng-click="addHeader()">Add</button>
<button class="btn btn-sucess" ng-click="log()">Log</button>
<div>
<ul class="list-group">
<li class="list-group-item" ng-repeat="header in inputHeaders">
<!-- need to to work on this logic -->
<div ng-show="inputHeaders.length>=1">
<input type="text" ng-model="header.value" />
<input type="text" ng-model="header.key" />
</div>
</li>
</ul>
</div>
</div>
</body>

javascript and mustache - passing an array to template

I am trying to learn mustache / icanhaz in conjunction with jquery and javascript, and I've got a mustache template to which I'm passing various data. One of the pieces of data is a list of choices, but that list can vary in length (say, one to three choices). How do I pass that varying data to mustache?
This is my code:
Javascript:
for (childIndex in scenes[sceneID].children) {
childSceneID = scenes[sceneID].children[childIndex];
childScene = scenes[childSceneID];
childLink = childScene.name;
}
decision = ich.decision(decisionData);
$('#page_container').append(decision);
Template:
<script id="decision" type="text/html">
<div id="page">
<h1>{{ tTitle }}</h1>
<ul id="options">
<li>{{tDecision}}</li>
</ul>
{{#tBacklink}}<a id="back" data-sceneid="{{tBacklink}}">Back</a>{{/tBacklink}}
</div>
</script>
So somehow I have to pass all the childLinks in the decision object to mustache to be parsed in a loop to output the list of <li> elements.
Anybody know how to do this?
Model your data in an object first of all,
var scene = {
tTitle: '',
tDecision: '',
tBacklink: ''
};
Then place each of these objects in an array on each iteration of your loop
var scenes = [];
for () {
scenes.push(scene);
}
Then call Mustache to render the template with the scenes array, the template has been modified like this
<script id="decision" type="text/html">
<div id="page">
{{#scenes}}
<h1>{{ tTitle }}</h1>
<ul id="options">
<li>{{tDecision}}</li>
</ul>
{{#tBacklink}}<a id="back" data-sceneid="{{tBacklink}}">Back</a>{{/tBacklink}}
{{/scenes}}
</div>
</script>

Categories