I'm doing a app for Ionic. Based on answers in a question made by me at $http.get not working at .factory, I wrote the following code:
services.js
angular.module('starter.services', [])
.factory('Chats', function($http) {
// Might use a resource here that returns a JSON array
var factory = {
chats: null,
all: function() { return chats; },
get: function(chatId) {
for (var i = 0; i < chats.length; i++) {
if (chats[i].id === parseInt(chatId)) {
return chats[i];
}
}
return null;
}
};
$http.get("http://lucassmuller.com/work/projetoblog/api.php?action=posts").then(function(data) {
factory.chats = data;
console.log('data ok');
});
return factory;
});
controllers.js
angular.module('starter.controllers', [])
.controller('DashCtrl', function($scope) {})
.controller('ChatsCtrl', function($scope, Chats) {
// With the new view caching in Ionic, Controllers are only called
// when they are recreated or on app start, instead of every page change.
// To listen for when this page is active (for example, to refresh data),
// listen for the $ionicView.enter event:
//
//$scope.$on('$ionicView.enter', function(e) {
//});
$scope.chats = Chats.all();
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
.controller('AccountCtrl', function($scope) {
$scope.settings = {
enableFriends: true
};
});
But when I run it to do a ng-repeat with data, shows an error saying that chats is not defined. How can I fix it?
The request that you make to any HTTP resource won't be resolved right away. It returns a promise object which is literally a promise saying that Hey, I don't know the result right now, but I will give you a result later regardless it succeeds or not. The promise object is resolved in the controller using its .then property which takes two functions as parameters, onSuccess and onFailure.
In your case, this is how you do it.
Factory/Service
.factory('Chats', function($http) {
// returning a promise from the service
var chats = $http.get("http://lucassmuller.com/work/projetoblog/api.php?action=posts");
var factory = {
chats: null,
all: function() { return chats; },
get: function(chatId) {
for (var i = 0; i < chats.length; i++) {
if (chats[i].id === parseInt(chatId)) {
return chats[i];
}
}
return null;
}
};
return factory;
});
Controller
.controller('ChatsCtrl', function($scope, Chats) {
// resolving the promise in the controller
Chats.all().then(function (res){ // onSuccess, called when response is successfully recieved
var chats = res.data
console.log(chats);
$scope.chats = chats;
}, function (err){ // onFailure, called when error response
console.log(err);
});
})
Check Angular Documentation for $http promises.
Related
In angularJS, With one call, when get the service response need access that json value in multiple controllers but in same page
I have two controller js file and both controllers are called in the same page when I called the service "this.getNavigationMenuDetails" in the first controller.js and as well as called in the controller2.js file as well. without timeout function, I want to access that same response which I get it from the "this.getNavigationMenuDetails" service in controller2.js. But it happened that service call twice in the page. I don't want to call the same service twice in a page.
When js are loading that time both controllers are called in the same layer then getting the response from the service so on the second controller2.js file code is not execute after the response. How can I solve this issue so that only one call i can get the response and access this response in controller2.js also.
controler1.js
var app = angular.module("navApp", []);
app.controller("navCtrl", ['$scope', 'topNavService', '$window', function ($scope, $timeout, topNavService, $window) {
$scope.menuItemInfo = {};
/*Navigation Menu new Code */
$scope.getNavigationDetails = function () {
topNavService.getNavigationMenuDetails().then(function (result) {
$scope.menuItemInfo = result;
angular.forEach($scope.menuItemInfo.items, function (val, key) {
if (val.menuTitle ===
$window.sessionStorage.getItem('selectedNavMenu')) {
if ($scope.menuItemInfo.items[key].isEnabled) {
$scope.menuItemInfo.items[key].isActive = 'highlighted';
} else {
$window.sessionStorage.removeItem('selectedNavMenu');
}
}
if (val.menuTitle === 'Find a Fair' && !val.hasSubMenu) {
$scope.menuItemInfo.items[key].redirectTo = appConfig.findafairpageurl;
}
});
});
};
$scope.init = function () {
if ($window.location.pathname.indexOf('all-my-fairs.html') > 0) {
if (angular.isDefined($cookies.get('cpt_bookfair'))) {
$cookies.remove('cpt_bookfair', {
path: '/'
});
}
}
$scope.getNavigationDetails();
$scope.callOnLoad();
};
$scope.init();
}]);
app.service('topNavService', ['$http', '$timeout', '$q'function ($http, $timeout, $q) {
var menuInfo;
this.getNavigationMenuDetails = function () {
if (!menuInfo) {
// If menu is undefined or null populate it from the backend
return $http.get("/etc/designs/scholastic/bookfairs/jcr:content/page/header-ipar/header/c-bar.getMenuDetails.html?id=" + Math.random()).then(function (response) {
menuInfo = response.data;
return menuInfo;
});
} else {
// Otherwise return the cached version
return $q.when(menuInfo);
}
}
}]);
Controller2.js
var app = angular.module('bookResourcePage', []);
app.controller('bookResourceCtrl', ['topNavService', '$scope', function (topNavService, $scope) {
$scope.topInfo = '';
topNavService.getNavigationMenuDetails.then(function success() {
$scope.productId = $scope.topInfo.isLoggedin;
$scope.linkParam = '?productId=' + $scope.productId;
}, function failure() {
console.error("something bad happened");
});
}]);
The service would work better if it cached the HTTP promise instead of the value:
app.service('topNavService', function ($http) {
var menuInfoPromise;
this.getNavigationMenuDetails = function () {
if (!menuInfoPromise) {
// If menu is undefined or null populate it from the backend
menuInfoPromise = $http.get(url);
};
return menuInfoPromise;
};
});
The erroneous approach of caching the value introduces a race condition. If the second controller calls before the data arrives from the server, a service sends a second XHR for the data.
You can do this with following approach.
Service.js
app.service('topNavService', function($http) {
var menuInfoPromise;
var observerList = [];
var inProgress = false;
this.addObserver(callback) {
if (callback) observerList.push(callback);
}
this.notifyObserver() {
observerList.forEach(callback) {
callback();
}
}
this.getNavigationMenuDetails = function() {
if (!menuInfoPromise && !inProgress) {
inProgress = true;
// If menu is undefined or null populate it from the backend
menuInfoPromise = $http.get(url);
this.notifyObserver();
};
return menuInfoPromise;
};
});
You have to make a function in service to add your controller's function on list. then each controller will register their get function on service and call service method to get data. first call will make service variable inProgress to true. so it will prevent for multiple server request. then when data available to service then it will call its notifyObserver function to message for all controller by calling their function.
Controller 1
app.controller('ctrl1', ['service', '$scope', function(service, $scope) {
service.addObserver('getData1'); //name of your controller function
$scope.getData1() {
service.getNavigationMenuDetails.then(function success() {
$scope.productId = $scope.topInfo.isLoggedin;
$scope.linkParam = '?productId=' + $scope.productId;
}, function failure() {
console.error("something bad happened");
});
}
$scope.getData1()
}]);
Controller 2
app.controller('ctrl1', ['service', '$scope', function(service, $scope) {
service.addObserver('getData2'); //name of your controller function
$scope.getData2() {
service.getNavigationMenuDetails.then(function success() {
$scope.productId = $scope.topInfo.isLoggedin;
$scope.linkParam = '?productId=' + $scope.productId;
}, function failure() {
console.error("something bad happened");
});
}
$scope.getData2()
}]);
with this approach you can real time update data to different controllers without have multiple same request to server.
This seems so simple yet I have been banging my head against it for hours now...
I have an angular 1.2.29 module that uses ui-router to resolve a data dependency from a service and inject it into a named nested view controller. The service returns my data object exactly as desired and I can inject that returned data into my view controller and log it again with no issue.
What I am baffled by is that if I inspect the object in the console, I can see the array I am trying to access on one of the nested properties of the object I injected from the service (this is from within the context of the controller I am trying to use it in).
However, if I try and pass that array of data into another method in the same controller (in this case a d3 method to draw a graph) - I get nothing but a empty string where the array is.
I can access every other property on the object I passed into the method just fine, and so long as I don't try and access the array directly it is 'visible' to me in my logging, but when I try and pass the array directly in I get an empty string.
I have tried everything I can from making a deep copy or pushing the array into a new object and accessing it that way, trying to cast that property as an array (again into a new object), and a whole host of other random hacks I have run across all to no avail.
I feel like I am missing something incredibly trivial here...
These three logs are called on the same object from within the same method one after the other and you can see the issue.
I am trying to pass the data.datasources.tabledata.datum array to another method that expects an array, but all I can get is the empty string.
Note: 'data' is the object returned from the service resolve at the state transition and injected into the view controller.
> console.log(data);
// Expected object.
> Object {datasources: Object}
datasources: Object
tabledata: Object
datum: Array[9383]
source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
__proto__: Object
__proto__: Object
__proto__: Object
> console.log(data.datasources);
// Expected object.
> Object {tabledata: Object}
tabledata: Object
datum: Array[9383]
source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
__proto__: Object
__proto__: Object
> console.log(data.datasources.tabledata);
// String instead of array... WTH???
> Object {source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv", datum: ""}
> console.log(data.datasources.tabledata.datum);
// Returns NOTHING - not even undefined... just an empty log line #_#
>
UPDATE: Here is the service loading the data (it is my own)
(function() {
'use strict';
angular
.module('mcsdss.providers')
.factory('FormulationRetrieval', FormulationRetrieval);
FormulationRetrieval.$inject = ['$http'];
function FormulationRetrieval($http) {
FormulationRetrieval.getFormulation = function (target) {
var promise = $http
.get(target)
.then(function (response) {
return FormulationRetrieval.configureFormulation(response.data);
});
return promise;
};
FormulationRetrieval.configureFormulation = function (f) {
FormulationRetrieval.formulationContainer = f;
FormulationRetrieval.loadFormulationSourceData(FormulationRetrieval.formulationContainer);
FormulationRetrieval.loadFormulationGisData(FormulationRetrieval.formulationContainer);
return FormulationRetrieval.formulationContainer;
};
FormulationRetrieval.loadFormulationSourceData = function (fc) {
function parseFormulationDatasource(fd, destination) {
Papa.parse(fd, {
complete: function(results) {
destination.datum = results.data;
}
});
}
function loadData(target) {
var promise = $http
.get(target.source)
.then(function (response) {
parseFormulationDatasource(response.data, target);
});
return promise;
}
var datasources = [fc.datagridConfig.datasources.tabledata, fc.graphConfig.datasources.graphContextData];
angular.forEach(datasources, loadData);
};
FormulationRetrieval.loadFormulationGisData = function (fc) {
function loadGeodata(target) {
angular.forEach(target, function(value, key) {
var promise = $http
.get(value.source)
.then(function (response) {
value.datum = response.data;
});
return promise;
});
}
var datasources = [fc.mapConfig.datasources.geojson];
angular.forEach(datasources, loadGeodata);
};
FormulationRetrieval.getAnalysisConfig = function (fc) {
var analysisConfig = fc.analysisConfig;
return analysisConfig;
};
FormulationRetrieval.getMaufConfig = function (fc) {
var maufConfig = fc.maufConfig;
return maufConfig;
};
FormulationRetrieval.getGraphConfig = function (fc) {
var graphConfig = fc.graphConfig;
return graphConfig;
};
FormulationRetrieval.getTableConfig = function (fc) {
var tableConfig = fc.datagridConfig;
return tableConfig;
};
FormulationRetrieval.getMapConfig = function (fc) {
var mapConfig = fc.mapConfig;
return mapConfig;
};
return FormulationRetrieval;
}
})();
Here is the ui-router resolve on the state:
(function() {
'use strict';
angular
.module('analyze')
.config(DashboardRoutes);
DashboardRoutes.$inject = ['$stateProvider'];
function DashboardRoutes($stateProvider) {
// Define states.
var analyze_state = {
abstract: true,
url: '/analyze',
templateUrl: 'modules/analyze/views/analyze.client.view.html',
controller: 'AnalyzeViewController',
controllerAs: 'analyze',
data: {
title: 'Analyze'
},
resolve: {
analysisData: function(FormulationRetrieval) {
return FormulationRetrieval.getFormulation('./data/formulations/bs.formulation.json');
},
analysisConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getAnalysisConfig(analysisData);
},
maufConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getMaufConfig(analysisData);
},
tableConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getTableConfig(analysisData);
},
graphConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getGraphConfig(analysisData);
},
mapConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getMapConfig(analysisData);
}
}
};
var analyze_layout_state = {
abstract: false,
url: '',
views: {
'graph': {
templateUrl: 'modules/analyze/views/analyze.graph.client.view.html',
controller: 'GraphViewController'
},
'map': {
templateUrl: 'modules/analyze/views/analyze.map.client.view.html',
controller: 'MapViewController'
},
'filters': {
templateUrl: 'modules/analyze/views/analyze.filters.client.view.html',
controller: 'FiltersViewController',
controllerAs: 'filters'
},
'datatable': {
templateUrl: 'modules/analyze/views/analyze.datatable.client.view.html',
controller: 'DatatableViewController',
controllerAs: 'datatable'
}
}
};
// Populate provider.
$stateProvider
.state('analyze', analyze_state)
.state('analyze.layout', analyze_layout_state);
}
})();
And here is the controller receiving the injected data:
(function() {
'use strict';
angular
.module('analyze')
.controller('AnalyzeViewController', AnalyzeViewController);
AnalyzeViewController.$inject = ['$rootScope', '$scope', '$state', '$location', 'Authentication', 'httpq', 'analysisData', 'maufConfig', 'tableConfig', 'graphConfig', 'mapConfig'];
function AnalyzeViewController($rootScope, $scope, $state, $location, Authentication, $httpq, analysisData, analysisConfig, maufConfig, tableConfig, graphConfig, mapConfig) {
// This provides Authentication context.
$scope.authentication = Authentication;
$scope.currentRoute = 'Analyze';
// console.log($scope.currentRoute);
// ALL OF THESE ARE INJECTED AND APPEAR TO BE CORRECT IN CONSOLE.
// console.log(analysisData);
// console.log(analysisConfig);
// console.log(maufConfig);
// console.log(tableConfig);
// console.log(graphConfig);
// console.log(mapConfig);
// NOTE: At one point I was loading the data through promises inside the
//controller, but moved it into the state resolve for better SOC.
//Strangely the $broadcast of the exact same value done here in the finally()
//block of the $httpq method works - using the new injected data object!
//And yet, the same $broadcast on $stateChangeSuccess (which DOES send the correct
//data into the listening subscribers if I only send the entire object) sends only
//empty string if I specify the array.
// Manual data loading.
$scope.sourceFile_A = './data/BSGAM_Heads_Wells_Drains_Zones_Master.csv';
$httpq.get($scope.sourceFile_A)
.then(function(data) {
// ...removed because not used.
})
.catch(function(data, status) {
console.error('Load error', response.status, response.data);
})
.finally(function() {
// Works here using the injected resolved promise, does not work in stateChangeSuccess... WTH??
$scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
});
$scope.$on('$stateChangeSuccess', function() {
// EXACT SAME BROADCAST AS ABOVE FAILS HERE - EMPTY STRING.
// $scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
});
// extra code removed...
})();
Lastly the Nested View Controller (one of 4) that I am trying to get to use the data via the $broadcast from the abstract parent. I can also access all the injected objects here directly without the broadcast but I am trying to decouple as much as possible as several views need to update in sync from the broadcast.
(function() {
'use strict';
angular
.module('analyze')
.controller('DatatableViewController', DatatableViewController);
DatatableViewController.$inject = ['$scope', 'Authentication', '$filter', 'ngTableParams', 'AnalysisDataFactory', 'tableConfig'];
function DatatableViewController($scope, Authentication, $filter, ngTableParams, AnalysisDataFactory, tableConfig) {
// This provides Authentication context.
$scope.authentication = Authentication;
// Expose public API.
$scope.headerFilter = headerFilter;
$scope.datasetOrder = datasetOrder;
$scope.rowClicked = rowClicked;
$scope.decorateSiblings = decorateSiblings;
$scope.clearSiblings = clearSiblings;
$scope.updateView = updateView;
// Private members.
$scope.headerdata = [];
$scope.tabledata = [];
$scope.suf01 = 0;
$scope.suf02 = 0;
$scope.suf03 = 0;
$scope.muf = 0;
$scope.$on('analysisDataLoaded', function(event, args) {
console.log('analysisDataLoaded...', event, args);
$scope.updateView(args);
// THIS IS WHAT I WANT TO DO INSTEAD:
// $scope.updateView(tableConfig); // Not working yet.
});
function headerFilter(target) {
return target.visible;
}
function datasetOrder(key) {
angular.forEach($scope.headers, function(target) {
// console.log('key='+key);
if (target.data == key) {
if (target.visible) {
return target.order;
}
}
});
return -1;
}
function rowClicked(target) {
for (var key in target) {
if (target.hasOwnProperty(key)) {
console.log(key + ' -> ' + target[key]);
}
}
}
function decorateSiblings(target) {
// console.log('data row touched, sending emission.');
$scope.$emit('currentDatatableTarget', target);
}
function clearSiblings(target) {
// console.log('datarow cleared, sending all clear.');
$scope.$emit('clearDatatableTarget', target);
}
function updateView(data) {
// ngTable
$scope.dataTable = new ngTableParams({
page: 1,
count: 10
}, {
total: $scope.tabledata.length,
counts: [10, 25, 50, 100, 250],
defaultSort: 'asc',
getData: function($defer, params) {
$scope.data = params.sorting() ? $filter('orderBy')($scope.tabledata, params.orderBy()) : $scope.tabledata;
$scope.data = params.filter() ? $filter('filter')(data, params.filter()) : data;
$scope.data = data.slice((params.page() - 1) * params.count(), params.page() * params.count());
$defer.resolve($scope.data);
}
});
}
}
})();
Something simple in here - probably a JS gotcha that I am overlooking.... Thanks in advance for any input!!
Turns out it was from not properly deferring and resolving the promises before trying to bind to them. I still have not identified exactly where that timing is going wrong in my code, but I can now intermittently load the data as I intended and can see that it is clearly a function of async inconsistencies. The $broadcast event is working just fine (when the async data loads properly).
I have the following controllers:
angular.module('app').controller('ParentCtrl', function(Service) {
var list = []
var init = function () {
Service.query().success(function() {
list = Service.getList();
});
}
});
angular.module('app').controller('ChildCtrl', function(Service) {
var list = []
var init = function () {
list = Service.getList();
}
});
angular.module('app').factory('Service', function($http) {
list = []
return {
query : function() {
$http.get('/path/to/my/api').success(function(data){
list = data
})
},
getList: function() {
return list;
}
}
});
My HTML is as follows:
<div ng-controller="ParentCtrl as parent">
<div ng-controller="ChildCtrl as child">
</div>
</div>
So basically when I receive the AJAX request I want both the controllers to get updated with the data
The best way would be to return the promise from $http.get:
angular.module('app').factory('Service', function($http) {
var promise;
return {
getList: function() {
if (promise) {
return promise;
}
promise = $http.get('/path/to/my/api');
return promise;
}
}
});
angular.module('app').controller('ParentCtrl', function(Service) {
var list = [];
var init = function () {
Service.getList().then(function(response) {
list = response.data;
});
}
});
angular.module('app').controller('ChildCtrl', function(Service) {
var list = [];
var init = function () {
Service.getList().then(function(response) {
list = response.data;
});
}
});
You can broadcast custom message to rootScope and Your controllers will get this message and handle it.
angular.module('app').controller('ParentCtrl', function($rootScope, $scope, Service) {
$scope.list = [];
$scope.$on('Service:list', function(event, data){
$scope.list = data;
});
});
angular.module('app').controller('ChildCtrl', function($rootScope, $scope, Service) {
$scope.list = [];
$scope.$on('Service:list', function(event, data){
$scope.list = data;
});
Service.query(); // run once, get in both controllers
});
angular.module('app').factory('Service', function($rootScope, $http) {
var list;
return {
query : function() {
$http.get('/path/to/my/api').success(function(data){
list = data;
$rootScope.$broadcast('Service:list', list);
})
},
getList: function() {
return list;
}
}
});
You could handle it in many ways. One simple way is to cache the promise and return it.
Example:-
angular.module('app').factory('Service', function($http, $q) {
var listPromise;
return {
getList: function() {
//If promise is already present then
return listPromise || (listPromise = $http.get('/path/to/my/api').then(function(response){
return response.data;
})
//Use the below 2 blocks (catch and finally) only if you need.
//Invalidate in case of error
.catch(function(error){
listPromise = null;
return $q.reject(error);
})
//Use finally to clean up promise only if you only need to avoid simultaneous request to the server and do not want to cache the data for ever.
.finally(function(){
listPromise = null;
}));
}
}
});
and in controller:
angular.module('app').controller('ParentCtrl', function(Service) {
var list = [];
var init = function () {
Service.getList().then(function(data) {
list = data;
});
}
});
angular.module('app').controller('ChildCtrl', function(Service) {
var list = [];
var init = function () {
Service.getList().then(function(data) {
list = data;
});
}
});
Caching a promise will make sure that it really does not matter who makes the first call and you always make the same service call to get the data and service will manage data caching via promise and prevent any duplicate calls.
Another practice is to implement a one-way data flow using flux pattern. Where you create stores that maintains data and it will make ajax call via a dispatcher and emits event to the subscribers of change event. There is an angular library (flux-angular) that can be used as well to implement this pattern. This will really help synchronize data between multiple components regardless of whether they are parent/child or siblings and regardless of who makes the first call etc..
How can I wait with my function till the $http request is finished?
My services.js looks as follows:
var app = angular.module('starter.services', []);
app.factory('Deals', function($http) {
function getDeals() {
$http.get('http://www.domain.com/library/fct.get_deals.php')
.success(function (data) {
var deals = data;
return deals;
})
.error(function(err){
});
}
return {
all: function() {
return getDeals();
},
get: function(keyID) {
//...
}
}
});
My controllers.js looks like:
var app = angular.module('starter.controllers', []);
app.controller('DealCtrl', function($scope, Deals) {
$scope.deals = Deals.all();
console.log($scope.deals);
});
The console.log in my controllers.js file outputs "undefined", but when I output the deals in the getDeals() function it contains the correct array which I get from my server.
What am I doing wrong?
$http and all of the async services in angularjs return a promise object. See promise api.
You need to use then method to assign it to a value in the scope.
So your controller:
app.controller('DealCtrl', function($scope, Deals) {
Deals.all().then(function (deals) {
$scope.deals = deals;
console.log($scope.deals);
});
});
Your service
app.factory('Deals', function($http) {
function getDeals() {
return $http.get('http://www.domain.com/library/fct.get_deals.php')
.success(function (data) {
var deals = data;
return deals;
});
}
return {
all: function() {
return getDeals();
},
get: function(keyID) {
//...
}
}
});
I'm trying to get a specific product by its id from a JSON file with products. I have some kind of problem as this question
AngularJS : get back data from a json array with an id
the code is similar. I read through that question and the accepted answer there, still can't figured this out. From what I understand the $scope.search() returns a promise which after success triggers the .then() to set get the correct person.
This line of code prints out the products array and also getting the product id from the url.
However it prints out twice in the console.
console.log($scope.products + $routeParams.productId);
app.js
var app = angular.module('gtbApp', [
'ngRoute',
'productControllers'
]);
// Setting up the routes with right controllers and partials
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/main', {
templateUrl: 'partials/product-grid.html',
controller: 'ProductController'
})
.when('/product/:productId', {
templateUrl: 'partials/product-detail.html',
controller: 'ProductDetailCtrl'
})
.otherwise({
redirectTo: '/main'
});
}]);
controllers.js
var app = angular.module('productControllers', []);
// For product-grid.html
app.controller('ProductController', ['$http', function($http){
var store = this;
store.products = [];
$http.get('products.json').success(function(data){
store.products = data;
});
}]);
// For product-detail.html
app.controller('ProductDetailCtrl', ['$scope', '$routeParams', '$http', function($scope, $routeParams, $http){
$scope.search = function() {
var url = 'products.json';
// Return a promise object
return $http.get(url).success(httpSuccess).error(function(){
console.log('Unable to retrieve info form JSON file.');
});
}
httpSuccess = function(response) {
$scope.products = response;
}
function getById(arr, id) {
for (var i = 0, len = arr.length; i < len; i++) {
if (arr[i].id === id) {
return arr[i];
}
}
}
$scope.search().then(function(){
// Prints out the products array and id twice
console.log($scope.products + $routeParams.productId);
$scope.product = getById($scope.products, $routeParams.productId);
// Prints out twice "undefined"
console.log($scope.product);
});
}]);
The main question is how to get specific product based on id why in "ProductDetailCtrl"
$scope.product = getById($scope.products, $routeParams.productId);
doesn't work.
Thanks in advance!
Update:
Found out why $scope.product is undefined, it is just because the $routeParams.productId is a string, and in getById() need a integer in second args.
However I don't know why console.log($scope.product); prints out twice.
I don't really understand what your main question is here. But anyways. When you use the $http service it will return a promise, which you eventually will have to unwrap. What you are doing in your code is that you are unwrapping it twice. Which is fine.
With $http response you can either use 'success'/'error' or just 'then' which can take a success and an error callback. Which means you could either unwrap in the search function or after you call the search function.
$scope.search = function() {
var url = 'products.json';
$http.get(url)
.success(function(data){
$scope.product = getById($scope.products, $routeParams.productId);
})
.error(function() {
console.log('Unable to retrieve info form JSON file.');
});
}
You could also do something like:
$scope.search = function() {
var url = 'products.json';
return $http.get(url);
}
$scope.search().then(function(data) {
$scope.product = getById(data, $routeParams.productId);
}, errorCallback);
And the below would achieve the same result
$scope.search = function() {
var url = 'products.json';
return $http.get(url);
}
$scope.search()
.success(function(data) {
$scope.product = getById(data, $routeParams.productId);
})
.error(errorCallback);
or reference the promise:
$scope.search = function() {
var url = 'products.json';
return $http.get(url);
}
var dataPromise = $scope.search();
dataPromise.then(function(data) {
$scope.product = getById(data, $routeParams.productId);
}, errorCallback);
What you need to know is that as long as you're returning something within a success/error/then function it will return a promise which you will have to unwrap in order to get the data.
You should be either using the .success() and .error() on the $http-promise or only then .then()
Do it like this:
app.controller('ProductController', ['$scope', '$routeParams', '$http', function($scope, $routeParams, $http){
$scope.search = function() {
var url = 'products.json';
// Return a promise object
return $http.get(url);
}
.....
$scope.search()
.success(function(data){ // --> data is the products.json
... // handle the successfull call
} );
.error(function(...) {
... // handle the error
} );
// or:
$scope.search().then(
function(data){ // --> data is the products.json
... // handle the successfull call
},
function(...) {
... // handle the error
});
}]);