I want to get a service to story values, that should be available at some controllers, taking regard of this post: AngularJS: How can I pass variables between controllers?
Everything fine. But: I want the service to be available at deeper funtions of controllers. Here is my example:
app.service('activeMovie', function() {
var movie = {
rating: 100
};
return {
getData: function() {
return movie;
},
setRating: function(value) {
movie.rating = value;
console.log("neues Rating" + movie.rating);
},
setData: function(pData) {
movie = pData;
}
};
});
and this are the controllers:
app.controller('MoviesController', function($http, $sce, activeMovie) {
var refresh = function(activeMovie) {
// ...
console.log("Test Service at refresh:" + activeMovie.getData().rating); // WORKS
};
refresh(activeMovie);
this.setActive = function(id, $activeMovie) {
$http.get('movies/'+id).success(function(response) {
// ...
});
console.log("Test Service at setActive:" + activeMovie.getData().rating); // WORKS only with the $-sign in the header
};
}
So it works how I did this.. If I dont use the $-sign in the second function, angularJS throws an error. Obviously I don't have to pass the parameter 'activeMovie' to the funtion when using the $-sign.. but why?
Related
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.
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");
};
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;
}])
I'm using a controller to load product data into an $rootScope array. I'm using $http service and works fine, but now I have a new function which fetch the number of products to be loaded. I can't use the function cause the response is slow.
I was wondering if I could use a provider to load the number of products to fetch in the config method before the apps start. And if I could move the $rootScope array to one service. I don't understand Angular docs, they are not really useful even the tutorial at least in providers and services...
app.controller('AppController', [ '$rootScope', '$http', function ( $rootScope,$http) {
$rootScope.empty = 0;
$rootScope.products = [];
$rootScope.lastId = 0;
$rootScope.getLastID = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.lastId = data.lastId;
});
};
$rootScope.getProducts = function () {
if ($rootScope.empty === 0) {
for (i = 1; i < 100; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
$rootScope.products.push(data);
});
}
}
$rootScope.empty.productos = 1;
};
}
I have done this with factory and service but is not working.
app.factory('lastProduct', ['$http', function lastProductFactory($http) {
this.lastId;
var getLast = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
lastId = data.lastId;
});
return lastId;
};
var lastProduct = getLast();
return lastProduct;
}]);
function productList($http, lastProduct) {
this.empty = 0;
this.lastId = lastProduct();
this.products = []
/*this.getLast = function () {
lastId = lastProduct();
};*/
this.getProducts = function () {
if (empty === 0) {
for (i = 1; i < lastId; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
products.push(data);
});
}
}
empty = 1;
return products;
};
}
app.service('productsList', ['$http', 'lastProduct' , ProductsList]);
services are not availables during configuration time, only providers hence you can not use $http to get a value inside the configuration block, but you can use the run block,
you can do
angular.module('app',['dependencies']).
config(function(){
//configs
})
.run(function(service){
service.gerValue()
})
setting the retrieved value inside a service or inside a value is a good idea to avoid contaminate the root scope, and this way the value gets retrieved before the services are instantiated and you can inject the retrieved value as a dependency
Making that many small $http requests does not seem like a good idea. But using a factory to store an array of data to be used across controllers would look something like this. To use a factory you need to return the exposed api. (The this style is used when using a service. I suggest googling the different but I prefer factories). And if you need to alert other controllers that data has changed you can use events.
angular
.module('myApp')
.factory('myData', myData);
function myData($http, $rootScope) {
var myArray = [], lastId;
return {
set: function(data) {
$http
.get('/path/to/data')
.then(function(newData) {
myArray = newData;
$rootScope.$broadcast('GOT_DATA', myArray);
})
},
get: function() {
return myArray
},
getLast: function() {
$http
.get('/path/to/data/last')
.then(function(last) {
lastId = last;
$rootScope.$broadcast('GOT_LAST', lastId);
})
}
}
}
And then from any controller you can inject the factory and get and set the data as you see fit.
angular
.module('myApp')
.controller('MainCtrl', MainCtrl);
function MainCtrl($scope, myData) {
$scope.bindableData = myData.get(); // get default data;
$scope.$on('GOT_DATA', function(event, data) {
$scope.bindableData = data;
})
}
I hope this helps. Let me know if you have any questions.
I done this but not working . rootScope total is undefined when set method is called from some controller.
http://imgur.com/qEl5WV5
But using 10 instead rootscope total
http://imgur.com/t6wB3JZ
I could see that the rootScope total var arrive before the others...
(app_dev.php/api/productos?op=ultimaIdProductos) vs (app_dev.php/api/producto/x)
var app = angular.module('webui', [$http, $rootScope]);
app.run (function ($http, $rootScope){
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.total = data.ultima;
});
});
function myData($http, $rootScope) {
var myArray = [];
return {
set: function () {
console.log($rootScope.total);
for (i = 1; i < $rootScope.total; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
myArray.push(data);
})
}
},
get: function () {
return myArray;
}
}
}
app.controller('AppController', ['$http', '$rootScope', 'myData', function ($http, $rootScope, myData) {
$rootScope.productos = [];
$rootScope.getProductos = function () {
console.log($rootScope.total);
myData.set();
$rootScope.productos = myData.get();
};
}]);
I've got a factory which looks a little bit like this:
app.factory('MyFactory',['$http',function($http){
return {
state: {
collectionOfThings: {}
},
getThings: function() {
$http.get(url).success(function(data) {
this.state.collectionOfThings = data;
});
}
};
}]);
All it provides is a method which gets a thing from a web service, and I want to store the answer in state.collectionOfThings. However, when the $http success method is called, this no longer refers to the factory and refers to window. I would have thought this was quite a straightforward thing to do, but I can't figure it out.
How do I populate the field in the factory with the results from the $http.get?
app.factory('MyFactory',['$http',function($http){
var obj = {};
obj.state = {
collectionOfThings: {}
};
obj.getThings = function() {
$http.get(url).success(function(data) {
obj.state.collectionOfThings = data;
});
}
return obj;
}]);