knockout.js bind array inside array - arrays

how do I bind a ko.observablsArray inside an array? I have the following viewModel:
function GetBegriffsKetteViewModel () {
var self = this;
self.Begriffsketten = ko.observableArray();
self.letzterBegriff = ko.observable(0);
self.aktuelleKette = -1;
}
var Begriffskette = function () {
var self = this;
var begriffe = new ko.observableArray();
var begriffBez = new ko.observableArray();
}
And in my html I have the following div:
<div id="ausgewaehlteKetten" data-bind="foreach : Begriffsketten">
<div class="row">
<div class="col-md-2" id="innerDivForEach" data-bind="foreach : begriffBez">
<span data-bind="text: $data" ></span>
</div>
</div>
</div>
If I run this code I get the following error:
ReferenceError: begriffBez is not defined
Is there a way to bind an array inside an array? What am I doing wrong?
Thanks
Thomas

First, you should not create knockout objects with new.
wrong: var x = new ko.observable();
correct: var x = ko.observable();
Second, if you want a variable to become an object property, you must assign it to the object instance (this - or, in your case, self).
Minor point: By convention, only constructors should start with a captital letter in JS. So since it's a regular property it would be begriffsketten with a lowercase b.
function BegriffsKetteList() {
var self = this;
self.begriffsketten = ko.observableArray();
self.letzterBegriff = ko.observable(0);
self.aktuelleKette = ko.observable(); // better use an observable here
}
function Begriffskette() {
var self = this;
self.begriffe = ko.observableArray();
self.begriffBez = ko.observableArray();
}
Once you do that, knockout can see the begriffBez and begriffe properties when it renders the view.
<div id="ausgewaehlteKetten" data-bind="foreach: begriffsketten">
<div class="row">
<div class="col-md-2" id="innerDivForEach" data-bind="foreach: begriffBez">
<span data-bind="text: $data" ></span>
</div>
</div>
</div>

Related

ng-repeat is calling another function more than its objects?

I am having a ng-repeat with one array and three objects within an array.
and I am calling a function from img src="{{}}" which is in ng-repeat.
So as per concept an img src function should get called 3 times (because I have 3 objects in an array), but it gets called 15 times.
<div class="" ng-init="getCurrentModuleId();"> <!-- 1st image row start -->
<div class="" ng-repeat="obj in courseModuleData"
ng-click="getStartFunction($index + 1);">
<a href="javascript:;" class="hoverStyle">
<img id="{{$index + 1}}" src="{{getLockPlayImage($index + 1);}}"
class="tresure_img">
</a>
</div>
</div>
js code:-
function getLockPlayImage(id) {
$log.info("Welcome to getLockPlayImage function");
var el = document.getElementById(id);
el.style.top = $scope.courseModuleData[id - 1].cordinates.x + "px";
el.style.left = $scope.courseModuleData[id - 1].cordinates.y + "px";
if (getModuleId.module_id == id) {
$scope.getImage = "img/start/playbutton_normal.png";
/*var el =document.getElementById(id);
el.setAttribute('ng-click', 'startQuestions()');*/
} else {
$scope.getImage = "img/start/lock_normal.png";
}
return $scope.getImage;
}
function getCurrentModuleId() {
var _promiseObject = new promise.Promise();
$log.info("Welcome to getLock getCurrentModuleId function");
var getActModule = $scope.startModule();
getActModule.then(function (result) {
_promiseObject.done(false, "Downloaded");
getModuleId = result.rows.item(0);
});
}

Can't read the result of a Firebase Promise with the ng-if Angular directive

