Angular JS Ng-Repeat Issue - javascript

I have a scenario like I want to display the contents which are available in only one array, if its present in another array there is no need to display it.
My html is like
<div ng-repeat="array1Value in array1">
<div ng-repeat="array2Value in array2">
<div ng-if="isNotFound(array1,array2Value.id)">
<span>{{array2Value.name}}</span>
</div>
</div>
</div>
My js class is like
var app = angular.module("MyApp",{});
app.controller("MyController",function($scope) {
$scope.array1 = [
{
id:"1",name:"one"
},
{
id:"2",name:"two"
},
{
id:"3",name:"three"
}
];
$scope.array2 = [
{
id:"1",name:"one"
},
{
id:"2",name:"two"
},
{
id:"4",name:"four"
}
];
$scope.alreadyPrinted = [{}];
$scope.isNotFound = function(array,value){
for(i = 0; i < array.length; i++){
if (value === array[i].id) {
return false;
}
}
if($scope.alreadyPrinted.indexOf(value) > -1){
return false;
} else {
$scope.alreadyPrinted.push(value);
return true;
}
}
});
I need only four as my output. But as of now nothing is coming. Please help me to correct the issue.

You need to remove external loop... try this:
<div ng-repeat="array2Value in array2">
<div ng-if="isNotFound(array1,array2Value.id)">
<span>{{array2Value.name}}</span>
</div>
</div>

If I understand you right, you want True to be returned if the value is not in the first array.
Try this for your isnotFound function:
$scope.isNotFound = function(array,value){
for(i = 0; i < array.length; i++){
if (value !== array[i].id) {
return true;
}
}
return fase;
}
Or maybe even better:
$scope.isNotFound = function(array,value){
angular.forEach(array, function(item){
if (value !== item.id) {
return true;
}
});
return fase;
}
I'm not sure why you are repeating over the first array and the allreadyPrinted part. Your html can just be:
<div ng-repeat="array2Value in array2">
<div ng-if="isNotFound(array1,array2Value.id)">
<span>{{array2Value.name}}</span>
</div>
</div>

I think there is problem in your alreadyPrinted code. Your code is displaying 'four' as output. Try to insert one alert box in your code like
for (i = 0; i < array.length; i++) {
if (value === array[i].id) {
alert('is not found function called');
return false;
}
}
You will see 'four' as output on your browser but for one iteration only.
Correct your alreadyPrinted code.alert box will help you to isolate the problem.

Related

Cannot read property 'textContent' of undefined in JavaScript

What is the problem? I declared variable n
var n = 0;
while (true) {
var comment = document.getElementsByClassName('wall_reply_text')[n].textContent;
if (comment.indexOf("публикации") == 0) {
alert(comment);
}
n++;
}
the most likely explanation is that there are no elements with class name wall_reply_text when this code runs, so document.getElementsByClassName('wall_reply_text') returns an empty array. when you try to access index 0 in that empty array, you get undefined, and thus the error when trying to access undefined.textContent.
You need to break if there is no element
var n = 0, comment=""
while (true) {
const div = document.getElementsByClassName('wall_reply_text')[n]
if (div) comment = div.textContent;
else break
if (comment.indexOf("публикации") == 0) {
console.log(n,comment);
}
n++;
}
<div class="wall_reply_text">1</div>
<div class="wall_reply_text">публикации</div>
<div class="wall_reply_text">3</div>
<div class="wall_reply_text">4</div>
Here is a more elegant way
[...document.querySelectorAll('.wall_reply_text')]
.map(div => div.textContent)
.forEach((comment,i) => {
if (comment.indexOf("публикации") == 0) {
console.log(i,comment);
}
})
<div class="wall_reply_text">1</div>
<div class="wall_reply_text">публикации</div>
<div class="wall_reply_text">3</div>
<div class="wall_reply_text">публикации</div>
Alternative if you just need the one index
const comments = [...document.querySelectorAll('.wall_reply_text')]
.map(div => div.textContent)
console.log(comments.indexOf("публикации"))
<div class="wall_reply_text">1</div>
<div class="wall_reply_text">публикации</div>
<div class="wall_reply_text">3</div>
<div class="wall_reply_text">4</div>

How to Show Next/Previous item of an array?

