I try to implement infinite scrolling grid based on ngGridEventScroll event. But I have it emited twice every time I scroll to the bottom of grid.
Here's my code for update data ($scope.mydata is used as source for grid):
$scope.$on('ngGridEventScroll', function () {
chunkStart += chunkSize;
$http.get('/api/mydata/' + chunkStart + '/' + chunkSize)
.success(function(mydata) {
$scope.mydata = $scope.mydata.concat(mydata);
});
});
Where's my mistake, can you give me any advice?
I had similar problem, here's my solution:
$scope.load = true;
$scope.$on('ngGridEventScroll', function() {
if ($scope.load) {
$scope.load = false;
$http.get('your/url/here')
.success(function(data) {
//operations here
$timeout(function() {
//or here
$scope.load = true;
});
});
}
});
I used $scope.load flag to indicate when to load data and angular $timeout() function fix time-related issues.
Related
Im using channel's of pusher in a Laravel application.
So when im fired an event from my controller this is received to my client, and with the pusher function I add some text with this lib https://github.com/albburtsev/jquery.typist on my front page:
channel.bind('App\\Events\\TextAdded', function(data) {
if(data.txt){
printPhrase(data.txt);
i++;
}
});
function printPhrase(txt) {
$('<span>')
.addClass('txt_' + i)
.appendTo('.typist_dialog')
.typist({
text:txt,
speed: 15,
}).on('end_type.typist', function() {
console.log('end') ;
}).typistStop() ;
}
});
As you can see I can catch the event "end_type" (when the function stop writing).
The problem is that i cannot -or I've no idea howto- puts "channel.bind" on queue, and so waiting untill that the printPhrase(txt) is finished... so not showing more than one printing for time at screen...
You'll have to set up some sort of queue to make sure they don't fire until the previous one is done. This code isn't tested but should do the trick:
var printQueue = [];
var queueWorking = false;
channel.bind('App\\Events\\TextAdded', function(data) {
if(data.txt){
printQueue.push(data.txt);
workQueue();
}
});
function printPhrase(txt) {
i++;
$('<span>')
.addClass('txt_' + i)
.appendTo('.typist_dialog')
.typist({
text:txt,
speed: 15,
}).on('end_type.typist', function() {
queueWorking = false;
workQueue();
}).typistStop() ;
}
function workQueue(){
if(printQueue.length && !queueWorking){
queueWorking = true;
printPhrase(printQueue.shift());
}
}
I have the following command inside an AngularJS controller
window.onunload = function () {
connection.invoke("RemoveUser", playerName);
}
It's weired because I have a pure JS where this statement works well, so outsite an angularJS controller when I close the tab or the window, it fires and do its job, but when I put this inside a controller, it doesn't fire. Any ideas?
Full script bellow
angular.module("mathGameApp", []).controller("mathGameCtrl", function ($scope) {
// Current player name
$scope.playerName;
$scope.welcomeIsVisible = true;
$scope.gameAreaIsVisible = false;
$scope.countdownIsVisible = false;
// Create connection
const connection = new signalR.HubConnectionBuilder()
.withUrl("/MathGame")
.configureLogging(signalR.LogLevel.Information)
.build();
// Get math challenge
connection.on("GetChallenge", data => {
// Bind challenge
$scope.expression = data.expression + " = " + data.possibleResult;
$scope.$apply();
});
// Receive and bind score
connection.on("ReceiveScore", data => {
$scope.score = data;
$scope.$apply();
});
// Rise alert
connection.on("RiseAlert", data => {
alert(data);
})
// Get status that the player was added to game room
connection.on("AddedToGameRoom", data => {
$scope.welcomeIsVisible = false;
$scope.gameAreaIsVisible = true;
$scope.$apply();
})
connection.on("ChallengeFinished", data => {
$scope.counter = 5;
$scope.countdownIsVisible = true;
$scope.$apply();
let interval = setInterval(function () {
if ($scope.counter == 0) {
$scope.countdownIsVisible = false;
$scope.buttonIsDisabled = false;
$scope.$apply();
clearInterval(interval);
connection.invoke("RefreshChallenge");
}
$scope.counter--;
$scope.$apply();
}, 1000);
})
// rise answer Correct/Wrong
connection.on("RiseAnswer", data => {
$scope.buttonIsDisabled = true;
$scope.expression = data;
$scope.$apply();
console.log($scope.buttonsDisabled);
console.log($scope.expression);
})
// Request the user to be added to game room
$scope.enterGame = function (playerName) {
connection.invoke("EnterGame", playerName);
}
$scope.answerQuestion = function (playerName, answer) {
connection.invoke("AnswerQuestion", {
"playerName": playerName, "isCorrect": answer
});
}
// Open connection
connection.start().then(() => {
}).catch((err) => {
alert(err.toString())
});
window.onunload = function () {
connection.invoke("RemoveUser", playerName);
}
})
Controllers should use the $onDestroy Life-Cycle Hook to release external resources.
app.controller("mathGameCtrl", function ($scope) {
̶w̶i̶n̶d̶o̶w̶.̶o̶n̶u̶n̶l̶o̶a̶d̶ ̶=̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶)̶ ̶{̶
this.$onDestroy = function () {
connection.invoke("RemoveUser", playerName);
}
})
For more information, see AngularJS $compile Service API Reference - Life-Cyle hooks.
Update
You can and should handle the 'unload' event through window.addEventListener(). It allows adding more than a single handler for an event. This is particularly useful for AJAX libraries, JavaScript modules, or any other kind of code that needs to work well with other libraries/extensions.
For more information, see
MDN Web API Reference - WindowEventHandlers.onunload
MDN Web API Reference - EventTarget.addEventListener()
Need search on HTML table, search work perfectly, but very and very slowly and freeze web page when load and search. Table need in HTML file, NOT FROM SERVER, only in file. How to improve performance?
Angular code:
var app = angular.module('jsSearch', []);
app.controller('FilterTable', ['$scope', function($scope) {
$scope.result = [];
$scope.basic_table = false;
$scope.result_table = true;
$scope.ishidden = false;
$scope.totalDisplayed = 20;
$scope.loadMore = function () {
$scope.totalDisplayed += 20;
};
$scope.init = function(event, table_id) {
var target = angular.element('#' + table_id);
angular.forEach(target.children()[1].children, function(tr) {
entry = [];
angular.forEach(tr.children, function(td) {
entry.push(td.innerHTML);
});
$scope.result.push(entry);
})
target.innerHTML = ''
};
$scope.search = function(event, table_id) {
var dataValue = event.target.attributes.id.value;
};
$scope.filter_count = function(event, table_id) {
$scope.ishidden = true;
if($scope.count_value) {
$scope.basic_table = false;
$scope.result_table = true;
}
if($scope.string_value) {
$scope.basic_table = false;
$scope.result_table = true;
}
else {
$scope.basic_table = false;
$scope.result_table = true;
}
$scpopearray_length = $scope.result.length;
$scope.ishidden = false;
};
}]);
app.filter('returnCount', function () {
return function (item, count_value) {
return item.slice(0, count_value);
};
});
Full App:
Link
App on hosting
I see you are parsing the song table in the DOM to extract the song data - if you absolutely have to have the song data as an HTML table then this will work, but your code will be much cleaner if you can save the song data as an array and assign that array to $scope.result directly, rather than parsing from the DOM. The parsing process also is also quite slow and means the page is unresponsive for a few seconds after loading.
I can't see how the two ng-change listeners on string_value and count_value are affecting anything on the page - removing them speeds up the filtering significantly.
Apart from that what's slowing down the interaction here is the DOM modification every time the filter is changed. As long as the whole result table is in the DOM and being modified it will be slow, and I can't imagine having all of the songs visible in one long list is useful to the user either. I'd suggest a UX change - switch count to a drop-down, with options of, say, 100, 50, 20, and 10, and default it to 100.
With the ng-change attributes removed from the inputs and a maximum of 100 entries visible the filtering is pretty fast.
Using the "loading remote data" example from the ngInfiniteScroll website I have tried to implement infinite scrolling. My issue;
The function nextPage() gets called continuously until there are no more records left to load (controlled by an offset value in the SQL query).
I'd appreciate any input on this as I'm rather lost.
Thanks in advance.
HTML
<tbody>
<div id="infinite_scroll" infinite-scroll='visits.nextPage()' infinite-scroll-disabled='visits.busy' infinite-scroll-distance='1'>
<tr ng-repeat="visit in visits.items" ng-class="{active: visit.uuid == data.detailItem.uuid}" ng-click="openDetailItem(visit.uuid)">
<td>{{visit.details.name}}</td>
<td>{{visit.created_at}}</td>
</tr>
</div>
</tbody>
Javascript - AngularJs Factory
angular.module('app').factory('Visits', function(VisitResource) {
// new visits object
var Visits = function () {
this.items = [];
this.busy = false;
this.offset = 0;
};
Visits.prototype.nextPage = function () {
// busy - stop
if (this.busy == true) {
// DEBUG
console.log('busy test 1.1: ' + this.busy);
return;
} else {
// DEBUG
console.log('busy test 1.2: ' + this.busy);
}
// busy now
this.busy = true;
VisitResource.getVisitations({
limit: 500,
offset: this.offset
}, function (response) {
// stop loading if no data returned
if(response.data.length == 0) {
// DEBUG
console.log('busy test 2: ' + this.busy);
return;
} else {
// DEBUG
console.log('Payload: ' + response.data.length);
}
var _this = this;
angular.forEach(response.data, function (a_visit) {
_this.items.push(a_visit);
});
// set the last acquired record value
this.offset = this.items[this.items.length - 1].id;
// not busy
this.busy = false;
}.bind(this));
};
return Visits;
});
As it turns out you can't get vanilla nginfinitescroll to trigger when the container is scrolled as nginfinitescroll is looking at the height of the window.
Here is a link to the answer on SO:
angularjs infinite scroll in a container
What is a best "Angular Way" to implement the directive that will have a shared timer for all it instances?
For example I have a directive "myComponent" and on the page it appears many times.
Inside of the component, exists some text that blink with some interval.
Because of business requirements and performance considerations, I would like that there will be single "timeout" that will toggle the blink for all instances at once (after document is ready).
I thought about the writing some code within directive definition:
//Pseudo code
angular.module("app",[]).directive("myComponent", function($timeout){
$(function() { $timeout(function(){ $(".blink").toggle(); }, 3000); } );
return {
//Directive definition
};
});
Or by using some kind of service that will receive the $element and add remove class to it:
//Pseudo code
angular.module("app",[])
.service("myService", function($timeout){
var elements = [];
this.addForBlink = function(element) { elements.push(element) };
$(function() { $timeout(function(){ $(elements).toggle(); }, 3000); } );
})
.directive("myComponent", function(myService){
return {
compile:function($element){
myService.addForBlink($element);
return function() {
//link function
}
}
};
});
In my opinion the most elegant and efficient would be to combine both these approaches by specifying the logic of the directive in the very directive initialization function. Here is a scaffold of what I actually mean:
app.directive('blinking', function($timeout){
var blinkingElements = [];
var showAll = function() {
for(var i = 0; i < blinkingElements.length; i++){
blinkingElements[i].addClass("blinking");
}
};
var hideAll = function() {
for(var i = 0; i < blinkingElements.length; i++){
blinkingElements[i].removeClass("blinking");
}
};
var blink = function () {
$timeout(showAll, 500);
$timeout(function(){
hideAll();
if (blinkingElements.length > 0) {
blink();
}
}, 1000);
};
return {
link : function(scope, element, attrs){
blinkingElements.push(element);
if (blinkingElements.length == 1) {
blink();
}
element.on("$destroy", function(){
var index = blinkingElements.indexOf(element);
blinkingElements.splice(index, 1);
});
}
}
});
And here is the working demo.
Moreover you can inject some service that will be responsible for configuration (setting the intervals and / or class) or you can provide the configuration by passing an object directly to the attribute. In the latter case you can enable applying different classes for different elements, but you should think of some policy how to deal with situation, when the interval was set more than once.