Angular not loading data from external json - angularjs

Not sure what I have wrong here in this setup to simply display books in a json,
I think it might be the view or the controller that may be wrong, but I'm unsure. Thanks for any help.
<div class="book col" ng-repeat="book in myBooks">
<h3 class="title">{{book.title}}</h3>
<p class="author">{{book.author}}</p>
</div>
services.js
app.factory('books', ['$http', function($http) {
return $http.get('books.json')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
books.json
{
"per_page":20,
"page":1,
"total_pages":1468,
"total_results":29346,
"books":[
{
"uuid":"235b68e4-5b16-4a25-b731-45c7e67c351e",
"id":98024,
"title":null,
"author":null,
"language":null,
"createtime":"2016-05-19T13:09:40.963+00:00"
},
{
"uuid":"5e87daca-e87b-4324-a652-e06d5349bd82",
"id":98055,
"title":null,
"author":null,
"language":null,
"createtime":"2016-05-23T15:50:11.777+00:00"
}
Controller.js
app.controller('BookshelfController', ['$scope', 'books', function($scope, books) {
books.success(function(data) {
$scope.myBooks = data;
});
}]);

Return from your factory an object which contains your function, calling which will return you the result of $http.get()
app.factory('books', ['$http', function($http) {
return {
getBooks: function() {
return $http.get('books.json');
}
};
}]);
Because $http.get returns a Promise object, in your controller use then to get the results from the response.
app.controller('BookshelfController', ['$scope', 'books', function($scope, books) {
books.getBooks().then(function (data) {
$scope.myBooks = data;
}, function (err) {
console.log(err);
});
}]);
Also check your url to be correct.

Related

Retrieving data from dynamic URL - angular

I'm trying to retrieve data with the following code, where the URL of the service has a dynamic parameter ie. the id, there is something wrong because the data isn't displaying, when I load the URL in the browser with this on the end:
../categories/165
could I get some help please? Thanks.
...
.when('/categories/:categoryId', {
controller: 'BookCategoryController',
templateUrl: 'views/booksincategory.html'
})
...
controller
app.controller('BookCategoryController', ['$scope', 'bookcategories', '$routeParams', function($scope, bookcategories, $routeParams) {
bookcategories($scope.id).success(function(data) {
console.log($scope.id);
$scope.detail = data.books[$routeParams.categoryId];
});
}]);
service
app.service('bookcategories', ['$http', function($http) {
return {
get: function(id) {
return $http.get('http://52.41.65.211:8028/api/v1/categories/'+ id + '/books.json')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}
}
}]);
booksincategory.html
<div class="category col" ng-repeat="book in detail">
<h3 class="title">{{book.title}}</h3>
</div>
Change your service code to :
app.service('bookcategories', ['$http', function($http) {
return {
getAllBooks: function(id) {
return $http.get('http://52.41.65.211:8028/api/v1/categories/'+ id + '/books.json')
}
}
}]);
and in Controller :
bookcategories.getAllBooks($scope.id).then(function(response) {
$scope.detail = response.data.books[$routeParams.categoryId];
});
EDIT2
You have to define $scope.id somewhere in your controller like below :
$scope.id= 1 ;
console.log($routeParams.categoryId);//check what you are getting here
bookcategories.getAllBooks($routeParams.categoryId).then(function(response) {
$scope.detail = response.data.books[$routeParams.categoryId];
});
after this your service URL will go like below (Took this URL from your Question)
http://52.41.65.211:8028/api/v1/categories/1/books.json
See 1 in the URL its $scope.id !
.success & .error function(deprecated since angular 1.4.X) are not chainable-friendly, so it prevents to return a promise object from get method. Removing success & error callback will allow to return promise object from function. Consider using .then function over promise since .success & .error callback deprecated.
get: function(id) {
return $http.get('http://52.41.65.211:8028/api/v1/categories/'+ id + '/books.json');
}
//controller code & pass `$routeParams.categoryId` in it
bookcategories($routeParams.categoryId).then(function(response) {
$scope.detail = response.data.books[$routeParams.categoryId];
});

Issues updating angular view from another controller

In the below, I have a sidenav and a main content section. I am trying to call the sidenav function from the main controller so the number updtes in the sidenav. What am I doing wrong here?
This is my view.
<div>Teams {{ teams.length }} </div>
This is my sidebar controller:
angular.module('myApp').controller('leftNav', function($scope, myFactory) {
myFactory.getTeams().then(function(res) {
$scope.teams = res.data;
})
This is my factory
angular.module('myApp').factory('myFactory', function($http) {
return {
getTeams : function() {
return $http.get('/teams').then(function(res) {
return res;
});
}
};
});
This is a complete different controller:
angular.module('myApp').controller('main', function($scope,$http, myFactory) {
$http.post(...blablabl..).then(function() {
// What can i call here to update the sidenav above?
});
});
You can utilise $controller service to call your leftNav controller from your main controller like below
angular.module('myApp').controller('main', function($scope,$http, myFactory,$controller) {
$http.post(...blablabl..).then(function() {
var leftCTRL= $controller('leftNav', { $scope: $scope.$new() });
});
});
you can try like so :
angular.module('myApp').controller('leftNav', function($scope, myFactory) {
myFactory.getTeams().then(function(res) {
this.teams = res.data;
})
angular.module('myApp').controller('main', function($scope,$http, $controller) {
var leftNav= $controller('leftNav');
$http.post(...blablabl..).then(function(res) {
leftNav.teams = res.data;
});
});

AngularJS: Service not returning data

I have been trying to get data from a json file through service in AngularJS(1.5.3). I am unable to retrieve the data from json and display it in the view. I get blank values instead. Below is the code:
//Service
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
var promise = $http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return "Invalid data";
})
return promise;
}
};
}]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
Update:
Below is the json data that I have.
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}];
Could anyone help me out.
I think the problem is in your first line!
you should pass all the services as string:
angularStoreApp.factory('dataService', ['$http', '$q' function ($http, $q) {
In order to get the code snippet working, you need to
initialize the module in the js: var angularStoreApp = angular.module('storeapp', []);
add ng-app in the view
add ng-controller in the view (or use routing)
Forgetting to add $q to the dependencies is indeed a mistake, but it doesn't prevent your app from working, as long as you don't use $q.
Below is an adjusted, working code snippet. I simulated the http call by returning json wrapped in a promise. If it still doesn't work, the problem is the HTTP call, i.e. the part I commented out. Execute the call in the browser and verify that the correct JSON is returned.
var angularStoreApp = angular.module('storeapp', []);
//Service
angularStoreApp.factory('dataService', ['$http', '$q',
function($http, $q) {
return {
getProducts: function() {
//simulate promise with json:
return $q.when([{
'Name': 'name 1',
'Price': 1.23
}, {
'Name': 'name 2',
'Price': 4.56
}]);
//var promise = $http.get('/api/json/products.json')
// .then(function successCallback(response) {
// if (typeof response.data === 'object') {
// return response.data;
// } else {
// invalid response
// return "Invalid data";
// }
// }, function errorCallback(response) {
// return "Invalid data";
// })
// return promise;
}
};
}
]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService',
function($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="storeapp" ng-controller="SearchCtrl">
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
</div>
I've replicated your code in jsbin and it is working fine through I've changed the code for brevity.
The issue might be with below code as it looks for products.json in root folder not in your project folder.
$http.get('/api/json/products.json')
Try with removing '/' in the URL.
$http.get('api/json/products.json')
I found the answer after reading the following links
http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/
http://bguiz.github.io/js-standards/angularjs/resolving-promises-for-a-controller/
The following is how I modified my code to get it working:
In the config routing section I used the resolve property for that route
var angularStoreApp = angular.module('AngularStore', ['ngRoute']);
angularStoreApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/products', {
templateUrl: '/Views/Store.html',
controller: 'SearchCtrl',
resolve: {
resolvedJSON: function (dataService) {
return dataService.getProducts();
}
}
})
.when('/product/:productName', {
templateUrl: '/Views/product.html',
controller: 'ProductCtrl'
})
.otherwise({
redirectTo: '/products'
});
}]);
Now I injected the resolvedJSON in my controller as follows
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'resolvedJSON', function ($scope, resolvedJSON) {
$scope.ProductItems = [];
$scope.ProductItems = resolvedJSON;
}]);
And my service code as follows:
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
return $http.get('/api/json/products.json')
.then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
return "Invalid data";
})
}
};
following is my json data
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}]
My view(Store.html) is something like this:
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.itemName}}, {{ProductItem.itemPrice}}
</div>
</div>
Hope this helps anyone facing a similar issue as mine. Thank you everyone who steered me to a better and simple answer.
u forgot the '$q' for minification
u should use the var deferred = $q.defer();// (I will add a full code)
example jsfiddle
angular.module('app', [])
.controller('ctrl', function(dataService, $scope) {
var vm = this;
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
})
.factory('dataService', ['$http', '$q', function($http, $q) {
function getProducts() {
var deferred = $q.defer();
$http.get('https://httpbin.org/get')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
// let say we get list of Products
deferred.resolve([{
id: 1,
name: 'Product1'
}, {
id: 2,
name: 'Product2'
}, {
id: 3,
name: 'Product3'
}]);
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return deferred.reject("Invalid data");
})
return deferred.promise;
}
return {
getProducts: getProducts
};
}]);
I think one of the problems you have is already mentioned #Ahmad Mobaraki ,and another one is in your dataService, the promise object returned by $http.get('/api/json/products.json') is already consumed by then() function, so what you need to do is to create a new promise and return it.
Code example:
//service
angularStoreApp.factory('dataService', ['$http', '$q', function ($http, $q) {
return {
getProducts: function () {
var defered = $q.defer();
$http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
defered.resolve(response.data);
} else {
// invalid response
defered.reject("Invalid data");
}
}, function errorCallback(response) {
defered.reject("Invalid data");
});
return defered.promise;
}
};
}]);
//Controller
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
hope this can help you :)

