AngularJS : get object by id from factory - angularjs

I have a factory to get an array with all my clientes from the database.
Then i need to filter this array by the person id and show only it's data in a single page.
I have a working code already, but it's only inside a controller and I want to use it with a factory and a directive since i'm no longer using ng-controller and this factory already make a call to other pages where I need to show client data.
This is what i tried to do with my factory:
app.js
app.factory('mainFactory', function($http){
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente")
.success( function(data) {
return data;
})
.error(function(data) {
});
};
var getDetCliente = function($scope,$routeParams) {
getCliente();
var mycli = data;
var myid = $routeParams.id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
};
return {
getCliente: getCliente,
getDetCliente: getDetCliente
}
});
app.directive('detClienteTable', function (mainFactory) {
return {
restrict: "A",
templateUrl: "content/view/det_cliente_table.html",
link: function($scope) {
mainFactory.getDetCliente().then(function(mycli) {
$scope.pagedCliente = mycli;
})
}
}
});
detClient.html
<p>{{pagedCliente.name}}</p>
<p>{{pagedCliente.tel}}</p>
<p>{{pagedCliente.email}}</p>
[...more code...]
The problem is, I'm not able to get any data to show in the page, and also, i have no errors in my console.
What may be wrong?
Keep in mind I'm learning AngularJS.

Basically you need to implement a promise chain as look into your code looks like you are carrying getCliente() promise to getDetCliente method. In that case you need to use .then function instead of using .success & .error which doesn't allow you to continue promise chain. There after from getDetCliente function you again need to use .then function that gets call when getCliente function gets resolved his promise. Your code will reformat it using form it and return mycli result.
Code
var getCliente = function() {
return $http.get("scripts/php/db.php?action=get_cliente")
.then( function(res) { //success callback
return res.data;
},function(err){ //error callback
console.log(err)
})
};
var getDetCliente = function(id) {
return getCliente().then(function(data){
var mycli = data;
var myid = id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
})
};
Edit
You shouldn't pass controller $scope to the service that will make tight coupling with you directive and controller, Also you want to pass id parameter of your route then you need to pass it from directive service call
link: function($scope) {
mainFactory.getDetCliente($routeParams.id).then(function(mycli) {
$scope.pagedCliente = mycli;
})
}

You are treating getCliente as a synchronous call in getDetCliente. Interestingly in your directive you understand that the getDetCliente is asynchronous. Change getCliente to this and treat it as an asynchronous call when you call it in getDetCliente:
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente");
};

Related

How to update angularjs.module.value with array and retrieving it through service?

I'm trying to update a global angularjs module.value in one controller with an array, and then retrieving that global array through in a service. But the array doesn't exist.
app.js
app.factory('featureClaims', function($q) {
var featureClaims = {};
featureClaims.init = function() {
featureClaims.claims = [];
}
featureClaims.get = function() {
return $q.when(featureClaims.claims);
}
featureClaims.set = function(data) {
featureClaims.claims = data;
return $q.when(featureClaims.claims); // I'm using the $q library to return a promise.
}
return featureClaims;
});
loginController
let loginController = function($scope, loginService, toastrObj, featureClaims) {
$scope.login = function(){
featureClaims.init();
featureClaims.set(result.data.FeatureClaims); // updating ok here
}
}
app.controller("loginController", ["$scope", 'loginService', 'toastrObj', 'featureClaims',loginController]);
home service
let homeService= function(featureClaims) { // featureClaims.claims is null
return{
validateUser: function(expectedClaim) {
if(expectedClaim !== ""){
featureClaims.get().then(function(data){
return data.includes(expectedClaim); // data is return as undefined
})
}
return false;
}
}
};
app.factory('homeService',['featureClaims', homeService]);
I don't think you can use a value service this way. From this post the author states: "Note: Make sure that you never overwrite the value service/object as a whole otherwise your assignment is lost. Always reassign the property values of the value object. The following assignment is wrong and does not lead to the expected behavior"
Instead why don't you convert your value to a factory like so:
app.factory('featureClaims', function($q) {
var featureClaims = {};
featureClaims.init = function() {
featureClaims.claims = [];
}
featureClaims.get = function() {
return $q.when(featureClaims.claims);
}
featureClaims.set = function(data) {
featureClaims.claims = data;
return $q.when(featureClaims.claims); // I'm using the $q library to return a promise.
}
return featureClaims;
});
In your controller:
featureClaims.init();
// you need to wait for the promise to resolve with 'then'
featureClaims.set(['foo', 'bar']).then(function(response) {
console.log(response); // logs ["foo", "bar"]
});
featureClaims.get().then(function(response) {
console.log(response); // logs ["foo", "bar"]
});
Tested and working. You will want to create a get method that simply returns the data instead of setting it first.

