KnockoutJs - How to throttle the binding afterkeydown - javascript

I got this working for my filtering functionality.
<input data-bind="value: filterByName, valueUpdate:'afterkeydown'" />
<div data-bind="foreach: filteredRecords"/>
</body>
Now i just need to throttle the binding of the filteredRecords or to put delay on it
my view model looks like this
self.filteredRecords= ko.computed(function () {
return getRecordByStatus(1);
});
self.filterByName= ko.observable("");
var getRecordByStatus = function (status) {
var periodRecord, filtered = [];
if (self.timesheetApprovalResponse().periods) {
// period filtering
if (self.selectedPeriod()) {
periodRecord = _.find(self.timesheetApprovalResponse().periods, function(p) {
return p.fromDate === self.selectedPeriod().key;
});
if (periodRecord) {
filtered = _.where(periodRecord.timesheets,
function(t) {
return t.status === status;
});
}
}
}
if (self.filterByName()) {
filtered = _.where(filtered,
function (t) {
console.log(t.name.toLowerCase() + "-" + self.filterByName().toLowerCase());
return t.name.toLowerCase().indexOf(self.filterByName().toLowerCase()) > -1;
});
}
return filtered;
};
Now my problem is where to put the .extend({ throttle: 500 });
I think I cannot put it on filteredRecords as it will also throttle in Page Load ?
Any other ideas?

If I understand what you are after, then you probably want to throttle filterByName. This will prevent filteredRecords from being notified until filterByName stops changing for x milliseconds.

Related

Pre-fill a keyword search filter on Angular JS

I'm trying to load a page with a pre-filled value in an input field. The page and value do load, although it doesn't trigger anything in the filtered results until I enter something on the keyboard. Is there a way around this? I'd like it to just load the filtered results once the page loads.
I'm new to Angular JS, but appreciate any sort of help or push in the right direction.
I have tried:
ng-init="search.keywords='initial'" on the input tag and that doesn't seem to cause any filtering at happen.
$scope.search = { keywords: 'initial' }; also loads the initial value, but doesn't trigger any filtering.
<input type="text" id="search-keywords" ng-model="search.keywords"
class="form-control" placeholder="Keyword search">
$scope.$watch("search", function (newVal, oldVal) {
if (newVal) {
$scope.doFilter(newVal);
}
}, true);
$scope.doFilter = function (search) {
$scope.filtering = true;
$scope.filteredCourses = $scope.filterExactMatchExceptNull($scope.courses, "inst", search.inst);
$scope.filteredCourses = $scope.filterExactMatchExceptNull($scope.filteredCourses, "fos", search.fos);
$scope.filteredCourses = $scope.filterCutoff($scope.filteredCourses, search.min, search.max);
$scope.filteredCourses = $filter("filter")($scope.filteredCourses, {
code: search.code,
name: search.name,
poa: search.poa
});
$scope.filteredCourses = $scope.filterByKeywords($scope.filteredCourses, search.keywords);
$scope.limit = 15;
if ($scope.limit >= $scope.filteredCourses.length) {
$scope.limit = $scope.filteredCourses.length;
}
$scope.filtering = false;
};
$scope.filterByKeywords = function (courses, keywords) {
if (!keywords || keywords == "") {
return courses.filter(function (course) {
return true;
});
}
var keywordArr = keywords.toLowerCase().trim().replace(/\W+/g, " ").replace(/\s\s+/g, " ").split(",");
return courses.filter(function (course) {
var matched = false;
for (var i = 0, length = keywordArr.length; i < length; i++) {
if (course.keywords && course.keywords.indexOf(keywordArr[i]) > -1) {
matched = true;
break;
}
}
return matched;
});
};
Any help would be greatly appreciated!
$watch function is used to detect any change in the inputfield after it is loaded into DOM.
So, to work it for the first time you may do:
Either use ng-init on the element to fire filter method on DOM load.
ng-init="doFilter(search)"
Or
Call filter function one time at controller level itself before actual watch starts.
$scope.search = { keywords: 'initial' };
$svope.doFilter($scope.search);
You could specify the method which should be executed on initialization of the page in the ng-init directive:
ng-init="doFilter({keywords: 'initial'})"

