How to dynamically change $http service URL in AngularJS? - javascript

I am using angular $http service to get/post the data. Currently all the services using common URL to retrieve the data. Now I have requirement where I want to switch between the 2 URLs based on specific condition. So what will be the best approach to implement this functionality seamlessly without making many changes into existing service code?
Note - I don't want to write identification logic at each services.
I created sample example to explain the problem. In example currently I am using TEST_FILE1 from constant service as URL but i want to switch between TEST_FILE1 and TEST_FILE2 based on specific condition say based on random number returned from randomNumberService. Sample Example - Sample Plunkar Any help appreciated!
app.constant('RESOURCES', (function() {
return {
TEST_FILE1: 'test.json',
TEST_FILE2: 'test1.json',
}
})());
app.factory('myService', function($http, RESOURCES) {
return {
async: function() {
return $http.get(RESOURCES.TEST_FILE1); //1. this returns promise
}
};
});
app.factory('randomNumberService', function() {
return {
getRandomNumber: function() {
return Math.floor((Math.random() * 6) + 1);
}
}
});

To not repeat the URL choice logic everywhere, then you have to move it in an other factory.
app.factory('identification', function (RESOURCES, randomNumberService) {
return {
getUrl: function () {
var randomNumber = randomNumberService.getRandomNumber();
var url = randomNumber > xxx ? RESOURCES.TEST_FILE1 : RESOURCES.TEST_FILE2;
return url;
}
};
});
app.factory('myService', function($http, identification) {
return {
async: function() {
return $http.get(identification.getUrl());
}
};
});
app.factory('randomNumberService', function() {
return {
getRandomNumber: function() {
return Math.floor((Math.random() * 6) + 1);
}
}
});

Related

When I use $scope.watch in angular, I have an issue in passing data

I use $scope.watch and set and get to pass one data from one controller to another. This works fine, but when I do this with different html files, data is not transferred in my first click. because browser is refreshed. But when I click go back arrow in chrome and set a value and clicked, I can get the value in my second page. How can I resolve this. Please tell me solution.
var data = {
Quantity: ''
};
return {
getQuantity: function() {
return data.Quantity;
},
setQuantity: function(quantity) {
data.Quantity = quantity;
}
};
.controller('DetailsCtrl', function($scope, $http, Data) {
$scope.quantity = '';
$scope.$watch('quantity', function(newValue50, oldValue50) {
if (newValue50 !== oldValue50)
Data.setQuantity(newValue50);
});
})
.controller('FifthCtrl', function($scope, $http, Data) {
$scope.$watch(function() {
return Data.getQuantity();
}, function(newValue50, oldValue50) {
if (newValue50 !== oldValue50)
$scope.quantity = newValue50;
});
})
If you want to store the value between different sessions in the browser you could add the quantity value to the local storage.
Here's an angularjs service/factory example
app.factory('Data', function () {
var data = {
Quantity: ''
};
return {
getQuantity: function () {
if (localStorage.getItem("data") === null) {
data.Quantity = 0;
} else {
var jsonData = localStorage.getItem('data');
data.Quantity = JSON.parse(jsonData);
}
return data.Quantity;
},
setQuantity: function (quantity) {
data.Quantity = quantity;
localStorage.setItem("data", JSON.stringify(data));
}
};
});
Note 1: You actually don't need the object 'data' in this case as you store the value in the storage but I let it stand as is.
Note 2: The localStorage can't store complext types such as objects and you need to make strings of them before you store them, thats why the JSON.stringify() is used.

Set factory variable from state