Passing JSON value from one Controller to another

I am trying to pass a JSON string value that is stored in one controller to another. I am using a custom service to pass the data, but it doesn't seem to be passing the value.
The First controller where the JSON string value is stored:
filmApp.controller('SearchController',['$scope', '$http','$log','sharedService',function($scope,$http,$log,sharedService){
$scope.results = {
values: []
};
var vm = this;
vm.querySearch = querySearch;
vm.searchTextChange = searchTextChange;
vm.selectedItemChange = selectedItemChange;
function querySearch(query) {
return $http.get('https://api.themoviedb.org/3/search/movie?include_adult=false&page=1&primary_release_year=2017', {
params: {
'query': query,
'api_key': apiKey
}
}).then(function(response) {
var data = response.data.results.filter(function(obj) {
return obj.original_title.toLowerCase().indexOf(query) != -1;
})
return data;
for (var i = 0; i < data.results.length; i++) {
$scope.results.values.push({title: data.results[i].original_title});
// $log.info($scope.results.values);
}
return $scope.results.values;
})
};
function searchTextChange(text) {
// $log.info('Search Text changed to ' + text);
}
function selectedItemChange(item) {
$scope.value = JSON.stringify(item);
return sharedService.data($scope.value);
}
}]);
The custom Angular service - The value is received here:
filmApp.service('sharedService',function($log){
vm = this;
var value = [];
vm.data = function(value){
$log.info("getValue: " + value); // received value in log
return value;
}
});
The Second controller that wants to receive the JSON value from the First controller:
filmApp.controller('singleFilmController',['$scope', '$http','$log','sharedService',function($scope,$http,$log,sharedService){
var value = sharedService.data(value);
$log.info("Data: " + value);
}]);
The value is received in the service but the second controller can't seem to access it. Not sure why it is happening as I'm returning the value from the data() method from the service. Also, the selectedItemChange method is used by the md-autocomplete directive.
A good approach would be using a Factory/Service. take a look at this: Share data between AngularJS controllers
Technically, you can resolve this by simply changing your service definition to
(function () {
'use strict';
SharedService.$inject = ['$log'];
function SharedService($log) {
var service = this;
var value = [];
service.data = function (value) {
$log.info("getValue: " + value); // received value in log
service.value = value;
return service.value;
};
});
filmApp.service('SharedService', SharedService);
}());
But it is a very poor practice to inject $http directly into your controllers. Instead, you should have a search service that performs the queries and handle the caching of results in that service.
Here is what that would like
(function () {
'use strict';
search.$inject = ['$q', '$http'];
function search($q, $http) {
var cachedSearches = {};
var lastSearch;
return {
getLastSearch: function() {
return lastSearch;
},
get: function (query) {
var normalizedQuery = query && query.toLowerCase();
if (cachedSearches[normalizedQuery]) {
lastSearch = cachedSearches[normalizedQuery];
return $q.when(lastSearch);
}
return $http.get('https://api.themoviedb.org/3/search/movie?' +
'include_adult=false&page=1&primary_release_year=2017', {
params: {
query: query,
api_key: apiKey
}
}).then(function (response) {
var results = response.data.results.filter(function (result) {
return result.original_title.toLowerCase().indexOf(normalizedQuery) !== -1;
}).map(function (result) {
return result.original_title;
});
cachedSearches[normalizedQuery] = results;
lastSearch = results;
return results;
}
});
}
}
filmApp.factory('search', search);
SomeController.$inject = ['$scope', 'search'];
function SomeController($scope, search) {
$scope.results = [];
$scope.selectedItemChange = function (item) {
$scope.value = JSON.stringify(item);
return search.get($scope.value).then(function (results) {
$scope.results = results;
});
}
}
filmApp.controller('SomeController', SomeController);
}());
It is worth noting that a fully fledged solution would likely work a little differently. Namely it would likely incorporate ui-router making use of resolves to load the details based on the selected list item or, it could equally well be a hierarchy of element directives employing databinding to share a common object (nothing wrong with leveraging two-way-binding here).
It is also worth noting that if I were using a transpiler, such as TypeScript or Babel, the example code above would be much more succinct and readable.
I'm pretty new to AngularJS but I'm pretty sure all you are doing is two distinct function calls. The first controller passes in the proper value, the second one then overwrites that with either null or undefined
I usually use events that I manually fire and catch on my controllers. To fire to sibling controllers, use $rootScope.$emit()
In the other controller then, you would catch this event using a $rootscope.$on() call
This article helped me a decent amount when I was working on this in a project.

