I'm trying to setup my angular controllers in the format of closures. But Im having issues with asynchronous behavior. Basically whats returned is an undefined value on the controller.
myApp.controller(['RoleController', RoleController]);
var RoleController = function (MockRest, $scope) {
var roleValues = [];
var setRoleValues = (function () {
return MockRest.getRoles().then(function(data) {
var formattedData = data.plain();
return Immutable.fromJS(formattedData);
});
})();
return {
displayRowCollection: setRoleValues,
roleValues: roleValues,
}
};
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.
Using Angular 1.5.9 on frontend and WebAPI 2 on server. Calling a standard $http.get in the service to Get() method on controller. This is returning the ViewModel that I want to populate variables with in angular controller.
var carListController = function ($http, $scope, carService) {
var model = this;
model.carList = carService.getCarsByMake('Bentley', 10);
console.log(model.carList);
model.cars = model.carList[0].cars;
model.totalCars = model.carList[0].totalCars;
model.numberOfPages = model.carList[0].numberOfPages;
};
I get this error:
Cannot read property 'cars' of undefined
As you can see the console.log is showing the model.carList so I know issue is in controller code populating the other variables. What am I missing here? Any help appeciated.
Edit: carService
var cars = [];
var carService = function ($http) {
var getCarsByMake = function (make, size) {
$http.get('http://localhost:50604/api/cars?make=' + make + '&size=' + size)
.then(function (response) {
// Success
angular.copy(response.data, cars);
}, function () {
// Failure
});
return cars;
};
return {
getCarsByMake: getCarsByMake
};
};
You have to wrap your $scope variable population in a promise approach.
Since the model.carList data is not yet loaded when the population is happening, it's normal that the error arrises (Cannot read property 'cars' of undefined; meaning carList is undefined).
In your service carService.getCarsByMake you have to return a promise( $http.get method)
Only when the promise is resolved, you can populate your $scopevariables with this data.
var carListController = function ($http, $scope, carService) {
var model = this;
carService.getCarsByMake('Bentley', 10).then(function(response){
model.carList = response.data;
model.cars = model.carList.cars;
model.totalCars = model.carList.totalCars;
model.numberOfPages = model.carList.numberOfPages;
});
};
Return $http request on the service side :
var cars = [];
var carService = function ($http) {
var getCarsByMake = function (make, size) {
return $http.get('http://localhost:50604/api/cars?make=' + make + '&size=' + size);
};
return {
getCarsByMake: getCarsByMake
};
};
I want to swap an array and get a json file, but don't know why nor where there's something wrong in my code (the service/controller part without an http request works though).
incriminated code
(function() {
(function() {
var JsonsService;
JsonsService = function($http) {
var pizze;
pizze = [];
return {
getPizze: function() {
$http.get('data/pizze-it.json').then(function(pizze) {
pizze = pizze.data;
});
}
};
};
JsonsService.$inject = ['$http'];
angular.module('myApp').factory('JsonsService', JsonsService);
})();
}).call(this);
(function() {
(function() {
var JsonsCtrl;
JsonsCtrl = function(JsonsService) {
var self;
self = this;
self.list = function() {
return JsonsService.getPizze();
};
};
JsonsCtrl.$inject = ['JsonsService'];
angular.module('myApp').controller('JsonsCtrl', JsonsCtrl);
})();
}).call(this);
Plnkr
I removed from app.js the entire block of code that is causing this error (service and controller), and placed it inside DontLoadThis.js (there's some markup to put back into main.html too)
This isn't necessarily the definite answer but there's a few things I've noticed that appear wrong.
Starting with your JsonsService:
JsonsService = function($http) {
var pizze;
pizze = [];
return {
getPizze: function() {
$http.get('data/pizze-it.json').then(function(pizze) {
pizze = pizze.data;
});
}
};
};
You're initialising a variable pizze but also using the callback variable pizze in the $http.get(). Instead I suggest:
var pizze = [];
...
$http.get('data/pizze-it.json').then(function(json_response) {
pizze = json_response.data;
});
This however is made redundant by the second issue: JsonsService.getPizze() doesn't actually return anything. A possible way around this would be to return the promise from getPizze() and deal with the result in the controller.
// in service
return {
getPizze: function() {
return $http.get('data/pizze-it.json');
}
};
// in controller
JsonsCtrl = function(JsonsService) {
var self;
self = this;
self.list = [];
JsonsService.getPizze().then(function (json_response) {
self.list = json_response.data;
});
};
This question already has answers here:
Share data between AngularJS controllers
(11 answers)
Closed 2 years ago.
i have tow controller in angularjs. if one controller change data other controller display updated data. in fact first controller has a event that it occur second controller display it. for this propose i wrote a service. this service has tow function. here is my service code.
app.service('sharedData', function ($http) {
var data=[]
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
})
},
getData: function(){
return data;
}
}
});
in first controller
app.controller("FirstController", function ($scope, $http,sharedData)
{
$scope.handleGesture = function ($event)
{
sharedData.setData();
};
});
in second controller:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
data = sharedData.getData();
}
);
in first controller setData work with out any problem but in second controller not work correctly. how to share data dynamically between tow controllers?
You are on the right track with trying to share data between controllers but you are missing some key points. The problem is that SecondController gets loaded when the app runs so it calls sharedData.getData() even though the call to setData in the firstController does not happen yet. Therefore, you will always get an empty array when you call sharedData.getData().To solve this, you must use promises which tells you when the service has data available to you. Modify your service like below:
app.service('sharedData', function ($http, $q) {
var data=[];
var deferred = $q.defer();
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
deferred.resolve(response);
})
},
init: function(){
return deferred.promise;
},
data: data
}
})
And the secondController like this:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
sharedData.init().then(function() {
data = sharedData.data;
});
});
For more info on promises, https://docs.angularjs.org/api/ng/service/$q
You had multiple syntax problems, like service name is SharedData and you using it as SharedDataRange, the service is getting returned before the get function.
What I have done is corrected all the syntax errors and compiled into a plunkr for you to have a look. Just look at the console and I am getting the data array which was set earlier in the setter.
Javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", function ($scope,sharedDateRange)
{
sharedDateRange.setData();
});
app.controller("SecondController", function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
});
app.service('sharedDateRange', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
Working Example
If you want to keep sharedDataRange as the variable name and service name as sharedData have a look at this example
javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", ['$scope','sharedData', function ($scope,sharedDateRange)
{
sharedDateRange.setData();
}]);
app.controller("SecondController", ['$scope','sharedData', function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
}]);
app.service('sharedData', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
You can bind the data object on the service to your second controller.
app.service('sharedData', function ($http) {
var ret = {
data: [],
setData: function () {
$http.get('/getData').success(function(response){
data = response;
});
}
};
return ret;
});
app.controller("FirstController", function ($scope, sharedData) {
$scope.handleGesture = function () {
sharedData.setData();
};
});
app.controller("SecondController", function ($scope, sharedData) {
$scope.data = sharedData.data;
});
What you need is a singleton. The service sharedData needs to be a single instance preferably a static object having a static data member. That way you can share the data between different controllers. Here is the modified version
var app = angular.module('app', []);
app.factory('sharedData', function ($http) {
var sharedData = function()
{
this.data = [];
}
sharedData.setData = function()
{
//$http.get('/getData').success(function(response){
this.data = "dummy";
//})
}
sharedData.getData = function()
{
return this.data;
}
return sharedData;
})
.controller("FirstController", function ($scope, $http,sharedData)
{
sharedData.setData();
})
.controller("SecondController", function ($scope,sharedData) {
$scope.data=sharedData.getData();
});
I have removed the event for testing and removed the $http get for now. You can check out this link for a working demo:
http://jsfiddle.net/p8zzuju9/
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();
};
}]);