I am using factories to get feed data for different categories in wordpress using the Rest-API and I am wondering if I am making a complete mess of it.
I have around 13 different categories that I am making just a basic call to that look like this:
.factory('Articles1', function ($http) {
var articles1 = [];
storageKey = "articles1";
function _getCache() {
var cache = localStorage.getItem(storageKey );
if (cache)
articles = angular.fromJson(cache);
}
return {
all: function () {
return $http.get("http://www.example.com/wp-json/posts?filter[category_name]=category1").then(function (response) {
articles1 = response.data;
console.log(response.data);
return articles1;
});
},
get: function (articleId) {
if (!articles1.length)
_getCache();
for (var i = 0; i < articles1.length; i++) {
if (parseInt(articles1[i].ID) === parseInt(article1Id)) {
return articles1[i];
}
}
return null;
}
}
})
there is a separate call for each of the 13 categories to get articles.
Is there a way to set the [category_name] to a variable, possibly the state name or something to that I can just make 1 call to wordpress and the url will rely on what state the user has chosen? I have been trying to learn angular and from what I have seen I am sure there must be a more elegant way of doing something like this?
Inject the Angular $location provider and you will have access to every part of your current URL (you can also use it to listen to the state changes). Then just build the URL to Wordpress dynamically:
.factory('Articles1', function ($http, $location) {
// ...
return {
all: function () {
var currentCategory = $location.path().split('/')[1]; // Assuming the category is part of the path and at the second place
var wpUrl = 'http://www.example.com/wp-json/posts?filter[category_name]=' + currentCategory;
return $http.get(wpUrl).then(function (response) { // ...

How to process users in one specific order using map o each function?

I create a cloud function that process all the items in a specific order and then make a network request to an API for each item and update them with the result of the API.
I tried to do something like this. But the order is not respected and the computation of the "remainingCredits" is wrong.
Note that the function "getUserPageView()" make an API call and return a promise with the result.
query.descending("createdAt");
return query.find().then(function(items) {
var remainingCredits=0;
var promises= _.map(items, function(item){
var credit=item.get("credit_buy");
return getUserPageView(123, new Date()).then(function(pageviews){
var usedCredit=credit-pageviews;
if(remainingCredits>0)
return remainingCredits+credit;
if(credit-usedCredit<=0){
console.log("usedCredit:"+usedCredit);
item.set("used",true);
return 0;
}else{
remainingCredits+=usedCredit;
return remainingCredits;
}
});
});
return Parse.Promise.when(promises);
}).then(function(results){
console.log(_.toArray(arguments));
status.success();
}, function(error){
status.error(error);
});
How can I calculate the remainingCredits with a specific order?
If it is important to process the asynchronously derived data in the original items order, then it should not be processed as it arrives.
Try waiting for all the data to arrive, then process it.
query.descending("createdAt");
return query.find().then(function(items) {
return Parse.Promise.when(_.map(items, function(item) {
return getUserPageView(123, new Date()).then(function(pageviews) {
//here, create an object with all the data required for later processing
return {
item: item,
pageviews: pageviews
};
});
}));
}).then(function() {
var remainingCredits = 0;
var runningBalance _.toArray(arguments).map(function(obj) {
var credit = obj.item.get("credit_buy");
var usedCredit = credit - obj.pageviews;
if(remainingCredits > 0)
return remainingCredits + credit;
if((credit - usedCredit) <= 0) {
console.log("usedCredit:" + usedCredit);
obj.item.set("used", true);
return 0;
} else {
remainingCredits += usedCredit;
return remainingCredits;
}
});
console.log(runningBalance);
status.success();
return runningBalance;
}, function(error) {
status.error(error);
});
I invented the variable name runningBalance because that is what you appear to be trying to create.
I tried to verify the processing of credits and can only suggest that it needs looking at again. For example, if usedCredit = credit - obj.pageviews, then the test if((credit - usedCredit) <= 0) {...} will simplify to if(obj.pageviews <= 0) {...}, which doesn't seem right.

Meteor call template method from another method

I want to call a method within a method for clientside but I don't know how to handle it, i've tried by calling like myFunction()and this.myFunction() but it is not working... This is my code
Template.decision.rendered = function () {
if ($(".active").length == 0) {
var random = Math.floor(Math.random() * 1000);
var $items = $(".item");
$items.eq(random % $items.length).addClass("active");
}
$.each($(".item"), function (index, value) {
if (Session.get($(this).attr('id'))) {
this.showResults(value.option);
}
});
};
Template.decision.showResults = function($option) {
$('#result').html('Option ' + $option + ' is voted');
};
As you can see I want to call showResults for each item inside rendered callback...
Found it using Template.decision.showResults(); silly me.
I think that a better way depending on what you are trying to do would be either to use a Session variable or a Meteor Method:
Session Variable.
Template.decision.created = function() {
Session.setDefault('showResults', false);
}
Template.decision.rendered = function() {
// [...]
$.each($(".item"), function (index, value) {
if (Session.get($(this).attr('id'))) {
Session.set('showResults', true);
}
});
}
Template.decision.showResults = function() {
return Session.get('showResults');
}
// in your template
<template name="decision">
{{#if showResults}}
<p>Here is the results.</p>
{{/if}}
</template>
Meteor Method.
// On the client.
Template.decision.rendered = function() {
// [...]
$.each($(".item"), function (index, value) {
if (Session.get($(this).attr('id'))) {
Meteor.call('showResults', function(error, result) {
if (!error and result === true) {
$('.class').hide() // It is here that you modify the DOM element according to the server response and your needs.
}
});
}
});
}
// Server-side method
// But, if you want it to react like a stub, put it inside your lib folder.
Meteor.methods({
showResults: function() {
// do your thing
result = true; // let's say everything works as expected on server-side, we return true
return result;
}
});

Firebase - Angular $on listener returns data undefined value on callback

I'm Trying to fetch some data from a firebase , I'm using angular firebase plugin. I double checked debugging in inspector, the url is the correct one. It responds back, that means that the url is correct but the callback's arguments is undefined.
I'm using loaded because I need it to fire once. I tried value but shame thing.
I think I exhausted all my energy on this for today so a second opinion would be great.
P.S. I really wonder why they are not using a promise instead of a callback.
fragment from angular + firebase factory
var seekMatch = function(player) {
var deferred = $q.defer();
angular.forEach(matches.$getIndex(), function(matchId) {
var matchRef = firebaseRef('matches/' + matchId); // <-- double checked, the url sends me to the correct firebase record
var matchDB = $firebase(matchRef);
matchDB.$on('loaded', function(data) {
console.log(data); // <----- return's undefined
if (matchMakingFormula(data.playerA, player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
});
return deferred.promise;
};
I'm adding all the code here to give you a better idea of what I'm trying to do.
Full code of my fb.match.service
'use strict';
angular.module('angularfireApp')
.factory('FBmatchService', ['$rootScope' , '$q', '$firebase', 'firebaseRef',
function ($rootScope, $q, $firebase, firebaseRef) {
// Service logic
var matchesRef = firebaseRef( '/matches/' );
var matches = $firebase(matchesRef);
var match = null;
var matchMakingFormula = function (playerA , playerB) {
return playerA.type !== playerB.type
&& distanceFormula( playerA.location.lat , playerA.location.long, playerB.location.lat , playerB.location.long ) < 1
};
var distanceFormula = function (lat1 , lon1 , lat2, lon2) {
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
};
var getMatch = function (matchId) {
match = matches.$getIndex(matchId);
return match;
};
var seekMatch = function ( player ) {
var deferred = $q.defer();
angular.forEach(matches.$getIndex() , function (matchId){
var matchRef = firebaseRef( 'matches/'+matchId );
var matchDB = $firebase( matchRef );
matchDB.$on('loaded',function (data) {
console.log(data);
if (matchMakingFormula(data.playerA , player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
});
return deferred.promise;
};
// Public API here
return {
get: function (matchId) {
return getMatch(matchId);
},
seek: function (points) {
return seekMatch(points);
//return match.promise;
},
new: function (points) {
//return match.promise;
},
join: function (match) {
//return match;
}
};
}]);
Thanks in advance. Cheers and have fun!
OK, finally "found" the solution. Thanks to kato that remind me to check my version.
Current version 0.7.2 preview works for me. Thing is that is not on bower yet and I assumed that I had the latest version while updating from bower. Which was wrong.
collection.$child( matchId ).$on('loaded' , function(match){ //<---- match now returns the proper object but also null or {} empty object sometimes if empty.
if (match) {
if (valid(match)){ //<-- so you need to validate the output not just !match
deferred.resolve(match);
}
else
{
deferred.reject('invalid');
}
}
else
{
deferred.reject('no match');
}
});
Either way is always a good idea to validate your endpoints before consuming them for recovery and error catching reasons.
Better update from github because the project seems to advance much quicker than it's bower registry.
Cheers and have fun.
i fixed that with a small hack in angularfire.js
line 336ish in the $on handler
336. callback();
change with
336. callback(self._snapshot);
line 587ish in the end of _wrapTimeout function add
587. //hack to preserve the snapshot from the timeout wipeouts
if ( evt === "loaded" ) {
self._snapshot = param;
}
I hope this will help you for now. I will try to find a proper solution for this.
Another think to bear in mind is that this
matchDB.$on('loaded', function(data) {
console.log(matchDB); // <--- notice what is going on here
if (matchMakingFormula(data.playerA, player)) {
if (!match) {
match = data;
deferred.resolve(match);
}
}
});
return's this
matchDB: Object
$add: function (item) {
$auth: function (token) {
$bind: function (scope, name) {
$child: function (key) {
$getIndex: function () {
$off: function (type, callback) {
$on: function (type, callback) {
$remove: function (key) {
$save: function (key) {
$set: function (newValue) {
$transaction: function (updateFn, applyLocally) {
playerA: Object // <---- notice this. it was the content of the object in firebase
__proto__: Object
Which is completely crazy...
It is actually merging the matchDB which is the DB reference of match with the object that I'm expecting from the firebase.
"13131314141"{
"playerA" : {
"location" : {
"lat" : 51.6021821,
"long" : "-02582276"
},
"type" : "monster"
}
}
You can actually make a solution out of this. but how you can take the result in the callback to use it as a promise deferred.resolve?
I can understand that they did this to be able to do
$scope.match = $matchDB.$on('loaded' , function(){});
but this doesn't serve my purpose that is decoupling firebase from my controllers and I don't really think it is actually a neat solution.
Please don't accept this as a solution because it is not a real one. You can hack your way to make it one but there is probably a better way to do it or at least the project is too young and soon a proper solution will be available.

Categories

Resources