I'm writing the first item of an array to the screen, and would like to create Next/Previous buttons for array, but I can't get it to work. I have tried several methods, but I can't find suitable solution.
Can anyone help?
This is the last one I have tried:
var data = [
{"subject":"starcraft2",
"date":"08.31",
"dDay":"mon",
"content1":"STARCRAFT2",
"content2":"season2",
"playerA":"Ju",
"playerB":"Lee",
"emblemA":"Terran",
"emblemB":"Zerg",
"result":"end"},
{"subject":"starcraft2",
"date":"08.29",
"dDay":"wed",
"content1":"STARCRAFT2",
"content2":"season2",
"playerA":"kim",
"playerB":"joo",
"emblemA":"Terran",
"emblemB":"Protoss",
"result":"end"},
];
function prevAction() {
// function (e) { // the e here is the event itself
alert("Prev Click!");
// document.getElementById('subject').textContent = prevItem();
// document.getElementById('date').textContent = prevItem();
for (var i = 0; i<data.length; i++)
for (var j=0; j<data[i]; j++)
while(j === 0)
{
j == j++;
console.log(j);
}
console.log(data[j].date + ', ');
document.getElementById('date').textContent = data[j].date;
// document.getElementById('subject').textContent = j[0];
}
Here's the jist of how you'd accomplish this in pure Javascript:
getNextItem() {
var index = document.getElementById("index").value;
//add guards here to prevent array overflow/underflow
if (data.length < index - 1) {
index.value++;
}
document.getElementById("DOM_ELEMENT_TO_ATTACH_DATA").innerHTML = data[index];
}
getPreviousItem() {
var index = document.getElementById("index").value;
//add guards here to prevent array overflow/underflow
if (index > 0) {
index.value--;
}
document.getElementById("DOM_ELEMENT_TO_ATTACH_DATA").innerHTML = data[index];
}
<input id="index" type="hidden" name="index" value="0">
<button type="button" onclick="getNextItem()">Next Item</button>
<button type="button" onclick="getPreviousItem()">Previous Item</button>
Note that this will just attach the pure json data to a DOM element, it won't do anything fancy with it.
Furthermore, if you want to do anything more complex, I'd strongly recommend you look into using a library like jQuery or Angular as it's going to make your life a whole lot easier in the long run.

Strange behavior when splicing array, javascript

I'm working with list of checkboxes and I have next logic behavior for it:
if all items selected, checkbox "select all" is checked
if one of all selected items has been unchecked, checkbox "select all" is unchecked as well
This logic is clear. Depends of what item is checked I extract its id to an additional array and then using this array for request that to get data.
For pushing everything works fine, but for slicing the logic is strange. So I can slice the array until first item is checked, however I unchecked the first item, pushed and sliced items no more related with checkboxes.
I have reproduced plunker with it, so I appreciate if anybody could help me to find what I'm missing.
$scope.modelClass = {
selectedAll: false
};
$scope.selectAllClass = function (array) {
angular.forEach(array, function (item) {
item.selected = $scope.modelClass.selectedAll;
$scope.param =''
});
};
$scope.checkIfAllClassSelected = function (array) {
$scope.modelClass.selectedAll = array.every(function (item) {
return item.selected == true
});
$scope.checked = array.filter(function (item) {
return item.selected == true
}).length;
angular.forEach(array, function (obj) {
if(obj.selected == true){
requestClass(obj)
}
});
};
var selectedClass = [];
var requestClass = function (obj) {
selectedClass.push(obj);
angular.forEach(selectedClass, function (val) {
if (val.selected != true) {
selectedClass.splice(selectedClass.indexOf(val.id), 1);
}
else {
selectedClass = selectedClass.filter(function (elem, index, self) {
return index == self.indexOf(elem);
})
}
});
$scope.param = _.map(selectedClass, 'id')
};
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]
The logic looks good for me, not sure what's wrong here. I've took the first solution from this post (it looks like you are using the second one) and slightly modified it for your needs.
$scope.model = {
selectedClass : []
}
$scope.isSelectAll = function(){
$scope.model.selectedClass = [];
if($scope.master){
$scope.master = true;
for(var i=0;i<$scope.classes.length;i++){
$scope.model.selectedClass.push($scope.classes[i].id);
}
}
else{
$scope.master = false;
}
angular.forEach($scope.classes, function (item) {
item.selected = $scope.master;
});
$scope.param = $scope.model.selectedClass
}
$scope.isChecked = function() {
var id = this.item.id;
if(this.item.selected){
$scope.model.selectedClass.push(id);
if($scope.model.selectedClass.length == $scope.classes.length ){$scope.master = true;
}
} else {
$scope.master = false;
var index = $scope.model.selectedClass.indexOf(id);
$scope.model.selectedClass.splice(index, 1);
}
$scope.param = $scope.model.selectedClass
}
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]
html
<div ng-class="{'selected': master, 'default': !master}">
<div>
<input type="checkbox" ng-model="master" ng-change="isSelectAll()" > Select all
</div>
</div>
<div ng-repeat="item in classes | orderBy : 'id'" ng-class="{'selected': item.selected, 'default': !item.selected}">
<div >
<input type="checkbox" ng-model="item.selected" ng-change="isChecked()">
{{ item.name }}
</div>
</div>
this is fixed plunker

Adding and removing items from arrays, based on a condition in AngularJS

