I am trying to use slice() method on an object array in AngularJS but its says 'slice is not a method'.
Here is the code
.controller('AppCtrl', function ($scope, Faq) {
$scope.filteredFaqData = [];
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 3;
$scope.faqData = {};
Faq.get().then(function (msg) {
$scope.faqData = msg.data;
});
$scope.$watch('currentPage + numPerPage', function () {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
$scope.filteredFaqData = $scope.faqData.slice(begin, end);
});
});
I am getting the data from json file in $scope.faqData using a service and it is working.
But slice method is not working in the console it is giving error "$scope.faqData.slice" is not a method.
.slice is a method available from the array prototype.
Your faqData is defined as a plain 'ol object. As such it does not have access to .slice.
Your service is mutating faqData into an array however giving you access to .slice, this is only when it resolves.
So, the issue is your watcher may fire before your promise resolves meaning your trying to call slice from a plain object.
You should define your objects to the type you will be using them with, avoid mutating objects where possible.
Your watch may also need to handle the fact your promise has not resolved yet, that depends on what you intend to do in the watcher.
slice docs
Related
I'm using array value as variable and then call the function N method, how I get them in function N.
I really want to simulate the Javascript array method, I don't want to use parameters to achieve it. For example,
var p1 = [1,2,3,4,5]; p1.push(6);
function _Array() {
this._this = this;
}
_Array.prototype.show = function () {
this._this.forEach(function(item){alert(item);}) //how to print 1,2,3,4,5
};
var p1 = [1,2,3,4,5];
p1 = new _Array();
//p1._Array.call(p1); //not work
// new _Array().show.call(p1); //not work
// p1.show(); //not work
You have to store that in the instance
function N(arr) {
this._this = arr
}
N.prototype.say = function () {
this._this.forEach(function (item) {
console.log(item)
})
}
p1 = new N([1, 2, 3, 4, 5])
p1.say()
If you are insistent on wanting to write a method that takes the array by reference, you can modify the array prototype like so:
Array.prototype.show = function() {
this.forEach(item => alert(item));
}
However, it is a VERY BAD IDEA to modify the built in object prototypes, as this can cause conflicts with external libraries implementing their own "show" function that is being used differently, or cause incompatibilities with future versions of JS that implements this method.
It would be far more prudent in most situations to pass the array as a parameter, unless you have a very specific reason why you're not doing so. In that case, you should at least prefix the method with some sort of project identifier to minimize the chances of conflicts occurring.
What is the meaning of return { push:function ..... in below code snippet. When I googled I found that push() method adds new items to the end of an array, and returns the new length. So I am not sure what is push:. It seems to be some kind of syntax. Can someone please explain me.
function(notificationsArchive) {
var MAX_LEN = 10;
var notifications = [];
return {
push: function(notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
//push method can rely on the closure scope now!
if (newLen > MAX_LEN) {
notificationToArchive = this.notifications.pop();
notificationsArchive.archive(notificationToArchive);
}
},
// other methods of the NotificationsService
};
The method push you are referencing has nothing to do with push with Arrays, it is a public method exposed by the module pattern. It only exposes methods and properties that the author of the code wants you to be able to call/set. It hides the variables MAX_LEN and notifications so they can not be changed from outside.
References on OO Module patterns:
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript
http://www.raymondcamden.com/2013/05/13/JavaScript-Design-Patterns-The-Revealing-Module-Pattern
This is a follow-up question to Take elements while a condition evaluates to true (extending ElementArrayFinder) topic and #cvakiitho's answer in particular.
The problem:
After executing the following code:
var i = 0;
var el = element.all(by.css('ul li a'));
var tableItems = [];
(function loop() {
el.get(i).getText().then(function(text){
if(text){
tableItems.push(el.get(i));
i+=1;
loop();
}
});
}());
tableItems would contain an array of ElementFinder instances - or, to put it simple - an array of web elements.
The Question:
Is it possible to convert an array of ElementFinders to an ElementArrayFinder instance?
The Motivation:
The reason I want this is to have all of the convenient ElementArrayFinder functional utilities, like map(), each(), reduce() and to be able to call getText() on it producing a promise which would resolve into an array of element texts.
Constructor of ElementArrayFinder basically requires two main arguments: protractor instance and a function getWebElements, which should return a promise resolving to an array of Web Elements. As soon as every ElementFinder has a method to get it's Web Element called getWebElement, it is possible to create such a function.
var arrayOfElementFinders = [el1, el2, el3];
var getWebElements = function () {
// create array of WebElements from array of ElementFinders
var webElements = arrayOfElementFinders.map(function (ef) {
return ef.getWebElement();
});
// immediately resolve and return promise
return protractor.promise.fulfilled(webElements);
};
Now when all requirements are satisfied, it is possible to create a new instance of ElementArrayFinder:
var elArrayFinder = new protractor.ElementArrayFinder(protractor, getWebElements);
To make it easy to use I would add a static method to ElementArrayFinder constructor and include it somewhere before tests start:
protractor.ElementArrayFinder.fromArray = function (arrayOfElementFinders) {
var getWebElements = function () {
var webElements = arrayOfElementFinders.map(function (ef) {
return ef.getWebElement();
})
return protractor.promise.fulfilled(webElements);
};
return new protractor.ElementArrayFinder(protractor, getWebElements);
};
And use it in tests like:
var elArrayFinder = protractor.ElementArrayFinder.fromArray(arrayOfElementFinders);
I'm passing my viewmodel from the server as JSON, then adding some functions and observables to the viewmodel before calling ko.applyBindings().
My problem is that I want to loop through an array of objects, and add a computed observable onto each. However, all the computed observables seem to be bound to the last object in my array.
Here is the loop:
for (var i = 0; i < viewModel.Records().length; i++)
{
var record = viewModel.Records()[i];
viewModel.Records()[i].CachedMonthlyAmount = ko.computed(function () {
var frequencyType = viewModel.GetFrequencyTypeByID(record.SelectedFrequencyTypeID());
return frequencyType.MonthlyCalculationFactor() * record.Amount();
});
}
I've also created this jsfiddle to demonstrate.
When using variables in for loop like that you will need to make a closure (ex. use immediately-invoked function expression IIFE) so you can bind the record variable within each computed function to a separate unchanging value outside of the computed function.
Simply you need to do:
for (var i = 0; i < viewModel.Records().length; i++)
{
var record = viewModel.Records()[i];
(function(record){
record.CachedMonthlyAmount = ko.computed(function () {
var frequencyType = viewModel.GetFrequencyTypeByID(record.SelectedFrequencyTypeID());
return frequencyType.MonthlyCalculationFactor() * record.Amount();
});
}(record));
}
Js Fiddle Demo
The second parameter to ko.computed defines the target object that will be used to evaluate this in the computed callback function. So you can pass your record as the second parameter and this will result in correct computed evaluation.
for (var i = 0; i < viewModel.Records().length; i++) {
var record = viewModel.Records()[i];
viewModel.Records()[i].CachedMonthlyAmount = ko.computed(function () {
var frequencyType = viewModel.GetFrequencyTypeByID(this.SelectedFrequencyTypeID());
return frequencyType.MonthlyCalculationFactor() * this.Amount();
}, record);
}
Read more about managing this from ko.computed documentation.
Updated fiddle: http://jsfiddle.net/952pfm61/4/
well much simpler solution would be using arrayForEach from Utility functions in Ko
Modified piece of code :
ko.utils.arrayForEach(viewModel.Records(),function(item){
item.CachedMonthlyAmount = ko.computed(function () {
var frequencyType = viewModel.GetFrequencyTypeByID(item.SelectedFrequencyTypeID());
return frequencyType.MonthlyCalculationFactor() * item.Amount();
});
});
Using index based value assigning i believe those days are gone for good(atmost) . As far as ko goes as your are trying to create a dependency via computed so (index based assigning) it is not advisable. which indeed possible by using 2nd parameter of computed function .
working sample here
I have a function in my controller which request dataservice for data and I am trying to add to current scope variable but it is giving me undefined error
$scope.affiliates = d.data;
if (isNaN($scope.affiliate_id)){
var i = 0;
while (i < $scope.affiliates.length){
var affiliate_id = $scope.affiliates[i].affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
$scope.affiliates[i].apiData = apiData;//ERROR IN HERE
});
i++;
}
}
TypeError: $scope.affiliates[i] is undefined
I have also tried returning data from dataService and set it outside but it always returns empty.
How can i resolve this?
Don't forget that getAffiliateConversionApiService is returning a promise (which means it is an async operation) therefore your while block will execute for every possible i value before you even get the result from getAffiliateConversionApiService.
Let's imagine that $scope.affiliates.length is 6. When your callback code inside the then is executed, your i will be 7.
One solution is to use angular.forEach instead of the while. However, if you still want to use the while you will need to store the i value in another variable:
while (i < $scope.affiliates.length){
var index = i;
var affiliate_id = $scope.affiliates[i].affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
$scope.affiliates[index].apiData = apiData;
});
i++;
}
This is not really an answer, but here is some debugging that should help you out.
Also, I switched from using a while loop to using angular's forEach
$scope.affiliates = d.data;
if (isNaN($scope.affiliate_id)){
console.log($scope.affiliates); // what does this return?
angular.forEach($scope.affiliates, function(value, index){
console.log(value); // output the affiliate, if any
var affiliate_id = value.affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
console.log(apiData); // see what is returned
$scope.affiliates[index].apiData = apiData; // still an error?
});
});
}