How to check if all documents are loaded with Firebase.util pagination

How can I check if I have to stop calling the loadMore() function, because all the documents have already been loaded from the database?
In the example below I'm using Ionic, but it's the same also with ng-infinite-scroll in AngularJS apps.
This is my actual code:
HTML:
...
<ion-infinite-scroll
ng-if="!noMoreItemsToLoad"
on-infinite="loadMore()"
distance="5%">
</ion-infinite-scroll>
</ion-content>
JS Controller:
$scope.loadMore = function(){
console.log('Loading more docs...');
Items.loadMore(); // calling the .next() method inside the Items service
if( !Items.hasNext()) { $scope.noMoreItemsToLoad = true; }
$scope.$broadcast('scroll.infiniteScrollComplete');
}
JS Items Factory:
.factory('Items', function (FIREBASE_URL, $firebaseArray, $firebaseObject) {
var itemsRef = new Firebase(FIREBASE_URL + 'items/');
var scrollRef = new Firebase.util.Scroll(itemsRef, 'name');
var self = {
getAllItems : function(){ ... },
loadMore: function(){
scrollRef.scroll.next(4);
},
hasNext: function(){
if(scrollRef.scroll.hasNext()) { return true; }
else { return false; }
}
}
return self;
}
Do the scroll.next in timeout, for example:
loadMore: function(){
$timeout(function() {
scrollRef.scroll.next(4);
});
},
I had the same issue and I think the solution is to modify the hasNext() function on firebase.util.js:
Cache.prototype.hasNext = function() {
return this.count === -1 || this.endCount >= this.start + this.count;
};
I put a missing equal sign (=) before this.start
I hope it works for you.

if statement within function breaks javascript

I'm stumped with this one and would really appreciate someone's help.
I'm customizing highslide for integration with wordpress. Via the following code within the highslide.config.js file I'm adding a class name to certain elements and passing different attributes through an onClick call depending on certain conditions.
Everything works until I add the following code:
if(hsGroupByWpGallery){
slideshowGroup: this.parentNode.parentNode.parentNode.id
};
When the above code is present, not only does that one statement not execute, but the whole thing stops working. Even if the if statement is something like if(1=1){}; it still breaks.
If I have instead simply slideshowGroup: this.parentNode.parentNode.parentNode.id or nothing (the two options I'm looking for), both do what I would expect. I just need an if statement to switch between them.
Here's the relevant code:
jQuery(document).ready(function() {
var hsCustomGalleryGroupClass = 'fbbHighslide_GalleryGroup';
var hsCustomGalleryGroupChecker = 0;
var hsGroupByWpGallery = true;
jQuery('.' + hsCustomGalleryGroupClass).each(function(){
hsCustomGalleryGroupChecker++;
return false;
});
if (hsCustomGalleryGroupChecker > 0){
jQuery('.' + hsCustomGalleryGroupClass).each(function(i, $item) {
var grpID = $item.id;
jQuery('#' + grpID + ' .gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
return hs.expand(this, {
slideshowGroup: grpID
});
};
});
});
} else {
jQuery('.gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
return hs.expand(this, {
// This is the problem if statement
if(hsGroupByWpGallery){
slideshowGroup: this.parentNode.parentNode.parentNode.id
};
});
};
});
};
});
Thanks in advance.
The problem is you are trying to assign a conditional property.. you can't have a if condition inside a object definition like that
jQuery('.gallery-item a').addClass('highslide').each(function () {
this.onclick = function () {
var obj = {};
//assign the property only if the condition is tru
if (hsGroupByWpGallery) {
obj.slideshowGroup = this.parentNode.parentNode.parentNode.id;
}
return hs.expand(this, obj);
};
});
Another way to do the same is
jQuery('.gallery-item a').addClass('highslide').each(function () {
this.onclick = function () {
//if the flag is true sent an object with the property else an empty object
return hs.expand(this, hsGroupByWpGallery ? {
slideshowGroup: this.parentNode.parentNode.parentNode.id
} : {});
};
});
I think you might want this, based on the other code:
jQuery('.gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
if(hsGroupByWpGallery){
return hs.expand(this, {
slideshowGroup: this.parentNode.parentNode.parentNode.id
});
}
};
});