I'm trying to make a dynamic "favorites" toggle-button.
When clicking the button, it should add the selected player to the users favorite-list. If the player is already favorited, it should remove it.
I've also tried to iterate through the favorites, to check if a player is already favorited. If true, it colors the favorite star gold.
A few problems here. My for loop for checking seems to be working properly as long as the array only contains one item. But as soon as I try adding more, the gold icon is only gold colored on the last player added to favorites. So the check only finds one favorite at a time, and I can add a player to favorites many times, as long as I vary the players I add.
If someone could point me in the right direction and help me understand why my loop isn't working correctly, that would be awesome!
http://codepen.io/utrolig/pen/LNgRwv
Javascript
angular.module('test', [])
.controller('TestController', function($scope){
$scope.players = [
{
uniqueid: "gem",
name: "Ole Christian",
cake: false,
},{
uniqueid: "utrolig",
name: "Stian",
cake: false,
},{
uniqueid: "drozo",
name: "Adrian",
cake: false,
}
];
$scope.user = {
name: "Stian",
username: "stiba",
favorites: [{uniqueid: "drozo"}],
}
$scope.checkFavorite = function(id){
fav = $scope.user.favorites;
var exists;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
exists = true;
} else {
exists = false;
}
}
return exists;
}
$scope.toggleFavorite = function(id){
fav = $scope.user.favorites;
if(fav.length === 0){
var newfav = {uniqueid: id};
fav.push(newfav);
} else {
if($scope.checkFavorite(id) === true){
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
} else if ($scope.checkFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
} else {
console.log('Error!');
}
}
}
$scope.isFavorited = function(){
return true;
};
})
HTML
<body ng-app="test">
<div class="container" ng-controller="TestController">
<h3>Players</h3>
<div ng-repeat="player in players" class="player-cont">
<div class="player">
<div class="favorite" ng-click="toggleFavorite(player.uniqueid)" ng-class="{'active': checkFavorite(player.uniqueid)}">
<i class="material-icons">star</i>
</div>
<i class="material-icons player-icon">person</i>
</div>
<div>
<p ng-bind="player.uniqueid"></p>
<p ng-bind="player.name"></p>
</div>
</div>
<h3>Favorites</h3>
<div ng-repeat="favorite in user.favorites track by $index">
<h5>{{favorite.uniqueid}}</h5>
</div>
<p class="user">
{{user.favorites}}
</p>
</div>
</body>
There's a couple of errors in your code.
The first is checkFavorite, if you examine the code you'll see that only the last item is actually compared to id, since the exists flag is updated for each item. You need to "short circuit" the loop and return true as soon as you find a value.
btw, is* is a common name convention for checking boolean values.
$scope.isFavorite = function(id){
var fav = $scope.user.favorites;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
return true;
}
}
return false;
}
Your toggle is also very verbose, if you "reduce" the code you end up with something like this
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// no previous items, remove, OK
if(fav.length === 0) {
var newfav = {uniqueid: id};
fav.push(newfav);
return;
}
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
}
// otherwise add the item
// remember, isFavorite returns true of false, there is no third state
else { // if ($scope.isFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}
This can be edited further, since the isFavorite function will return false if the list is empty, i.e. no need for the first if
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) {
fav.splice(i, 1);
// unless the item exists more than once, break the loop
break;
}
}
}
// otherwise add the item
else {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}

Get a div inside a td with Javascript

I want to get the text inside the div in the class "atName".
I am looping though the table td's like this:
var searchString = document.getElementById("search").value;
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable td'),
colslen = cols.length,
i = -1;
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
Here i want to access the text inside the div
}
Every td is set up like this:
<td id="H" class="element nonmetal gasI">
<div class="atN">1</div>
<div class="atS gas"><a class="gas" href="https://en.wikipedia.org/wiki/Hydrogen" target="_blank">H</a></div>
<div class="atName">Hydrogen</div>
<div class="atW">1.00794</div>
</td>
I want the text inside the "atName" div.
Does anyone know how?
Thanks!
The same way you selected the tds:
cols[i].querySelector('.atName').textContent;
btw. you should give different IDs to your tds or use classes because IDs should be unique
UPDATE
To avoid any confusion, I'm already assuming we're looping the tds (from your code), and this line goes here:
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var divText = cols[i].querySelector('.atName').textContent; // <--- here
}
...
}
You can get the object by class name :
document.getElementsByClassName('atName')
But this return you a list of object with this class.
So you can do in your while:
while (++i < colslen)
{
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var text = cols[i].getElementsByClassName('atName')[0].textContent;
}
}
Maybe this will help?
var searchString = "Hy";
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable tr td .atName');
for (var i=0;i<cols.length;i++)
{
if (cols[i].innerHTML.indexOf(searchString)>-1)
{
alert(cols[i].innerHTML);
}
}
}
What you're looking for is element.innerHTML but hopefully this selector trick will help you too.
Edit: element.textContent is different but you might desire to use it instead.

Categories

Resources