I'm calling this factory function in Angular, passing in a percentage value as a parameter:
if(scroll_factory.fetch(50)) {
// If the user has scrolled 50% of page height, do something.
};
scroll_factory.$inject = ['$window', '$document'];
function scroll_factory($window, $document) {
var factory = {
fetch: fetch,
fetch_position: fetch_position
}
return factory;
function fetch(percent) {
$document.on('scroll', fetch_position)
function fetch_position() {
var window_inner_height = $window.innerHeight;
var y = pageYOffset;
var percentage = (y / window_inner_height) * 100;
if (percentage > percent) {
// Condition true - return true for the outer fetch.
}
}
return false;
}
What I want is for the fetch function to return true when the scroll condition is true. Thanks.
Your approach will not work. In:
if(scroll_factory.fetch(50)) { ... }
you are already evaluating (the function returns a value a this point) the fetch function.
You need to rethink that the fetch_position callback is continuously evaluated.
You want to have a scroll-spy functionality as a separated module?
.factory('ScrollSpy', function($document, $window) {
return function(callback) {
$document.on('scroll', function(e) {
var y = ...
if ( y > 123)
callback(y);
})
};
})
One way to solve this is to provide a factory which accepts a callback which can be evaluated depending on your demands.
I made a small example, hope it helps: http://plnkr.co/edit/du3dpMlaKYZFuy8WA0QL?p=preview
Related
I am trying to test my DOM project, so it should make sure that the cost is 2.75 and sms is 0.75. It returns an assertion error that says expected 2.75 to equal undefined. I need help
accessing the correct values of call and sms.
Here's my factory function
var callCost = 0;
var smsCost = 0;
var totalCost = 0;
const warning = 30;
const critical = 50;
function getCall() {
return callCost;
}
function getSms() {
return smsCost;
}
function getTotal() {
totalCost = callCost + smsCost;
return totalCost;
}
function radioButtons(selectedBill) {
if (selectedBill === "call") {
callCost += 2.75;
} else if (selectedBill === "sms") {
smsCost += 0.75;
}
}
function totalClassName() {
if (getTotal() >= warning && getTotal() < critical) {
return "warning";
} else if (getTotal() >= critical) {
return "critical";
}
}
return {
getCall,
getSms,
getTotal,
radioButtons,
totalClassName
}
}
describe('The radio-bill function', function(){
it('Should be able to add call at 2.75', function(){
var itemType = RadioBill();
itemType.radioButtons("call");
assert.equal(2.75, itemType.radioButtons("call"))
})
})
You only need to change your assert line to get your test working.
var itemType = RadioBill();
itemType.radioButtons("call");
assert.equal(itemType.getCall(), 2.75);
Here, the first thing to note is that the order of the arguments in a call to assert does matter. The first argument is the actual value, the second one is the expected value. Typically, but not always the actual value will be the result of an operation, and the expected value will be constant.
The second point is that in your code the function radioButtons does not return a value, it just changes the value of an internal state variable. But there is already the function getCall to get that value, and that is what the assert line is checking.
I know some javascript function declarations. Like exression function, anonymous function, but I do not understand what kind of syntax of these two functions? Can anybody tell me what is the name of these two functions? I mean "manipulateData: function (input)" and "getDataById: function (id)".
Why return statement can have this syntax to return two functions? Why not return one function in one time instead of two functions? It will be great if you can give me some reference documents? Thanks.
app.service('MyService', function ($http, $q, $angularCacheFactory) {
var _dataCache = $angularCacheFactory('dataCache', {
maxAge: 3600000 // items expire after an hour
});
/**
* #class MyService
*/
return {
manipulateData: function (input) {
var output;
// do something with the data
return output;
},
getDataById: function (id) {
var deferred = $q.defer();
if (_dataCache.get(id)) {
deferred.resolve(_dataCache.get(id));
} else {
// Get the data from the server and populate cache
}
return deferred.promise;
}
};
});
These functions are just anonymous functions that happen to be values in an object. Consider this:
var object = {
add: function(x, y) {
return x + y;
}
};
object.add(1, 2); // = 3
This is the same as:
function addFunction(x, y) {
return x + y;
}
var object = {
add: addFunction
};
object.add(1, 2); // = 3
There's nothing special about these functions, as they're just normal properties of an object.
You are not returning a function in this case but an Object.
When you define a service in angularjs you have to provide its implementation in the callback (the second argument of app.service)
This callback has to return methods you want to make available to other parts of your application.
Then in a controller or in another service you will be able to write:
app.controller("MyCtrl", ["MyService", function(MyService) {
MyService.getDataById('an id');
}]);
Angular Service returns an instance of the service you bind to the app namespace, those functions in the return statement are public methods that can be worked with. Basically an Object that contains two methods manipulateData, and getDataById.
It's similar to this
function company() {
let product; // This is private
// Public Methods
return {
setLatestProduct: function(value) {
product = value;
console.log(product, ' set');
},
getLatestProduct: function() {
return product;
}
}
}
const apple = company();
console.log(apple); // { setLatestProduct: function, getLatestProduct: function }
I have an AngularJS project, and I'm using a modified version of md-steppers, whose interesting functions boil down to this:
var enableNextStep = function () {
//do not exceed into max step
if ($scope.selectedStep >= $scope.maxStep) {
return;
}
//do not increment $scope.stepProgress when submitting from previously completed step
if ($scope.selectedStep === $scope.stepProgress - 1) {
$scope.stepProgress = $scope.stepProgress + 1;
}
};
var completeCurrentStep = function (CurrentStep) {
$scope.stepData[CurrentStep].completed = true;
};
$scope.moveToNextStep = function moveToNextStep() {
if ($scope.selectedStep < $scope.maxStep) {
enableNextStep();
$scope.selectedStep = $scope.selectedStep + 1;
completeCurrentStep($scope.selectedStep - 1); //Complete After changing Step
}
};
$scope.moveToPreviousStep = function moveToPreviousStep() {
if ($scope.selectedStep > 0) {
$scope.selectedStep = $scope.selectedStep - 1;
}
};
The problem is that I would like to use these four functions in two different controllers (so as to not repeat them), that have different stepProgress, selectedStep and maxStep values. I couldn't find a way to do so with services, but I might just be confused about the way AngularJS work, as I am more used to Python.
Thanks.
Abstracting out that functionality into a factory that accepts an array of callbacks and a controller's ng-model would make it more reusable. Of course, ultimately the API you want is up to you. The goal is that you don't want any $scope business inside the factory, it shouldn't be concerned about what's inside the callbacks, it just steps through them.
/**
* #param steps {array} - array of callbacks
*/
function stepperFactory(steps) {
iterate(0, steps);
}
function iterate(current, steps) {
if (!steps[current])
return;
if (typeof steps[current] === 'function')
// pass an async "done" callback
// so your array of input callbacks can be async
// you could also use promises or $q for this
steps[current](() => iterate(current + 1, steps));
}
And so the api you expose would be like:
['stepperFactory', function(stepperFactory) {
this.model = { step: 0, msg: 'start' };
this.steps = [
(done) => {
this.model.step++;
done();
},
(done) => {
setTimeout(() => {
this.model.msg = '3rd step';
this.model.step++;
done();
});
}
];
stepperFactory(this.model, this.steps);
}]
You can use service to share functions which will take maxStep, stepProgress, etc as arguments and instead of modifying the $scope, they will return updated values.
In service:
function moveToPreviousStep(step) {
if (step > 0) {
return (step - 1);
}
return step;
};
and in controller
function moveToPreviousStep() {
$scope.selectedStep = service.moveToPreviousStep($scope.selectedStep);
}
$scope.moveToPreviousStep = moveToPreviousStep;
Let's say I have the following JavaScript
function myGlobalFunction(){
function firstInnerFunction(){
return "rainbows";
}
function secondInnerFunction(){
function innerInnerFunction(){
return "clouds";
}
return innerInnerFunction();
}
return firstInnerFunction(); //valid call
}
Is there anyway I can call firstInnerFunction() in the global scope? If so(and better yet), can I go two levels down and call innerInnerFunction() from the global scope?
What are you trying to achieve in doing something like that? I would suggest something like the following:
var myGlobalFunction = {
var innerInnerFunction() { return "clouds"; }
get firstInnerFunction() { return "rainbows"; }
get secondInnerFunction() { return innerInnerFunction(); }
};
You can then call firstInnerFunction() with something like this:
myGlobalFunction.firstInnerFunction;
as for calling innerInnerFunction() from the global scope, myGlobalFunction.secondInnerFunction() will serve the same purpose as calling innerInnerFunction() directly.
In short, no. JavaScript is function scoped, so everything inside is hidden from the outside. So in order to access the inner functions you'll need to expose them somehow.
Absolute simplest (but ugly) option is to do something like this:
var secondInnerCopy;
function myGlobalFunction(){
function firstInnerFunction(){
return "rainbows";
}
function secondInnerFunction(){
function innerInnerFunction(){
return "clouds";
}
return innerInnerFunction();
}
secondInnerCopy = secondInnerFunction;
return firstInnerFunction(); //valid call
}
myGlobalFunction();
secondInnerCopy(); //valid (only after myGlobalFunction called though)
Better option would be to restructure into an object graph with functions:
var global = {
myGlobalFunction: function(){
return this.inner.firstInnerFunction(); //valid call
},
inner: {
firstInnerFunction: function() {
return "rainbows";
},
secondInnerFunction: function(){
return this.inner.innerInnerFunction();
},
inner: {
innerInnerFunction: function(){
return "clouds";
}
}
}
};
global.myGlobalFunction();
global.inner.inner.innerInnerFunction();
//etc...
I don't think that this is possible, however I could be wrong. You could do something like this to access the nested functions via the top level function.
function A(x) {
function B(y) {
function C(z) {
alert(x + y + z);
}
C(3);
}
B(2);
}
A(1); // alerts 6 (1 + 2 + 3)
Another Example
function addSquares(a,b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2,3); // returns 13
b = addSquares(3,4); // returns 25
c = addSquares(4,5); // returns 41
Check this link, it has some good information on functions and nested functions in Javascript. Javascript Function Scope
I believe something like this would also work, though I like what Alconja did.
var myGlobalFunction = {
firstInnerFunction: function(){
return "rainbows";
},
secondInnerFunction : function(){
var innerInnerFunction = function(){
return "clouds";
}
var inninnerFuncTwo = function(){
return 'more stuff';
}
return {
inn : innerInnerFunction,
inn2: inninnerFuncTwo
}
}
}
myGlobalFunction.secondInnerFunction().inn();
myGlobalFunction.secondInnerFunction().inn2();
If you want a call to globalFunction to define firstInnerFunction, you can do that as follows
function globalFunction() {
firstInnerFunction = function() { return "rainbows" ; } ;
}
For innerInner function, you can do as follows
function globalFunction() {
firstInnerFunction = function() { return "rainbows" ; } ;
secondInnerFunction = function() {
innerInnerFunction = function() { return "clouds" ; } ; }
}
Now you can do this
globalFunction() ; // defines firstInnerFunction and secondInnerFunction in the global scope
firstInnerFunction() ; // returns "rainbows"
secondInnerFunction() ; // defines innerInnerFunction
innerInnerFunction() ; // returns "clouds"
I have a function that I want it execute alternating processes every time it's triggered. Any help on how I would achieve this would be great.
function onoff(){
statusOn process /*or if on*/ statusOff process
}
One interesting aspect of JavaScript is that functions are first-class objects, meaning they can have custom properties:
function onoff() {
onoff.enabled = !onoff.enabled;
if(onoff.enabled) {
alert('on');
} else {
alert('off');
}
}
For this to work, your function should have a name. If your function is anonymous (unnamed), you can try to use arguments.callee to access it, but that is deprecated in the new ES5 standard and not possible when using its strict mode.
With the use of closures, you can define a static variable that is only accessible by the function itself:
var toggle = (function()
{
var state = true;
return function()
{
if(state)
alert("A");
else
alert("B");
state = !state;
};
})();
Now you can repeatedly invoke toggle(), and it would alternate between "A" and "B". The state variable is unaccessible from the outside, so you don't pollute the global variable scope.
Use closures. In addition to closures, this method demonstrates arbitrary arguments and arbitrary numbers of functions to cycle through:
Function cycler
function cycle() {
var toCall = arguments;
var which = 0;
return function() {
var R = toCall[which].apply(this, arguments);
which = (which+1) % toCall.length; // see NOTE
return R;
}
}
Demo:
function sum(a,b) {return a+b}
function prod(a,b) {return a*b}
function pow(a,b) {return Math.pow(a,b)}
function negate(x) {return -x;}
var f = cycle(sum, prod, pow, negate);
console.log(f(2,10)); // 12
console.log(f(2,10)); // 20
console.log(f(2,10)); // 1024
console.log(f(2)); // -2
// repeat!
console.log(f(2,10)); // 12
console.log(f(2,10)); // 20
console.log(f(2,10)); // 1024
console.log(f(2)); // -2
Arbitrary cycler
Alternatively if you do not wish to assume all cycled things are functions, you can use this pattern. In some ways it is more elegant; in some ways it is less elegant.
function cycle() {
var list = arguments;
var which = 0;
return function() {
var R = list[which];
which = (which+1) % toCall.length; // see NOTE
return R;
}
}
Demo:
var cycler = cycle(function(x){return x}, 4, function(a,b){return a+b});
cycler()(1); // 1
cycler(); // 4
cycler()(1,5); // 6
// repeat!
cycler()(1); // 1
cycler(); // 4
cycler()(1,5); // 6
NOTE: Because javascript thinks 10000000000000001%2 is 0 (i.e. that this number is even), this function must be three codelines longer than necessary, or else you will only be able to call this function 10 quadrillion times before it gives an incorrect answer. You are unlikely to reach this limit in a single browsing session... but who knows
If I'm understanding what you want, this may be what you're looking for:
var AlternateFunctions = function() {
var one = function() {
// do stuff...
current = two;
}, two = function() {
// do stuff...
current = one;
}, current = one;
return function() {
current();
}
}();
Then calling AlternateFunctions(); will cycle between one() and two()
There are a couple of good answers already posted, but I'm wondering what you're trying to achieve. If you're keeping track of some DOM element's state, instead of having state saved within the function, you should check the state of the element so that the function isn't operating in a vacuum (and possibly not doing what you expect). You can check some attribute, e.g., class:
function onoff(obj){
if(obj.className === 'on') {
obj.className = 'off';
}else{
obj.className = 'on';
}
}
var last=0;
function toggle() {
if(last) {
last=0;
// do process 2
}
else {
last=1;
// do process 1
}
}
See jsfiddle demo
var status=true;
function onOff(el){
/*
* toggle
*/
status = (status ? false : true);
status
? el.html('on')
: el.html('off');
}