Angular Load data OnScroll

Just need some help, Im stuck and don't have an idea how to implement this append in Angular. I want to append a new data when the user scroll and the scrollbar hits the bottom. here is my code.
note DataModel.getAllData access the api with paging parameters.
I stuck on how to add new elements during onScroll. here is what i am trying to achieve on scroll I want to use the list_of_data.html. put all the new data in list_of_data. get the final output then append it on data_list.
directive
app.directive('scroll', function() {
console.log('scroll directive');
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight - 20) {
scope.$apply(attr.scroll);
}
});
};
});
main.html
<div class="container" scroll="onScroll()">
<div id="data_list" data-ng-init="getData()" >
<div ng-include src="data_uri"></div>
</div>
</div>
list_of_data.html
<div ng-repeat="mydata in list_of_data">
<div>mydata.name</div>
<div>mydata.description</div>
</div>
controller
var next;
$scope.onScroll = function()
{
if(next != null || next != "undefined"){
DataModel.getAllData(user_info,next)
.then(function (data) {
$scope.list_of_data = data;
next = data.next;
}, function (error) {
console.log("error");
});
}
}
$scope.getData = function(){
$scope.data_uri= 'views/list/list_of_data.html';
DataModel.getAllData(user_info)
.then(function (data) {
$scope.list_of_data = data;
next = data.next;
}, function (error) {
console.log("error");
});
}
//EDIT I tried something like this. but it just overrides the data
You can do something like :
$scope.onScroll = function()
{
DataModel.getNextData().then(function(results){
$scope.list_of_data.push(results);
});
}
Assuming that you have a specific API function to get the next results.

How can I make a function return true after functions within jQuery's .each() finishes?

I'm using knockout.js. I have several editable grids in a form, and the names of the fields inside those grids matter, so using uniqueName isn't an option.
So what I'm doing is calling a function to rename the fields when the user clicks submit.
function renameFields(o, index) {
var el_name = $(o).attr('name');
var name_index = el_name.lastIndexOf('_') + 1;
$(o).attr('name', el_name.substring(0, name_index) + index);
}
function Submit() {
self.submit = function() {
// Use unique AND meaningful input/textarea [name]s in .grids.
window.index_arr = [];
$('.grid').each(function() {
$(this)
.children('.row')
.each(function(i){
window.index_arr.push(i);
$(this)
.find('input, textarea')
.each(function() {
renameFields($(this), window.index_arr[i]);
})
})
});
// Submit the form
return true;
}
}
renameFields works fine, but true is returned before it's called on every field.
Let Knockout do the dirty work of keeping the indices of your attributes up to date with something like this:
HTML:
<div id="grid" class="grid">
<div class="row" data-bind="foreach:rows">
<input data-bind="attr: { name: 'string_' + $index() }">
<textarea data-bind="attr: { name: 'string_' + $index() }">
</div>
</div>
View model:
var vm = {
rows: ko.observableArray()
};
ko.applyBindings(vm, document.getElementById("grid"));
Please give us more about how you are setting up the click handlers...
I'll take a SWAG at it... I think you are dealing with hoisting
Without a fiddle I can't test it, but try the following code.
var renameFields = function(o, index) {
var el_name = $(o).attr('name');
var name_index = el_name.lastIndexOf('_') + 1;
$(o).attr('name', el_name.substring(0, name_index) + index);
}
var Submit = function() {
self.submit = function() {
// Use unique AND meaningful input/textarea [name]s in .grids.
window.index_arr = [];
$('.grid').each(function() {
$(this)
.children('.row')
.each(function(i){
window.index_arr.push(i);
$(this)
.find('input, textarea')
.each(function() {
renameFields($(this), window.index_arr[i]);
})
})
});
// Submit the form
return true;
}
}

Categories