Loading data from angular service on startup

UPDATE
I am currently doing this, and I'm not sure why it works, but I don't think this is the correct approach. I might be abusing digest cycles, whatever those are.
First, I want to have the array navigationList be inside a service so I can pass it anywhere. That service will also update that array.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigationList = [];
var getNavigation = function() {
ExtService.getUrl('navigation.json').then(function(data) {
angular.copy(data.navigationList, navigationList);
});
}
return{
getNavigation: getNavigation,
navigationList: navigationList,
}
}]);
Then in my controller, I call the service to update the array, then I point the scope variable to it.
ChapterService.getNavigation();
$scope.navigationList = ChapterService.navigationList;
console.log($scope.navigationList);
But this is where it gets weird. console.log returns an empty array [], BUT I have an ng-repeat in my html that uses $scope.navigationList, and it's correctly displaying the items in that array... I think this has something to do with digest cycles, but I'm not sure. Could anyone explain it to me and tell me if I'm approaching this the wrong way?
I have a main factory that runs functions and calculations. I am trying to run
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
return: {
navigation: navigation
}
I did a console.log on the data before it gets returned and it's the correct data, but for some reason, I can't return it..
The ExtService that has the getUrl method is just the one that's typically used (it returns a promise)
In my controller, I want to do something like
$scope.navigation = ChapterService.navigation.getNavigationData();
in order to load the data from the file when the app initializes,
but that doesn't work and when I run
console.log(ChapterService.navigation.getNavigationData());
I get null, but I don't know why. Should I use app.run() for something like this? I need this data to be loaded before anything else is done and I don't think I'm using the best approach...
EDIT
I'm not sure if I should do something similar to what's being done in this jsfiddle, the pattern is unfamiliar to me, so I'm not sure how to re purpose it for my needs
My code for ExtService is
app.factory('ExtService', function($http, $q, $compile) {
return {
getUrl: function(url) {
var newurl = url + "?nocache=" + (new Date()).getTime();
var deferred = $q.defer();
$http.get(newurl, {cache: false})
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
});
EDIT 2
I'd like to separate the request logic away from the controller, but at the same time, have it done when the app starts. I'd like the service function to just return the data, so I don't have to do further .then or .success on it...
You are using promises incorrectly. Think about what this means:
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
getNavigationData is a function that doesn't return anything. When you're in the "then" clause, you're in a different function so return data only returns from the inner function. In fact, .then(function(data) { return data; }) is a no-op.
The important thing to understand about promises is that once you're in the promise paradigm, it's difficult to get out of it - your best bet is to stay inside it.
So first, return a promise from your function:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json');
}
}
return {
navigation: navigation
}
}])
Then use that promise in your controller:
app.controller('MyController', function($scope, ExtService) {
ExtService
.navigation
.getNavigationData()
.then(function(data) {
$scope.navigation = data;
});
})
Update
If you really want to avoid the promise paradigm, try the following, although I recommend thoroughly understanding the implications of this approach before doing so. The object you return from the service isn't immediately populated but once the call returns, Angular will complete a digest cycle and any scope bindings will be refreshed.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
// create an object to return from the function
var returnData = { };
// set the properties of the object when the call has returned
ExtService.getUrl('navigation.json')
.then(function(x) { returnData.nav = x });
// return the object - NB at this point in the function,
// the .nav property has not been set
return returnData;
}
}
return {
navigation: navigation
}
}])
app.controller('MyController', function($scope, ExtService) {
// assign $scope.navigation to the object returned
// from the function - NB at this point the .nav property
// has not been set, your bindings will need to refer to
// $scope.navigation.nav
$scope.navigation = ExtService
.navigation
.getNavigationData();
})
You are using a promise, so return that promise and use the resolve (.then) in the controller:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json'); // returns a promise
});
}
return: {
navigation: navigation
}
}
controller:
ChapterService
.navigation
.getNavigationData()
.then(function (data) {
// success
$scope.navigation = data;
}, function (response) {
// fail
});
This is a different approach, I don't know what your data looks like so I am not able to test it for you.
Controller
.controller('homeCtrl', function($scope, $routeParams, ChapterService) {
ChapterService.getNavigationData();
})
Factory
.factory('ChapterService', [ 'ExtService', function(ExtService) {
function makeRequest(response) {
return ExtService.getUrl('navigation.json')
}
function parseResponse(response) {
retries = 0;
if (!response) {
return false;
}
return response.data;
}
var navigation = {
getNavigationData: function () {
return makeRequest()
.then(parseResponse)
.catch(function(err){
console.log(err);
});
}
}
return navigation;
}])