I'm trying to show the categories of a service using AngularJS and firebase with the ng-if directive by testing if the entry exist in a table.
The tables are written in a denormalize way as recommended on the Firebase guideline.
- servicecat
- catid
- name : "Category Name"
- services
- srvid1 : true
- srvid2 : true
- srvid3 : true
- services
- srvid
- name: "Service Name"
- Details: "blabla"
- servicecat:
- catid1 : true
- catid2 : true
I am trying to show on a page of a service the categories of the table where the key of the service exists.
<li
ng-repeat="cat in servicecat"
ng-if="checkIfTrue(cat.$id, postSrv.$id)">
{{ cat.name }}<br>
</li>
So I'm trying to get the value a srvid ("true") and return it with the function checkIfTrue(catid,srvid).
$scope.checkIfTrue = function(catid,srvid) {
var ref = fb.child('servicecat/'+catid+'/services/'+srvid);
ref.once('value').then(function(snap){
return snap.exists();
});
}
I don't understand why the ng-if directive does not take the result of the function (which is true when the element exists in the table).
I can see that if I had a "return true;" at the end of the function (out of the ref.once('value') function), it is taken by the ng-if directive.
I was then trying to drop the result of the "once" function in a global variable that I could return at the end of the checkIfTrue function. It seems that the return on the "once" function is a "promise" that the ng-if directive can't read.
So I manage to fetch all categories and all services with the following factory. But when I try to get the selected categories of a specific sercices, It seems I miss something. I was trying to do it by joining two tables but It seems that I shouldn't do this method. Anyway I can't manage to solve this issue.
app.factory("getServices", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref().child("services");
return $firebaseArray(ref);
}
]);
app.factory("getServiceCategories", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref().child("servicecat");
return $firebaseArray(ref);
}
]);
app.controller("maincontroller", ["$scope","$firebaseArray","$firebaseObject",
"getServices","getServiceCategories",
function($scope,$firebaseArray,$firebaseObject, getServices, getServiceCategories)
{
// GET ALL SERVICES AND ALL CATEGORIES
$scope.services = getServices;
$scope.servicecat = getServiceCategories;
// SHOW & EDIT SERVICE DETAILS AND CATEGORIES
$scope.showDetailsService = function(srvid) {
var ref = fb.child('services/'+srvid);
$scope.selectSrvtoPost = $firebaseObject(ref);
var ref2 = fb.child('services/'+srvid+'/servicecat/');
$scope.selectSrvCatID = $firebaseArray(ref2);
var result = ref2.once('value', function(snap) {
snap.forEach(function(snap2) {
var catid = snap2.key;
var ref3 = fb.child('servicecat/'+catid);
ref3.once('value', function(snap3) {
var catname = snap3.val().name;
console.log("Srvid: "+srvid+" /// Id : "+catid+" /// Name : "+catname);
return snap3;
})
})
})
// ANOTHER TRY WITH $loaded()
var ref2 = fb.child('services/'+srvid+'/servicecat/');
$scope.listcat = $firebaseArray(ref2);
$scope.listcat.$loaded()
.then(function(snap) {
snap.forEach(function(snap2) {
var catid = snap2.key;
var ref3 = fb.child('servicecat/'+catid);
ref3.once('value', function(snap3) {
var catname = snap3.val().name;
console.log("Srvid: "+srvid+" /// Id : "+catid+" /// Name : "+catname);
return snap3;
})
})
})
}
)];
<div ng-controller="mainController">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--3-col">
<h3>List Services</h3>
<ul>
<li ng-repeat="service in services" ng-click="showDetailsService(service.$id)" class="clickable"><strong>{{ service.title }}</strong><br>{{ service.details }} <br><span class="idgrey">{{ service.$id }}</span></li>
</ul>
<h3>List All Categories</h3>
<li ng-repeat="cat in servicecat">{{ cat.name }}</li>
</div>
<div class="mdl-cell mdl-cell--3-col">
<h3>Post Full Service Selected </h3>
<p>
<strong>{{ selectedService.title }}</strong><br>
{{ selectedService.details }}
<h5>Id of Selected Categories</h5>
<ul>
<li ng-repeat="cat in selectedCategoriesId">{{ cat.$id }}</li>
</ul>
<hr>
<h5>Name of Selected Categories</h5>
<ul>
<li ng-repat="cat in selectedSrvCat">ID : {{ findcatscope.name }}</li>
</ul>
</div>
</div>
</div>

Edit an object inside several ng-repeat

I got two ng-repeat who display objects call 'post', and I have a button for edit the text and update it.
Everything works fine but I still got a little problem here's the html code :
<li ng-repeat="post in posts | filter: { etat: 'aTraiter' } ">
<p ng-show="!editing[$index]" ng-model href="#/{{post._id}}">{{ post.corps }}</p>
<input ng-show="editing[$index]" type="text" ng-model="post.corps">
<a ng-show="!editing[$index" ng-click="edit(post)">Editer</a>
<a ng-show="editing[$index]" ng-click="update(post)">Confirmer</a>
<a ng-show="editing[$index]" ng-click="cancel(post)">Annuler</a>
</li>
<li ng-repeat="post in posts | filter: { etat: 'enCours' } ">
<p ng-show="!editing[$index]" ng-model href="#/{{post._id}}">{{ post.corps }}</p>
<input ng-show="editing[$index]" type="text" ng-model="post.corps">
<a ng-show="!editing[$index]" ng-click="edit(post)">Editer</a>
<a ng-show="editing[$index]" ng-click="update(post)">Confirmer</a>
<a ng-show="editing[$index]" ng-click="cancel(post)">Annuler</a>
</li>
the controller :
$scope.editing = [];
$scope.posts= Posts.query();
$scope.edit = function(post){
var idx = $scope.posts.indexOf(post);
$scope.editing[idx] = angular.copy($scope.posts[idx]);
}
$scope.update = function(post){
var idx = $scope.posts.indexOf(post);
Posts.update({id: post._id}, post);
$scope.editing[idx] = false;
}
$scope.cancel = function(post){
var idx = $scope.posts.indexOf(post);
post = angular.copy(post);
$scope.editing[idx] = false;
}
If I got just one post I can edit it and all is ok.
But when I got one post in the both ng-repeat I got some bugs, if I click on edit, buttons change in the both ng-repeat and the both post can be edit.
I'm not really sure but I thinks it's a problem with my :
ng-show="editing[$index]"
I try to put the index of the post like this
ng-show="editing[posts.indexOf(post)]"
But this is not working, can somebody help me ?
(The jsfiddle link)
EDIT the post query :
Posts.query();: Array[0]
0: d
__v: 0
_id: "569563a96a81e64409623179"
corps: "asdad"
etat: "enCours"
nomReseau: "Google+"
section: "evolution"
__proto__: d
1: d
__v: 0
_id: "56954e676a81e6440962316b"
corps: "sdfsdfsf"
etat: "enCours"
nomReseau: "Google+"
section: "evolution"
__proto__: d
Using $index in a filtered repeat to access information in your array.
The problem with using $index, passing it to your controller and then trying to use that index to search for a 'post' in your array of posts, is that $index references your view index and not the true index of the item in the array.
This is traditionally not a problem unless you are filtering your array with ng-repeat. Why? Because $index does not reflex the index of the item, but the index of how the item is appearing in the DOM. So although the first rendered post could have index 5 in your posts array, it will still have $index of 0 because it is the first rendered item in the ng-repeat.
Solution: Separate your data first into two separate arrays and then repeat through them individually.
Controller:
$scope.posts = Posts.query();
$scope.postsATraiter = $scope.posts.filter(function(item, index) {
return item.etat === 'aTraiter';
});
$scope.postsEnCours = $scope.posts.filter(function(item, index) {
return item.etat === 'enCours';
})
$scope.edit = function(post, postType){
var idx = getPostsByType(postType).indexOf(post);
$scope.editing[idx] = angular.copy($scope.posts[idx]);
}
$scope.update = function(post, postType){
var idx = getPostsByType(postType).indexOf(post);
Posts.update({id: post._id}, post);
$scope.editing[idx] = false;
}
$scope.cancel = function(post, postType){
var idx = getPostsByType(postType).indexOf(post);
post = angular.copy(post);
$scope.editing[idx] = false;
}
function getPostsByType(postTypeString) {
if (postTypeString === 'aTraiter') {
return $scope.postsATraiter;
} else {
return $scope.postsEnCours;
}
}
Now that the data is separate you are free to use $index because we know that the $index will respect the true index of the item in the array because it is not being filtered.
<li ng-repeat="post in postsATraiter">
<p ng-show="!editing[$index]" ng-model href="#/{{post._id}}">{{ post.corps }}</p>
<input ng-show="editing[$index]" type="text" ng-model="post.corps">
<a ng-show="!editing[$index" ng-click="edit(post, 'aTraiter')">Editer</a>
<a ng-show="editing[$index]" ng-click="update(post, 'aTraiter')">Confirmer</a>
<a ng-show="editing[$index]" ng-click="cancel(post,'aTraiter')">Annuler</a>
</li>
<li ng-repeat="post in postsEnCours">
<p ng-show="!editing[$index]" ng-model href="#/{{post._id}}">{{ post.corps }}</p>
<input ng-show="editing[$index]" type="text" ng-model="post.corps">
<a ng-show="!editing[$index]" ng-click="edit(post, 'enCours')">Editer</a>
<a ng-show="editing[$index]" ng-click="update(post, 'enCours')">Confirmer</a>
<a ng-show="editing[$index]" ng-click="cancel(post, 'enCours')">Annuler</a>
</li>
You may have to fiddle around with your implementation more, but it appears that $index and the way it is being used may be the root of the problems you are having.
You're using $index, which is just ... well... an index. Both collections can have a value at index==1, right? So, $index isn't unique across the entire set of posts.
Luckily, it would appear that you have an ID for each post that seems to be unique: post._id. How about using that instead?
One little side-note - I'm using jquery's grep method below to find a post by Id. It's fine, but I like underscore.js better. Take a look at both ...
So, here's your controller code:
//This is me willfully and wantonly changing your variables ... sorry.
$scope.selectedPost = undefined;
$scope.selectedPost_unchanged = undefined;
//This is the same, though, so you should feel good ;-)
$scope.posts= Posts.query();
$scope.edit = function(postId){
var result = $.grep($scope.posts, function(p){ return p._id == postId; });
if(result.length==0) { return; }
//just store the one we are editing. that should be cool, right?
$scope.selectedPost = results[0];
//ok... im changing this too... see if you like it better?
//we're gonna use it in the CANCEL method (below).
$scope.selectedPost_unchanged = angular.copy(results[0]);
}
$scope.update = function(post){
Posts.update({id: post._id}, post);
$scope.selectedPost = undefined;
$scope.selectedPost_unchanged = undefined;
}
$scope.cancel = function(post){
post = angular.extend({}, $scope.selectedPost_unchanged);
$scope.selectedPost = undefined;
$scope.selectedPost_unchanged = undefined;
}
//This is new too ... just adding it so that the html is clearer.
$scope.isEditing = function(post) {
if($scope.selectedPost==undefined) { return false; }
return post._id == $scope.selectedPost._id;
}
The HTML changes a bit too
All of your editing[$index] code becomes just isEditing(post)
VoilĂ ! Except maybe use css+ng-class...
Not for nothing, but I would add the while editing/not-editing show/hide using css. Then, add an ng-class to the li element instead (eg - ng-class="{editing: isEditing(post)}"). Then, take care of all your show/hides with css. This way, you only have to put isEditing(post) in ONE location in your html (instead of adding it to every element). ng tags are not expensive, but they REALLY add up inside of ng-repeat tags.

Get model array values in controller's service

I've been facing an issue since couple of hours. My view template looks like-
<div class="row" ng-repeat="row in CampaignsService.getRows().subItems track by $index">
<div class="col-sm-2">
<select class="form-control dropDownPercent" ng-model="CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]" ng-change="CampaignsService.wow(CampaignsService.dropDownPercent, $index)" ng-options="o as o for o in CampaignsService.showPercentDropDown().values">
</select>
</div>
<div class="col-sm-2" style="line-height: 32px">
of visitors send to
</div>
<div class="col-sm-4">
<select class="form-control" ng-model="campaignSelect" ng-options="campaign.Campaign.id as campaign.Campaign.title for campaign in CampaignsService.getRows().items">
<option value=""> Please select </option>
</select>
</div>
<div class="col-sm-4">
<a class="btn btn-default" target="_blank" href="">Show campaign</a>
</div>
Variable CampaignsService.selectCounter is a counter variable and declared in service but when I'm going to use ng-model="CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]" it gives me error -
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 35 of the expression [CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]] starting at [{CampaignsService.selectCounter}}]]
And when I use ng-model="CampaignsService.dropDownPercent['{{CampaignsService.selectCounter}}']" it does not give any error but it takes this variable as string.
My question is how could I create a model array and get model's array values in my service ?? I read many questions in stack community and none of the trick work for me. My service under my script, is
.service('CampaignsService', ['$rootScope', 'AjaxRequests', function ($rootScope, AjaxRequests) {
this.dropDownPercent = [];
this.selectCounter = 0;
var gareeb = [];
this.showPercentDefault = 100;
// this.campaignsData = [];
this.$rowsData = {
items: [], //array of objects
current: [], //array of objects
subItems: [] //array of objects
};
this.getRows = function () {
return this.$rowsData;
}
this.addNewRow = function () {
var wowRow = {}; //add a new object
this.getRows().subItems.push(wowRow);
this.selectCounter++;
gareeb.push(0);
}
this.calculatePercentages = function (index) {
angular.forEach(this.getRows().current, function (data, key) {
if (key == index) {
console.log(data);
}
})
}
this.showPercentDropDown = function ($index) {
var balle = 0;
var start;
angular.forEach(gareeb, function (aha, keywa) {
balle += aha;
})
var last = 100 - balle;
var final = [];
for (start = 0; start <= last; start += 10) {
final.push(start);
}
return this.values = {
values: final,
};
}
this.wow = function (valueWa, keyWa) {
console.log(this.dropDownPercent);
gareeb[keyWa] = valueWa;
this.changePercentDropDown();
}
this.changePercentDropDown = function () {
var angElement = angular.element(document.querySelector('.dropDownPercent'));
angular.forEach(angElement, function (data, key) {
console.log(data);
})
}
}])
Target model structure should be
ng-model="CampaignsService.dropDownPercent[1]"
ng-model="CampaignsService.dropDownPercent[2]"
ng-model="CampaignsService.dropDownPercent[3]"
A big thanks in advance.
Since you are in context of the Angular expression, you don't need interpolation tags {{...}}. So ngModel directive should look like this:
ng-model="CampaignsService.dropDownPercent[CampaignsService.selectCounter]"

