Simple service based on Observable with Angular 1.5 - angularjs

I would like to use Rxjs in my project which is based on Angular 1.5 and written in ECMA 5. I would like to rewrite my service methods which are based on promises and want implement Observables.
I write sth like this in my service:
function service(Restangular) {
var self = this;
self.user = new Rx.Subject();
var service = {
artistStream: artistStream,
};
function artistStream(artist) {
self.user.onNext({
data : Restangular
.all('text/')
.customGET('search', {q : artist})
});
}
return service;
}
And then I tried use this service in my component like this :
function testRx(artist) {
console.log(artistService.artistStream(artist));
}
I get error from console :
TypeError: self.user.onNext is not a function
What I'm doing wrong? It is my first time when I use Observables

Ok, rewrite service and work properly.
function artistStream(artist) {
const promise = Restangular
.all('text/')
.customGET('search', {q : artist})
const subscription = Rx.Observable.fromPromise(promise);
return subscription;
}

Related

Need to use $scope.apply() otherwise view sometimes slow

So i'm building a web application in AngularJS that connects to an API (Qlik Sense Engine API) with QSocks.
Qsocks is a lightweight wrapper around the Qlik Sense Engine API wrapper that is written in NodeJS but can also be imported in a web environment. QSocks contains and uses the NPM package Promise package so it uses it's own non AngularJS promises.
My service looks like this:
var app_promise = (appFactory.activeConnection() ? appFactory.activeConnection() : appFactory.app());
this.getData = function(qMeasures, time) {
ratioChild.qHyperCubeDef.qMeasures[0].qDef.qDef = qMeasures;
ratioChild.qHyperCubeDef.qMeasures[0].qDef.qLabel = qMeasures;
ratioChild.qHyperCubeDef.qDimensions[4].qDef.qFieldDefs = [time];
ratioChild.qHyperCubeDef.qDimensions[4].qDef.qFieldLabels = [time];
var deferred = $q.defer();
app_promise.then(function (obj) {
obj.createChild(ratioChild).then(function (childObj) {
deferred.resolve(childObj);
});
});
return deferred.promise;
}
In simple words, when i call this service in e.g. a controller. I get an object where i can build other objects on it.
Side Note:
I do need to make a new AngularJS promise because app_promise.then and obj.createChild(ratioChild).then are the NPM promise package promises.
This is how my controller looks like (first part):
if (!$rootScope.balanceSheetFixedObj) {
var fixYearsqMeasure = "Sum({<Jaar=>}Saldo)";
balanceSheetService.getData(fixYearsqMeasure, self.time).then(function (childObj) {
$rootScope.balanceSheetFixedObj = childObj;
return childObj;
}).then(handleFixData)
} else {
handleFixData();
}
This is how my controller looks like (second part):
function handleFixData(childObj) {
childObj = (childObj) ? childObj : $rootScope.balanceSheetFixedObj;
childObj.getLayout().then(function(data) {
self.data = data;
if (data.qHyperCube.qPivotDataPages[0].qData.length > 0) {
var fixPivotData = data.qHyperCube.qPivotDataPages[0];
self.labels = fixPivotData.qLeft;
$scope.$apply(); // Here is my problem!
With $scope.$apply() my view is publishes/updated after a second.
If i leave out the $scope.$apply() it do publish/update the view but after 10-15 Seconds.. Way to late! Why is my view so slow? I would like to leave out the $scope.$apply()
I manage to solve my own problem. After looking back it was quite obvious what my problem was.
Thanks to #charlietfl i've taken a look to the childObj.getLayout(). What i saw what that the getLayout() function returns a QSocks promise and the code that updates my view was written inside of the .then() of the QSocks promise. As getLayout() is not an angular promise, this was the problem. My view was not updated properly.
My solution was to create a service function that creates an Angular Promise
this.getObjLayout = function(childObj) {
var deferred = $q.defer();
childObj.getLayout().then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And in the controller i invoke that function
function handleFixData(childObj) {
childObj = (childObj) ? childObj : $rootScope.balanceSheetFixedObj;
balanceSheetService.getObjLayout(childObj).then(function (data) {
self.data = data;
if (data.qHyperCube.qPivotDataPages[0].qData.length > 0) {
var fixPivotData = data.qHyperCube.qPivotDataPages[0];
self.labels = fixPivotData.qLeft;
}
})
}

Wait for server data in order to initialize service

I want to create an angular service that when the app is loaded for the first time will fetch data from the server once and then I'll be able to query the data from the local service object. Here's how I tried to do it:
angular.module('my.services').factory('Properties', function ($http) {
var properties = $http.get("/properties");
return {
get: function (property) {
// Logic here
console.log(property);
}
}
});
Problem is - $http.get() is asynchronous and in order to achieve what I'm trying to do, I have to use it synchronously. Is there a way to do it with "native" AngularJS? Or I must use an XHR call for this?
You can try to do something like this
angular.module('my.services').factory('Properties', function ($http) {
var propertiesPromise = $http.get("/properties");
return {
get: function (property) {
propertiesPromise.then(function(properties){
console.log(properties);
});
}
}
});

How do I pass the variable in a function into my controller?

I'm trying to pass the videoUrl variable in the showResponse function into my controller. I've been trying to figure out a solution without success. Can anyone guide me in the right direction?
var myApp = angular.module('myApp', []);
myApp.controller('mainCtrl', ['$scope', function($scope){
$scope.videoUrl = videoUrl;
}])
// Helper function to display JavaScript value on HTML page.
function showResponse(response) {
var videoUrl = [];
for (prop in response.items) {
videoUrl[prop] = "https://www.youtube.com/embed/" + response.items[prop].snippet.resourceId.videoId;
}
}
// Called automatically when JavaScript client library is loaded.
function onClientLoad() {
gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
}
// Called automatically when YouTube API interface is loaded
function onYouTubeApiLoad() {
gapi.client.setApiKey('#######');
search();
}
function search() {
// Use the JavaScript client library to create a search.list() API call.
var request = gapi.client.youtube.playlistItems.list({
part: 'snippet',
playlistId: '########'
});
// Send the request to the API server,
// and invoke onSearchRepsonse() with the response.
request.execute(onSearchResponse);
}
// Called automatically with the response of the YouTube API request.
function onSearchResponse(response) {
showResponse(response);
}
It would probably better/easier if you could get this stuff into angular, so that it's all happening within services. That's how data sharing is supposed to happen in angular. But maybe that's challenging due to the nature of onClientLoad. The dirty way to do it is:
Get the controller's scope directly and set it on that scope. Assuming you've got something defined like:
<div ng-controller="mainCtrl"></div>
you can get that controller's scope using jQuery:
function showResponse(response) {
var videoUrl = [];
for (prop in response.items) {
videoUrl[prop] = "https://www.youtube.com/embed/" + response.items[prop].snippet.resourceId.videoId;
}
var scope = $('[ng-controller="mainCtrl"]').scope();
scope.videoUrl = videoUrl;
}
Note that this will cause angular purists to weep and gnash their teeth.

Can I pass a param from controller into service in AngularJS?

Let's assume I have some service and some controller. What that service will return depends of what the controller will pass into it. But is it possible indeed? I suspect a service may look like this:
var app = angular.module('tApp',[])
.provider('getFileContents', function(param){
this.paramId = param;
this.$get = function(){
var par = this.paramId;
return{
getContents:function(){
return "paramId comes here: "+par;
}
}
}
});
then I think my controller should look like this:
app.controller('test_controlController',
function test_controlController($scope,getFileContents){
$scope.contents = getFileContents.getContents('test_control');
console.dir($scope.contents);
});
...but it doesn't work. It says:
Uncaught Error: Unknown provider: param from tApp
So is it possible to make it working?
You are adding a parameter to the service constructor instead to the service function. and you are using a provider instead of a service or factory, you can get some information about the difference between services/factories and providers here:
Angular Service VS Provider VS Factory
Back to your code, make to following changes:
Service:
app.service('FileService', function () {
return {
getFileContents : function (fileID) {
//function logic goes here
console.log(fileID);
}
}
});
Controller:
app.controller('TestController', function ($scope,getFileContents) {
$scope.contents = getFileContents.getFileContents(123);
});
Add a parameter for your getContents method in your service
return{
getContents:function(foo){
return "paramId comes here: "+ foo;
}
}

Firebase's AngularFire in an AngularJS service

The best way of handling Firebase in AngularJS surely has to be from within a service, so it's available to all Controllers across the App.
I just can't get it to work! ... I first tried using angularFire(new Firebase(url)), hoping I could bind to the service's scope, but Angular complains that it cannot $watch it.
So I tried angularFireCollection instead like this:
app.factory('myService', function myService(angularFireCollection) {
var url = 'https://myfirebase.firebaseio.com';
return {
getAll: function(path) {
var ref = angularFireCollection(new Firebase(url + '/' + path));
console.log(ref);
return ref;
},
...
};
});
However, the angularFireCollection is an Object containing a load of methods etc. if I bind it to a controller $scope I just get garbage. It also complains that it can't call the Firebase functions before I try to use them (e.g. Error: Firebase.push failed: second argument must be a valid function.)... anyone got any ideas where I'm going wrong?
See this PLUNKER
If you want to encapsulate some of the functionality into a service, consider keeping the returned ref in state of the service. I expanded on your plunker. It seems to mostly do what you were trying for.
http://plnkr.co/edit/Uf2fB0
Jeff answered the question correctly ... I'm just posting a further development on Jeff's example for those who are interested.
I have abstracted the Firebase service creation, so you can dynamically create an instance of whatever Firebase service you want:-
var registerFirebaseService = function (serviceName) {
app.factory(serviceName, function (angularFire) {
var _url = null;
var _ref = null;
return {
init: function (url) {
_url = url;
_ref = new Firebase(_url);
},
setToScope: function (scope, localScopeVarName) {
angularFire(_ref, scope, localScopeVarName);
}
};
});
};
You first create an instance of the service as follows
registerFirebaseService('itemsService'); // create itemsService instance
Then you can inject the itemsService service into your controllers. The instance is initialised using your Firebase URL e.g.
itemsService.init('https://firebase.firebaseio.com/' + userId + '/items');
The Firebase can now be bound to your controller e.g.
itemsService.setToScope($scope, 'items');
adapted PLUNKER

Resources