Angular: Rewriting function to use promise

I'm using an Angular factory that retrieves data from a feed and does some data manipulation on it.
I'd like to block my app from rendering the first view until this data preparation is done. My understanding is that I need to use promises for this, and then in a controller use .then to call functions that can be run as soon as the promise resolves.
From looking at examples I'm finding it very difficult to implement a promise in my factory. Specifically I'm not sure where to put the defers and resolves. Could anyone weigh in on what would be the best way to implement one?
Here is my working factory without promise:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $state, StorageHandler) {
var obj = {
InitData : function() {
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
},
};
return obj;
});
Here's what I tried from looking at examples:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $q, $state, StorageHandler) {
var obj = {
InitData : function() {
var d = $q.defer(); // Set defer
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
return d.promise; // Return promise
},
};
return obj;
});
But nothing is shown in console when I use this in my controller:
DataHandler.InitData()
.then(function () {
// Successful
console.log('success');
},
function () {
// failure
console.log('failure');
})
.then(function () {
// Like a Finally Clause
console.log('done');
});
Any thoughts?
Like Florian mentioned. Your asynchronous call is not obvious in the code you've shown.
Here is the gist of what you want:
angular.module("myApp",[]).factory("myFactory",function($http,$q){
return {
//$http.get returns a promise.
//which is latched onto and chained in the controller
initData: function(){
return $http.get("myurl").then(function(response){
var data = response.data;
//Do All your things...
return data;
},function(err){
//do stuff with the error..
return $q.reject(err);
//OR throw err;
//as mentioned below returning a new rejected promise is a slight anti-pattern,
//However, a practical use case could be that it would suppress logging,
//and allow specific throw/logging control where the service is implemented (controller)
});
}
}
}).controller("myCtrl",function(myFactory,$scope){
myFactory.initData().then(function(data){
$scope.myData = data;
},function(err){
//error loudly
$scope.error = err.message
})['finally'](function(){
//done.
});
});

Calling service for factory in controller

I have a problem when calling a service created using .factory in my controller.
The code looks like the following.
Factory (app.js):
.factory('Database',function($http){
return {
getDatabase: function(){
var database = {};
$http.get('http://localhost:3001/lookup').
success(function(data){
database.companyInfo = data.info.companyInfo;
});
}).
error(function(data){
console.log('Error' + data);
});
return database;
}
};
})
Controller:
angular.module('webClientApp')
.controller('MainCtrl', function (Database,Features,$scope,$http) {
$scope.databaseString = [];
$scope.quarters = ['Q1','Q2','Q3','Q4'];
$scope.years = ['2004','2005','2006','2007','2008','2009','2010',
'2011','2012','2013','2014'];
$scope.features = Features.getFeatures();
$scope.database = Database.getDatabase();
console.log($scope.database);
Now when I inspect the element in Firebug I get the console.log($scope.database) printed out before the GET statement result. $scope.database is shown as an Object {} with all the proprieties in place.
However if I try to use console.log($scope.database.companyInfo) I get an undefined as result, while instead I should get that data.info.companyInfo' that I passed from theDatabase` service (in this case an array).
What is the problem here? Can someone help me?
(If you need clarifications please let me know..)
The $http.get() call is asynchronous and makes use of promise objects. So, based on the code you provided it seems most likely that you are outputting the $scope.database before the success method is run in the service.
I build all my service methods to pass in a success or failure function. This would be the service:
.factory('Database',function($http){
return {
getDatabase: function(onSuccuess,onFailure){
var database = {};
$http.get('http://localhost:3001/lookup').
success(onSuccess).
error(onFailure);
}
};
})
This would be the controller code:
angular.module('webClientApp')
.controller('MainCtrl', function (Database,Features,$scope,$http) {
$scope.databaseString = [];
$scope.quarters = ['Q1','Q2','Q3','Q4'];
$scope.years = ['2004','2005','2006','2007','2008','2009','2010',
'2011','2012','2013','2014'];
$scope.features = Features.getFeatures();
Database.getDatabase(successFunction,failureFunction);
successFunction = function(data){
$scope.database = data.info.companyInfo;
console.log($scope.database);
});
failureFunction = function(data){
console.log('Error' + data);
}
Change your code in the following way:
.factory('Database',function($http){
return {
getDatabase: function(){
return $http.get('http://localhost:3001/lookup');
}
};
})
Then get the response in controller(Promise chain)
Database.getDatabase()
.then(function(data){
//assign value
})
.catch(function(){
})

Resources