Currently my array cust is empty but the json data (wrapped with some metadata) is successfully loaded as I can see in Chrome debugger network preview.
How to pass the result in my array, so that there are indexed and I can access them in my program?
angular.module('Cust').factory('CustomizingService', ['$resource', function ($resource) {
var url = 'http://localhost:31736/Service1.svc/:link/';
return {
Customizing: $resource(url, {callback: 'JSON_CALLBACK'}, {
customers: {
method: 'JSONP', transformResponse: function(data) {return angular.fromJson(data).body.rows},
params: { link: 'GetCustomers', numberOf: '#numberOf', valid = #valid },
isArray: true },...
My controller:
app.controller('controllerA', ['$scope', 'CustomizingService',
$scope.cust = CustomizingService.Customizing.customers({numberOf: 12, valid: true});
}]);
app.controller('controllerA', ['$scope', 'CustomizingService', function(){
CustomizingService.Customizing
.customers({numberOf: 12, valid: true}).$promise
.then(function(data){
$scope.cust = data;
},function(error){
});
}]);
I solved the problem by using the AngularJS service restangular. It is an easy way to handle Rest API Resources properly and easily.
More information here: https://github.com/mgonto/restangular.
Now I can drop ng-resource :-)
The new code:
app.controller('controllerA', ['$scope', 'CustomizingService', 'Restangular', function($scope, CustomizingService, Restangular) {
//http://localhost:31736/Service1.svc/GetCustomers?numberOf=12&valid=true
Restangular.all('Service1.svc').customGETLIST('GetCustomers',{numberOf: 12, valid: true}).then(function(result){
$scope.customers= result;
});
}]);
app.config(function(RestangularProvider){
RestangularProvider.setBaseUrl('http://localhost:31736/');
RestangularProvider.setDefaultRequestParams('jsonp', {callback:
'JSON_CALLBACK'});
});
Related
I've learning to use Karma to test my angularjs app. However, not a few of my controllers use multiple services which are http requests that retrieve json which is then loaded to the page. I've been stuck because of two questions I can't answer. 1) How do I mock these services 2) what exactly should I test for my controller Question 2 I find difficult to answer because controller functions depend on services which I use in other controllers. Anyway, let me show one of my controllers and then, the "library" housing my services:
One of my controllers
angular.module('ccApp')
.controller('CountriesCtrl', ['$scope', '$routeParams', '$location','countryInfo', 'getCountries', 'countriesCache', 'getNeighbors',
'buildCountry', '$timeout', '$q',
function($scope, $routeParams, $location, countryInfo, getCountries, countriesCache, getNeighbors,
buildCountry, $timeout, $q){
getCountries.countriesObject.then(function(response){
$scope.geocountries = response.data.geonames;
},
function(response){
alert("error");
});
$scope.toCountry = function(geocountry){
getNeighbors(geocountry.geonameId)
.then(function(response){
buildCountry(geocountry, response);
var path = '/countries/'+countryInfo.name+'/capital';
$location.path(path);
}),
function(response){
alert('Error');
};
};
$scope.goHome = function(){
$location.path('/');
};
}]);
What here should I test in the controller spec?
Here's the library where the services are housed:
angular.module('library', [])
.service('countryInfo', function(){
var country = {
name: '',
pop: '',
area: '',
capital: '',
code: '',
capPop: '',
numNeigh: 0,
neighbors: []
};
return country;
})
.factory('countriesCache', ['$cacheFactory', function($cacheFactory){
return $cacheFactory('countriesCached');
}])
.factory('getCountries', ['$http', function($http){
var request = {
username: 'vman'
};
return { countriesObject : $http({
method: 'GET',
url: 'http://api.geonames.org/countryInfoJSON',
params: request
})};
}])
.factory('getCountry', ['$http', function($http){
return function(countryCode){
return $http({
method: 'GET',
url: 'http://api.geonames.org/countryInfoJSON',
params: { username: 'vman', country: countryCode }
});
};
}])
.factory('getNeighbors', ['$http', function($http){
return function(geonameId){
return $http({
method: 'GET',
url: 'http://api.geonames.org/neighboursJSON',
params: { username: 'vman', geonameId: geonameId }
});
};
}])
.factory('buildCountry', ['countryInfo', '$q', '$timeout', function(countryInfo, $q, $timeout){
return function(geocountry, response){
countryInfo.name = geocountry.countryName;
countryInfo.code = geocountry.countryCode;
countryInfo.pop = geocountry.population;
countryInfo.area = geocountry.areaInSqKm;
countryInfo.capital = geocountry.capital;
countryInfo.neighbors = response.data.geonames;
countryInfo.numNeigh = response.data.geonames.length;
};
}])
.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function() {
$location.path('/error');
});
}]);
The following snippet creates a mock of the above service:
module(function($provide) {
$provide.service('demoService', function() {
this.isDemoApi = jasmine.createSpy('isDemoApi');
});
});
//Getting reference of the mocked service
var mockDemoSvc;
inject(function(demoService) {
mockDemoSvc = demoService;
});
To test the controller, firstly you will have to mock all the services which this controller is using by using the above code.
This would help to test controller APIs seperatly. Then you can go ahead to test APIs which are binded with the scope, like: toCountry()
I'm using Angular in an application. After getting a specific object (a movie in my case), I'm assigning the object to $scope ($scope.movie = response), so that I can use it in the view. The problem is that my view seems not to display anything I use in $scope. I've tried deleting everything and doing a dummy test like $scope=name="whatever" and when I use something like {{name}} in the view nothing is rendered. Have anyone faced this problem ? I've already searched for this error, and it seems like it would be a good idea to use $apply(). I've tried that and it didn't work. The function that fetches the data is below:
var app = angular.module('movies');
app.factory('Films', ['$resource',function($resource){
return $resource('/films.json', {},{
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
}]);
app.factory('Film', ['$resource', function($resource){
return $resource('films/:id.json', {}, {
show: {method: 'GET' },
update: { method: 'PUT', params: {id: '#id'} },
delete: { method: 'DELETE', params: {id: '#id'} }
});
}]);
app.controller('MoviesController', ['$scope', '$http', '$location', '$resource', '$routeParams', 'Films', 'Film', function($scope, $http, $location, $resource, $routeParams, Films, Film){
$scope.movies = Films.query();
$scope.user = document.getElementById('name').innerHTML; // Find a better way to interact with devise via angular
$scope.createMovie = function() {
$scope.movies = Films.query();
$http.get(
'/categories.json'
).success(function(data,status,headers,config){
$scope.categories = data;
}).error(function(data, status, headers, config){
alert("There was an error while fetching the categories on the database. Error " + status);
});
$location.path("/" + 'new').replace();
};
$scope.listMovies = function() {
$location.path("/").replace();
};
$scope.save = function(){
if($scope.form.$valid){
Films.create({film: $scope.movie}, function(){
$scope.form.$setPristine();
}, function(error){
alert("Movie not created");
});
}
};
$scope.deleteMovie = function(movie){
Film.delete(movie);
$scope.movies = Films.query();
};
$scope.viewDetails = function(movie){
$scope.name="ola";
alert(movie.id);
$location.path("/" + movie.id);
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
$scope.movie = Movie.get({filmId: movie.id});
$scope.movie.$promise.then(
function(response){
$scope.$apply();
$scope.movie = response;
console.log("filme e: " + response.name);
},
function(error){
console.log("request failed");
}
);
};
}]);
I had a look at your repository and I think where your problem is. You are trying to reuse the MoviesController in all of your routes. But AngularJS will create a new instance for every route and therefore you can't access your previous data because it will be destroyed.
So I would start by creating a separated controller for each view, so you can move the code of your viewDetails method to a new MovieDetailController. To have access to the movie id in this controller, you need to use the $routeParams service.
angular.module('movies').controller('MovieDetailController', MovieDetailController);
function MovieDetailController($scope, $resource, $routeParams) {
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
Movie.get({filmId: $routeParams.id}).then(
function(response) {
$scope.movie = response;
},
function(error){
console.log('request failed');
}
);
}
Change your route definition to use the new controller.
.when('/movies/:id', {
controller: 'MovieDetailController',
templateUrl: 'movie_details.html'
})
And now your viewDetails method in the MoviesController just need to redirect to the movie detail url.
$scope.viewDetails = function(movie) {
$location.path('/movies/' + movie.id);
}
I hope it works for you. Let me know when you try!
I ve got an angular resource service which then returns the data to a controller and I get all the data plus the data by name.
My application works just fine in the browser but I get a resource error in the console. Bad resource configuration.
I had a look in various questions and everyone states that I need to set the configuration property isArray to either false or true.
I have tried to do this but I still get an error.
Any ideas much appreciated.
Here is my service :
(function() {
var app = angular.module('test');
app.service('ContactResource', function($resource) {
return $resource('/contacts/:firstname', {},
{'update': {method: 'PUT'}},
{'query': { method: 'GET', isArray: true }},
{'get': { method: 'GET', isArray: false }}
);
});
}());
And here is my controller:
(function() {
var app = angular.module('test');
app.controller('contactsCtrl', function($scope, $routeParams, ContactResource) {
$scope.contacts = ContactResource.query();
$scope.singlecontact = ContactResource.get({firstname: $routeParams.firstname});
});
}());
The error I am getting is : Error: [$resource:badcfg] http://errors.angularjs.org/1.4.2/$resource/badcfg?p0=get&p1=object&p2=array&p3=GET&p4=%2Fcontacts
When I click it says :
Error in resource configuration for action get. Expected response to contain an object but got an array (Request: GET /contacts)
When I get the url is /contacts the response is :
[{EmailAddress:some#email.com, etc}]
When the url is /contacts/firstname the response is :
{EmailAddress:some#email.com,etc}
I solved the problem by adding a new controller called single controller and by separating the service into two functions. Here is how my code looks like now.
This is the service:
(function() {
var app = angular.module('test');
app.service('ContactResource', function($resource, $routeParams) {
this.all = function() {
return $resource('/contacts', {},
{'query': { method: 'GET', isArray: true }}
)};
this.single = function() {
return $resource('/contacts/:firstname', {firstname: '#firstname'},
{'query': { method: 'GET', isArray: false }}
);
}
});
}());
And the controllers :
(function() {
var app = angular.module('test');
app.controller('contactsCtrl', function($scope, $routeParams, ContactResource) {
$scope.contacts = ContactResource.all().query();
});
app.controller('singleCtrl', function($scope, $routeParams, ContactResource) {
$scope.singlecontact = ContactResource.single().query({firstname: $routeParams.firstname});
});
}());
For some reason which I am still not sure $resource wouldn't accept them into the same controller.
I have a controller:
app.controller('ProductDetailCtrl', ['$scope', '$q', '$resource', 'Product', function($scope, $q, $resource, Product) {
$scope.product = {};
$scope.init = function(id)
{
$scope.product = Product.get({productId: id});
}
$q.all([$scope.product.$promise]).then(function() {
$scope.selected_color = $scope.product.products_colors[0];
});
// $scope.selected_color = $scope.product.products_colors[0];
}]);
And a factory:
app.factory('Product', ['$resource', function($resource) {
return $resource("/api/products/:productId", {}, {
query: {method: 'GET', isArray: true},
});
}]);
However, when I load the page it gives me this:
Error: $scope.product.products_colors is undefined #http://localhost:3000/assets/products/controllers/productscontroller.js?body=1:11:5 qFactory/defer/deferred.promise.then/wrappedCallback#http://localhost:3000/assets/angular.js?body=1:11499:15 qFactory/ref/<.then/<#http://localhost:3000/assets/angular.js?body=1:11585:11 $RootScopeProvider/this.$get</Scope.prototype.$eval#http://localhost:3000/assets/angular.js?body=1:12609:9 $RootScopeProvider/this.$get</Scope.prototype.$digest#http://localhost:3000/assets/angular.js?body=1:12421:15 $RootScopeProvider/this.$get</Scope.prototype.$apply#http://localhost:3000/assets/angular.js?body=1:12713:13 bootstrap/doBootstrap/<#http://localhost:3000/assets/angular.js?body=1:1420:9 invoke#http://localhost:3000/assets/angular.js?body=1:3919:7 bootstrap/doBootstrap#http://localhost:3000/assets/angular.js?body=1:1419:1 bootstrap#http://localhost:3000/assets/angular.js?body=1:1432:5 angularInit#http://localhost:3000/assets/angular.js?body=1:1345:5 #http://localhost:3000/assets/angular.js?body=1:21818:5 jQuery.Callbacks/fire#http://localhost:3000/assets/jquery.js?body=1:3100:1 jQuery.Callbacks/self.fireWith#http://localhost:3000/assets/jquery.js?body=1:3212:7 .ready#http://localhost:3000/assets/jquery.js?body=1:3424:3 completed#http://localhost:3000/assets/jquery.js?body=1:3454:3
return logFn.apply(console, args);
How would I wait until the $resource finishes loading? I want to set one of my $scope variables based on the result of Product.get(..). I know that Product.get(..) is returning and object with a products_colors property because console.log says so
Use a callback:
Product.get({productId: id}, function(data) {
$scope.product = data;
});
Below is my code for querying a resultset in angularjs. Below is both controller.js and services.js. However I want to get .success and .error defined in my getResult call so that I can defined different $scopes for each flow and show it on UI accordingly. I searched but everywhere I got it for $http which I am not using. As I am new to angularjs, could you please help me out with it?
app.controller('DemoCtrl3', ['$scope', 'PFactory', '$location', function ($scope, PFactory, $location) {
$scope.getResult = function () {
$scope.allposts = PFactory.postmain.query();
$location.path('/view2');
}
services.js is:
return {
postmain: $resource('/ngdemo/web/posts', {}, {
query: {method: 'GET', isArray: true },
create: {method: 'POST'}
}),
You can pass success and error callbacks to resource actions (query, create, etc.) and get response data as callback parameter. Here is an example of doing it:
HTML
<body ng-controller="ctrl">
<h1>{{message}}</h1>
</body>
JavaScript
angular.module('app',['ngResource']).
service('PFactory', ['$resource', function($resource) {
return {
postmain: $resource('data.json', {}, {
query: {method: 'GET', isArray: true },
create: {method: 'POST'}
})
}
}]).
controller('ctrl', ['$scope', 'PFactory', function($scope, PFactory) {
PFactory.postmain.query(function success(data){
$scope.message = 'Number of records loaded: '+data.length;
}, function error() {
$scope.message = 'Server Error!'
});
}]);
Plunker: http://plnkr.co/edit/k5LgMPkU6jAteaFCn74C?p=preview
AngularJS documentation: $resource