Trying to get array to log if its empty to the console - arrays

I am working with a map and data. When you click on a state, it pulls the appropriate data from the JSON and displays the stores in an unordered list. If a state does not have data (Wyoming is one for example), it should be logging to the console that it is empty. So when you click on Wyoming, it should realize the array is empty and log that its empty to the console.
$("#paginateView").hide();
$("#stateButton").hide();
$("#mapWhereToBuy").usmap({
click: function(event, data) {
$.ajax({
url: 'https://165.227.69.79:8443/alanric/armaster/state/' + data.name + '?callback=functionCall',
dataType: "jsonp",
cache: true,
jsonpCallback: "functionCall",
success: function(json){
console.log(json);
var storeNames = '<ul class="list-group">';
$.each(json, function (i, item) {
var stores = item.cname;
if (stores && stores.length) {
storeNames += '<li class="content list-group-item">' + stores + '</li>';
} else {
console.log("Sorry, we do not carry products in your state.");
}
});
storeNames +='</ul>';
$('#clicked-state').html(storeNames);
$("#paginateView").show();
$("#stateButton").show();
$("#stateButton").html('You Selected:' + ' ' + data.name);
pageSize = 15;
showPage = function(page) {
$(".content").hide();
$(".content").each(function(n) {
if (n >= pageSize * (page - 1) && n < pageSize * page)
$(this).show();
});
}
showPage(1);
$("#paginateView li a").click(function() {
$("#paginateView li a").removeClass("current");
$(this).addClass("current");
showPage(parseInt($(this).text()))
});
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.cento.com/js/raphael.js"></script>
<script src="https://www.cento.com/js/jquery.usmap.js"></script>
<div id="mapWhereToBuy"></div>
<h1>Search By State</h1>
<h4>Click on a state to see store locations</h4>
<hr>
<div id="clicked-state"></div>

Some Array methods do not recognize empty arrays, like map(). But map() will always return something even an empty array. First example is .map() with an array of numbers the second example is with an empty array.
Demo
var array1 = [];
var result1 = array1.map(function(obj, idx) {
var ID = idx;
return idx;
});
console.log(result1);
var array2 = [0, 0, 1];
var result2 = array2.map(function(obj, idx) {
var ID = idx;
return idx;
});
console.log(result2);

I was able to get it to work by using this "json.length === 0". If there is a better alternative please let me know thanks!

Related

passing variable value from controller to view in AngularJS

I am developing an application where i popup a window and when it gets popup the next time i will try to open it should not be opened. Next time it should open When i will close the popup then it should open.
Now for that i am using count variable and whenever it will be 1 the popup opens and whenever it is greater than or equal to 2 it shows alert. But now when i close the popup it is not resetting the value of count to 0 in view.
In JSfiddle i tried using var self = this; it works fine but when i tried it on my code it says Uncaught Exception Typeerror cannot find property 'count' defined at line self.count = 0;
How to achieve this? or any alternate solution for this?
<a ui-sref-active="active" ng-click="count=count+1; connectMachine(machine, count)" ng-init="count=0" ><span><i class="fa fa-desktop fa-5x"></i></span></a>
$scope.connectMachine = function(machine, count) {
var promise = restAPIService.connectMachineService(
$scope.thisStudentThisBatch.guacProfileId,
machine.connectionId, $stateParams.batchID).get();
promise.$promise.then(function(response) {
var json = JSON.parse(response.data);
console.log(json.id);
var dnsUrl = $location.absUrl().split('/');
dnsUrl = dnsUrl[0] + '//' + dnsUrl[2];
var apiUrl = dnsUrl + $rootScope.apiUrl + "guacamole/disconnect/"
+ json.id;
var conn_params = $http.defaults.headers.common.Authorization
+ "++" + apiUrl;
$scope.machineURL = response.headers.url + "&conn_params="
+ conn_params;
var params = "height=" + screen.availHeight + ",width="
+ screen.availWidth;
var NewWin;
var self = this;
if ($scope.count == 1) {
NewWin = window.open($scope.machineURL);
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
self.count = 0;
}
}
var intervalID = window.setInterval(checkWindow, 500);
}, function(error) {
dialogs.error("Error", error.data.error, {
'size' : 'sm'
});
});
}
You can use a variable defined on $scope to trigger the value of button instead of using ng-init
HTML:
<div ng-app="myApp">
<ul ng-controller="TodoCtrl">
<li class="list-group-item" ng-repeat="todo in todos">{{todo.text}}
<button class="btn btn-default" ng-click="addLike($index)">value- {{count[$index]}}</button>
</li>
</ul>
</div>
JS:
var myApp = angular.module('myApp', []);
function TodoCtrl($scope) {
$scope.todos = [{
text: 'todo one'
}, {
text: 'todo two',
done: false
}];
$scope.count = [0, 0];
$scope.addLike = function(index) {
var NewWin;
$scope.count[index] ++;
if ($scope.count[index] == 1) {
NewWin = window.open('https://www.google.com');
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
$scope.count[index] = 0;
$scope.$apply();
}
}
var intervalID = window.setInterval(checkWindow, 500);
};
};
JS Fiddle :https://jsfiddle.net/p41dLjmn/

Filter on string

I'm learning angularjs and got an exercise that wants me to Use angular filter to show a title in the following format :
first letter of each word upper cased and each other letter lower cased also
remove any non-English letters from the title. For example:
A title with the name
“##THIS is a Title!!”
should be changed to
“This Is A Title”
I'm getting each title from an array of objects and present them like so.
<div ng-repeat="obj in objects">
<h3 class="panel-title">{{obj.Title}}</h3>
</div>
i understand that filter receives an array and filters through it . but this requires me to filter the string.
been searching for a while, how can i do this?
please refer below fiddle
http://jsfiddle.net/HB7LU/28315/
<div ng-controller="MyCtrl">
Hello, {{ name | ordinal|capitalize }}
</div>
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Super hero!!12##3';
}
myApp.filter('ordinal', function() {
// Create the return function
// set the required parameter name to **number**
return function(strTitle) {
// Ensure that the passed in data is a number
// If the data is not a number or is less than one (thus not having a cardinal value) return it unmodified.
strTitle=strTitle.replace(/[^a-zA-Z ]/g, "")
return strTitle;
}
});
myApp.filter('capitalize', function() {
return function(input){
if(input.indexOf(' ') !== -1){
var inputPieces,
i;
input = input.toLowerCase();
inputPieces = input.split(' ');
for(i = 0; i < inputPieces.length; i++){
inputPieces[i] = capitalizeString(inputPieces[i]);
}
return inputPieces.toString().replace(/,/g, ' ');
}
else {
input = input.toLowerCase();
return capitalizeString(input);
}
function capitalizeString(inputString){
return inputString.substring(0,1).toUpperCase() + inputString.substring(1);
}
};
});
angular.module('app', []).filter('myFilter', function(){
return function(input){
if(!input)
return;
var out = '';
var english = /^[A-Za-z0-9 ]*$/;
for(var letter of input)
if(english.test(letter))
out += letter;
var result = '';
for(var i = 0; i < out.length; i++)
result += out[i][(i === 0 || out[i-1] == ' ') ? 'toUpperCase' : 'toLowerCase']();
return result;
}
})
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<body ng-app="app">
<input ng-init='text="##THIS is a Title!!"' type='text' ng-model='text'>
<p>{{text | myFilter}}</p>
</body>

Compare two arrays and concat without duplicates

I have two arrays. I can push and splice by clicking on a word in searchWords, which adds or removes a word to the currentWordlist.
What I want to have is a button that transfers all the searchWords to the currentWordlist, without overwriting the words that are actually on the currentWordlist.
I came up with this code:
$scope.addAll = function () {
var searchWords = [];
var currentWords = [];
// safes all searchwords to the array
for (var i = 0; i < $scope.searchWords.length; i++) {
searchWords.push($scope.searchWords[i]);
}
// safes all currentwords to the array
for (var j = 0; j < $scope.currentWordlist.length; j++) {
currentWords.push($scope.currentWordlist[j]);
}
console.log("searchWords " + searchWords.length);
console.log("currentWords " + currentWords.length);
angular.forEach(searchWords, function(value1, key1) {
angular.forEach(currentWords, function(value2, key2) {
if (value1._id !== value2._id) {
$scope.currentWordlist.push(value1);
}
});
});
};
I go through both of the arrays and safe them so that I can use the arrays inside my two angular.forEach to check if there are duplicates. If I don't push to the currentWordlist. But it's not working. I get an [ngRepeat:dupes] error, but I cannot use track by $index because otherwise removing from the list removes the wrong word. I think I am doing something critically wrong here, but I couldn't find out what so far (hours of trial and error :0)
I would suggest to use angular unique filter with ng-repeat directive. The code could be as follows:
$scope.addAll = function () {
// use angular.copy to create a new instance of searchWords
$scope.combinedWords = angular.copy($scope.searchWords).concat($scope.currentWordlist);
};
And then in your view:
<div ng-repeat="word in combinedWords | unique:'_id'">
{{word}}
</div>
Usage:
colection | uniq: 'property'
It also possible to filter by nested properties:
colection | uniq: 'property.nested_property'
You can simply do like this
angular.forEach($scope.searchWords, function(value1, key1) {
var temp=true;
angular.forEach($scope.currentWordlist, function(value2, key2) {
if (value1.id === value2.id)
temp=false;
});
if(temp)
$scope.currentWordlist.push(value1);
});
var app = angular.module("app", []);
app.controller("ctrl", function($scope) {
$scope.searchWords=[{id:1,name:'A'},{id:2,name:'B'},{id:1,name:'A'},{id:4,name:'D'}];
$scope.currentWordlist=[];
$scope.addAll = function() {
angular.forEach($scope.searchWords, function(value1, key1) {
var temp=true;
angular.forEach($scope.currentWordlist, function(value2, key2) {
if (value1.id === value2.id)
temp=false;
});
if(temp)
$scope.currentWordlist.push(value1);
});
console.log($scope.currentWordlist);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<button ng-click="addAll(newWord)">Add</button>
<div>{{currentWordlist}}</div>
</div>

How do I slide the videos if I click on next button using angular.js

I able to embed the 4 videos in a particular div using "angular.js".But,I have so many videos nearly 20.
So,what I'm trying to do is,on click of "Next" button should get next 4 videos in a same div.How can I achieve this?
Can anyone please help me out regarding this issue ...
My html code:
<div class="panel-body">
<video width=176 height=99 style=" margin-left: 10px; margin-right: 10px;"
ng-repeat="videoSource in videoSources track by $index" autoplay
controls ng-src="{{videoSource | trustUrl}}">
</video>
<br> <a href="#" ng-click='loadVideos()'>Load videos</a>
<br><button type="button">Next</button>
</div>
My js code:
angular.module('Admin', [])
.controller('Home', function($scope) {
$scope.videoSources = [];
$scope.loadVideos = function() {
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Customer_Service.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Customer_Service.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
};
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
]);
The actual Angular way would be to create a custom filter to paginate the results. In the example, I've created a paginate filter that takes two parameters: pageNum and pageSize to slice the input array into the required chunk without any pre-processing of the array required.
Also added the necessary next and previous buttons and hid the load videos button.
angular.module('Admin', [])
.controller('Home', function($scope) {
$scope.pageNum = 0;
$scope.pageSize = 4;
$scope.isFirstPage = function() {
return $scope.pageNum === 0;
};
$scope.isLastPage = function() {
return $scope.pageNum >= Math.floor($scope.videoSources.length / $scope.pageSize);
};
$scope.prevPage = function() {
$scope.pageNum--;
};
$scope.nextPage = function() {
$scope.pageNum++;
};
$scope.videoSources = [];
$scope.loadVideos = function() {
for (var i = 0; i < 6; i++) {
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/boat_149.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/horse_riding_205.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/flower_124.webm');
}
};
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
])
.filter('paginate', function() {
console.log('creating paginate function', arguments);
return function(inputArray, pageNumber, pageSize) {
console.log('paginating', arguments);
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
if (!Array.isArray(inputArray)) return inputArray;
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="panel-body" ng-app="Admin" ng-controller="Home">
<video width=176 height=99 ng-repeat="videoSource in videoSources | paginate:pageNum:pageSize track by $index" autoplay controls ng-src="{{videoSource | trustUrl}}">
</video>
<div ng-show="videoSources.length">
<button ng-disabled="isFirstPage()" ng-click="prevPage()">Previous</button>
<button ng-disabled="isLastPage()" ng-click="nextPage()">Next</button>
</div>
<div ng-hide="videoSources.length">
<a href="#" ng-click='loadVideos()'>Load videos</a>
</div>
</div>
And because I felt like playing around with the code some more, here's a version that makes a pagination object, so that the functionality can be reused in different controllers and directives:
angular.module('Admin', [])
.controller('Home', function($scope, Pagination) {
$scope.videoSources = [];
$scope.pagination = new Pagination(4);
$scope.loadVideos = function() {
for (var i = 0; i < 6; i++) {
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/boat_149.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/horse_riding_205.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/flower_124.webm');
}
};
})
.factory('Pagination', function() {
var Pagination = function(pageSize) {
this.pageSize = pageSize || 4;
this.pageNum = 0;
this.sourceLength = 0;
};
Pagination.prototype.isFirstPage = function() {
return this.pageNum === 0;
};
Pagination.prototype.isLastPage = function(sourceLength) {
return this.pageNum >= Math.floor((sourceLength || this.sourceLength) / this.pageSize);
};
Pagination.prototype.prevPage = function() {
this.pageNum--;
};
Pagination.prototype.nextPage = function() {
this.pageNum++;
};
Pagination.prototype.setPage = function(pageNum) {
this.pageNum = pageNum;
};
Pagination.prototype.setPageSize = function(pageSize) {
this.pageSize = pageSize;
};
Pagination.prototype.setSourceLength = function(sourceLength) {
this.sourceLength = sourceLength;
}
Pagination.prototype.getPage = function() { return this.pageNum; };
Pagination.prototype.getPageSize = function() { return this.pageSize; };
Pagination.prototype.getSourceLength = function() { return this.sourceLength; };
return Pagination;
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
])
.filter('paginate', function() {
console.log('creating paginate function', arguments);
return function(inputArray, pageNumber, pageSize) {
console.log('paginating', arguments);
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
if (pageNumber && pageNumber.pageSize) pageSize = pageNumber.pageSize;
if (pageNumber && pageNumber.pageNum !== undefined) pageNumber = pageNumber.pageNum;
if (!Array.isArray(inputArray)) return inputArray;
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="panel-body" ng-app="Admin" ng-controller="Home">
<video width=176 height=99 ng-repeat="videoSource in videoSources | paginate:pagination track by $index" autoplay controls ng-src="{{videoSource | trustUrl}}">
</video>
<div ng-show="videoSources.length">
<button ng-disabled="pagination.isFirstPage()" ng-click="pagination.prevPage()">Previous</button>
<button ng-disabled="pagination.isLastPage(videoSources.length)" ng-click="pagination.nextPage()">Next</button>
</div>
<div ng-hide="videoSources.length">
<a href="#" ng-click='loadVideos()'>Load videos</a>
</div>
</div>
The filter now looks a bit stranger, because I wanted to keep the option to use it as paginate:pageNumber:pageSize but also allow it to be used as paginate:paginationObject and that took a little trickery. But now, our pagination functionality is abstracted away into a factory so it can be reused and keep our controller lean, and that is the Angular Way(TM) :D
Edit: Extra paginate filter explanation:
.filter('paginate', function() {
a console.log() call I used to debug that I forgot in here
console.log('creating paginate function', arguments);
To be able to accept parameters in an Angular filter, you have to return a function that needs those parameters from the filter function
return function(inputArray, pageNumber, pageSize) {
Another debugging console.log() call
console.log('paginating', arguments);
We can't be sure the parameters were passed in, so we provide sensible defaults (in this case, if pageNumber wasn't given, we'll set it to 0 and if pageSize wasn't given, we'll set it to 4)
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
Because we want to be able to pass in a Pagination object as a parameter instead of a page number & page size, we see if the first parameter isn't by chance an object containing pageSize and/or pageNum members, and if it is, we set the local pageNumber and pageSize variables to the values of the Pagination object's members
if (pageNumber && pageNumber.pageSize) pageSize = pageNumber.pageSize;
if (pageNumber && pageNumber.pageNum !== undefined) pageNumber = pageNumber.pageNum;
Then we check to see if the first parameter to the filter (the value being filtered) is actually an array. If it isn't, we just return the value unchanged. For example, if we were to have {{ 1 | paginate }} in an Angular template, the result would be 1, our algorithm wouldn't break. If it's an array, though, such as {{ [1, 2, 3, 4, 5] | paginate }} (with default paginate parameters) it would become [1, 2, 3, 4] and {{ [1, 2, 3, 4, 5] | paginate:0:2 }} would become [1, 2].
if (!Array.isArray(inputArray)) return inputArray;
And then the actual pagination logic (funny how it's way smaller than the input checking part of the code). We slice the input array to start at index pageNumber * pageSize and to end at index (pageNumber + 1) * pageSize (non-inclusive). Think of the first page (for pageSize = 4) having page number 0 and starting at index 0 and finishing at index 3 (so ending at index 4 (= 1 * 4) non-inclusive), page two having page number 1 and starting at index 4 (= 1 * 4) and finishing at index 7 (index 8 (= 2 * 4) non-inclusive, and so on. More information on Array.prototype.slice()
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
Without sliding effect, you can do it with pages:
Change loading videos:
$scope.videoSources.push({url:'video_here');
Then, page them:
var page = 1, perpage = 4;
$.each($scope.videoSources, function(k, v) {
v.page = (page++)/perpage;
Now that we have pages, in your HTML you can filter your data:
ng-repeat="video in videoSources | filter: {page: current_page}"
And next page ng-click:
ng-click="current_page++"
Same for previous page.
**Important noticing: because I changed your array to contain objects, to access the video you need to use video.url in your ng-src

Is this a correct way of pagination in angularjs?

On clicking "more" i call loadNextPage method that call loadComments method which load data based next id nextCommentId and push the data to allComments array and this array is rendered in html
$scope.loadNextPage = function()
{
loadComments($scope.nextCommentId);
}
function loadComments(page){
$scope.allComments.push(d.data);
..
..
$scope.nextCommentId = nextId;
}
Here is my html
<div ng-repeat="comment in allComments">
...
</div>
My question is, is this the correct way of doing pagination in angularjs as i am keeping all data in array so if data will grow it will consume memory.
<div data-pagination="" data-num-pages="numPages()"
data-current-page="currentPage" data-max-size="maxSize"
data-boundary-links="true"></div>
todos.controller("TodoController", function($scope) {
$scope.filteredTodos = []
,$scope.currentPage = 1
,$scope.numPerPage = 10
,$scope.maxSize = 5;
$scope.makeTodos = function() {
$scope.todos = [];
for (i=1;i<=1000;i++) {
$scope.todos.push({ text:"todo "+i, done:false});
}
};
$scope.makeTodos();
$scope.numPages = function () {
return Math.ceil($scope.todos.length / $scope.numPerPage);
};
$scope.$watch("currentPage + numPerPage", function() {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
$scope.filteredTodos = $scope.todos.slice(begin, end);
});
});

Resources