Displaying the array elements one by one in a form in Angular js

I am building an application using Angular js and Taffy Db.
I have got a resultset from Taffy DB which is an an array.
I want to display the elements one by one in my HTML page.
javascript:
$scope.viewList = function () {
$scope.sharelists = [];
$scope.resultSet = teamlist().get();
var teamdata = $scope.resultSet;
var length = teamdata.length;
angular.forEach(teamdata, function (teamdata, i) {
if (i < length) {
console.log(i);
$scope.teamlistresult = teamdata.text;
$scope.sharelists.push({
text: $scope.teamlistresult
});
}
});
};
});
HTML:
<label for="sharedby">Shared by</label>
<input class="btn-primary" type="submit" value="View" ng-click="viewList()" />
<ul class="unstyled">
<li ng-repeat="share in sharelist">
<input type="checkbox" ng-model="share.done">
<span>{{share.text}}</span>
</li>
</ul>
But I couldnt display the array elements one by one.
Please advice
I am not sure but if this method
$scope.resultSet = teamlist().get(); is async and returns promise then you should use
teamlist().get().then(function(data) {
var teamdata = data;
var length = teamdata.length;
angular.forEach(teamdata, function (teamdata, i) {
if (i < length) {
console.log(i);
$scope.teamlistresult = teamdata.text;
$scope.sharelists.push({
text: $scope.teamlistresult
});
}
});
}

Resources