How to get the length of an array without ngRepeat

I'm trying to count the items in an array without using ng-repeat (I don't really need it, i just want to print out the sum).
This is what I've done so far: http://codepen.io/nickimola/pen/zqwOMN?editors=1010
HTML:
<body ng-app="myApp" ng-controller="myCtrl">
<h1>Test</h1>
<div ng-cloak>{{totalErrors()}}</div>
</body>
Javascript:
angular.module('myApp', []).controller('myCtrl', ['$scope', '$timeout', function($scope) {
$scope.tiles= {
'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]}
$scope.totalErrors = function() {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
})
}
}
}]);
This code works on codepen, but on my app I get this error:
Cannot read property '0' of undefined
and if I debug it, topLevel is undefined when the functions is called.
I think it is related to the loading of the data, as on my app I have a service that looks like this:
angular.module('services', ['ngResource']).factory('tilesData', [
'$http', '$stateParams', function($http, $stateParams) {
var tilesData;
tilesData = function(myData) {
if (myData) {
return this.setData(myData);
}
};
tilesData.prototype = {
setData: function(myData) {
return angular.extend(this, myData);
},
load: function(id) {
var scope;
scope = this;
return $http.get('default-system.json').success(function(myData) {
return scope.setData(myData.data);
}).error(function(err) {
return console.error(err);
});
}
};
return tilesData;
}
]);
and I load the data like this in my controller:
angular.module('myController', ['services', 'ionic']).controller('uiSettings', [
'$scope', '$ionicPopup', '$ionicModal', 'tilesData', function($scope, $ionicPopup, $ionicModal, tilesData) {
$scope.tiles = new tilesData();
$scope.tiles.load();
$scope.totalErrors = function() {
debugger;
var topLevel;
topLevel = $scope.tiles.data;
console.log(topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length;
}).reduce(function(prev, curr) {
return prev + curr;
});
};
}
]);
but I don't know what to do to solve this issue. Any help will be really appreciated. Thanks a lot
The $http.get() method is asynchronous, so you can handle this in your controller with a callback or a promise. I have an example using a promise here.
I've made an example pen that passes back the sample data you use above asynchronously.This mocks the $http.get call you make.
I have handled the async call in the controller in a slightly different way to what you had done, but this way it works with the .then() pattern that promises use. This should give you an example of how you can handle the async code in your controller.
Note as well that my service is in the same module as my controller. This shouldn't matter and the way you've done it, injecting your factory module into your main module is fine.
angular.module('myApp', [])
//Define your controller
.controller('myCtrl', ['$scope','myFactory', function($scope,myFactory) {
//call async function from service, with .then pattern:
myFactory.myFunction().then(
function(data){
// Call function that does your map reduce
$scope.totalErrors = setTotalErrors();
},
function(error){
console.log(error);
});
function setTotalErrors () {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
});
}
}
}])
.factory('myFactory', ['$timeout','$q',function($timeout,$q){
return {
myFunction : myFunction
};
function myFunction(){
//Create deferred object with $q.
var deferred = $q.defer();
//mock an async call with a timeout
$timeout(function(){
//resolve the promise with the sample data
deferred.resolve(
{'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]})
},200);
//return promise object.
return deferred.promise;
}
}]);
Have a look : Link to codepen
Also, have a read of the $q documentation: documentation

Update template when scope is changed

I have a template in which I output the values (movie titles) from my database,
%div{"ng-repeat" => "movie in movies"}
{{ movie.title }}
And a template in which users can input a movie title,
%div{"ng-controller" => "searchCtrl", :id => "container_search"}
#addMovie{"ng-controller" => "addMovieCtrl"}
%div{"ng-click" => "addMovie()"}
%input{:type => "text", "ng-model" => "title"}
addMovie action.
When a user types in a movie title in the inputfield and clicks the div it gets saved into the database, and when I refresh the page I can see the result. But I want this to happen asynchronously (at the same time, right?).
This is the controller,
angular.module('addMovieseat')
.controller('movieOverviewCtrl', [
'$scope', 'movieService', function($scope, movieService) {
movieService.success(function(data) {
$scope.movies = data;
});
}
]);
And this is the service,
angular.module('addMovieseat')
.factory('movieService', ['$http', function($http) {
return $http.get('movies.json')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}])
.factory('movies', ['$http', function($http){
var o = {
movies: []
};
o.create = function(movie){
return $http.post('/movies.json', movie).success(function(data){
o.movies.push(data);
});
};
return o;
}])
Your service should not do an HTTP request as soon as it's instanciated, and then always return the same result. Instead, it should provide a method that allows getting the movies.
Once that is done, you can simply call the service again right after saving a new movie:
angular.module('addMovieseat')
.factory('movieService', ['$http', function($http) {
return {
loadMovies: function() {
return $http.get('movies.json');
}
};
}])
.factory('movies', ['$http', function($http){
return {
create: function(movie) {
return $http.post('/movies.json', movie);
}
};
}]);
and your controller can now simply do
angular.module('addMovieseat')
.controller('movieOverviewCtrl', [
'$scope', 'movieService', 'movies', function($scope, movieService, movies) {
var init = function() {
movieService.loadMovies().then(function(response) {
$scope.movies = response.data;
});
};
init();
$scope.save = function() {
movies.create({title: $scope.title}).then(init);
};
}]);
Note that you're making your own life more complex than it should by defining two services instead of just one that would have a loadMovies() and a create() functions.

Resources