angular $resource does not behave the way expected - angularjs

I have this angular code
angular.module('ninthBeatApp')
.factory('Item', function ($resource) {
return $resource('/api/items/:id', {
id: '#_id'
});
});
angular.module('ninthBeatApp')
.controller('ItemCtrl', function ($scope, $stateParams, Item) {
$scope.item = Item.get({ _id: $stateParams.id });
});
In the backend, I got
GET /api/items?_id=550b3d2b532f415ce5080056 304 27ms
this is not what I want.
It supposed to be
GET /api/items/550b3d2b532f415ce5080056
Anyone knows why?

Try the following:
$scope.item = Item.get({id: $stateParams.id});
This will plug the $stateParams value into the url directly instead of passing it as a GET query param. The #_id refers to how a default parameter is determined.

Related

Resolving AngularJS promise returned by Factory function

I have a factory object that has a getProduct(id) function as shown below
angular.module('app')
.factory('productFactory', ['$http', 'URL', function($http, URL){
var urlBase = URL.APISERVER;
//productFactory return object
var productFactory = {};
productFactory.getProducts = function(){
return $http.get(urlBase + '/products/');
}
productFactory.getProduct = function(id){
return $http.get(urlBase + '/products/' + id);
}
//return factory object
return productFactory;
}]);
In my controller I want to resolve the getProduct(id) promise. I am resolving this promise in another function getProduct(id) where I passed the product id.
angular.module('portal')
.controller('ProductCtrl', ['$stateParams','productFactory',
function($stateParams, productFactory){
//Using 'controller as syntax' in view
var prod = this;
//Saves status of promise
prod.status;
//Get a single product
prod.product;
//Get the product id from stateParams and pass that to
//productFactory's getProduct(id) function
var id = $stateParams.id;
getProduct(id);//THIS IS WHAT CAUSES THE 500 ERROR
//Resolves promise
function getProduct(id){
productFactory.getProduct(id)
.success(function(product){
prod.product = product;
})
.error(function(error){
prod.status = 'Unable to load product data' + error.message;
});
}
}]);
However, when I invoke getProduct(id); in my controller I get 500 error and that's because I don't yet have access to the $stateParams.id when the controller compiles.
I have a list of products. When clicked on a particular product I am redirected to the view of this single product. So far it works with the 500 error. Any ideas how to fix this issue. My initial idea is to call the getProduct(id); in my controller conditionally. I am sure there is a better way to do this.
UPDATE
Here is the relevant routing config for this state.
.state('products-view', {
url: '/products/:id',
templateUrl: '/core/products/view.html'
})
I use something like the following to resolve http requests using ui-router.
$stateProvider
// nested list with custom controller
.state('home.list', {
url: '/list/:id',
templateUrl: 'partial-home-list.html',
resolve:{
// Inject services or factories here to fetch 'posts'
posts:function($http, $stateParams){
return $http.get('//jsonplaceholder.typicode.com/posts/' + ($stateParams.id || 1));
}
},
// resolved 'posts' get injected into controller
controller: function(posts, $scope){
$scope.posts = posts.data;
}
})
You can dependency inject productFactory instead of $http like I did. The posts key specified in resolve can be dependency injected into the controller specified as a function or as a string.

AngularJs, calling a $resource from within an Interceptor

I have just about given up with this. But I have a $resource that uses a query() to retrieve a list of items. I then have an Interceptor that runs over those items in order to insert an $interval.
The $interval at a specific point will then get an item again using the $resource's get() function. But its this get() that is not working.
The call happens but I cannot get the response back into the template.
myServices.factory('Items', ['$resource',
function($resource) {
return $resource("/items", {}, {
'query': {
interceptor: MyInterceptor,
url: "/items",
isArray: true
},
})
}]);
myServices.factory('MyInterceptor', function($q, $interval, $injector, $rootScope) {
return {
'response': function(response) {
angular.forEach(response.resource, function (item) {
$interval(function () {
item.something = 1 + item.something;
if(item.something == 10)
{
item = $injector.get("Mine").get({slug: item.id});
}
});
});
return response;
}
};
});
I thought that this line item = $injector.get("Mine").get({slug: item.id}); would work, but it doesn't. The new item is not changed in the template.
So I changed it to something like this, which did not work either;
$injector.get("Mine").get({slug: item.id}, function(data){
item = data;
});
I have tried with $q and $promise too, but I had no luck with those either. Finding decent examples on those subjects was tough too.
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
Based on the above statement I will give an answer.
First things first, remove the interceptor. You won't need it. Instead use a service.
Write a service called ProcessedItems.
angular.module('app')
.service('ProcessedItems', ['Items', '$q', function(Items, $q){
return {
query: function() {
var defer = $q.defer();
Items.query()
.$promise
.then(function(response){
angular.forEach(response.resource, function(i)){
i.s = 1 + i.s;
if(i.s == 10) {
i = $injector.get("Mine").get({slug: i.id});
i.$promise.then(function(){
defer.resolve(response);
}, function(){
defer.reject();
});
};
};
});
return defer.promise;
}
};
}]);
After this service is set up, in your controller you can do
angular.module('app')
.controller('AppController', ['$scope', 'ProcessedItems',
function($scope, ProcessedItems){
$scope.items = [];
ProcessedItems.query().then(function(pitems){
$scope.items = pitems;
});
});
What this will essentially do is first process the data completely and then display it in the view.

How to access $scope from $resource

I need to splice an array on success. How do I call back out to my $scope.list in order to remove an item from the list array? Is that how I should do it or is there a better way? Like bubbling an "update" or something?
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
var urlBase = 'http://example.com/api/';
function resourceErrorHandler(response) {
console.log("error");
}
function resourceResponseHandler(response) {
console.log($scope.list);
$scope.list.data.splice(index, 1);
console.log("success");
}
phonecatServices.factory('api_resource', ['$resource',
function($resource){
return $resource(urlBase+':api_resource/:id.json', {}, {
query: {method:'GET', isArray:false},
save:{method:'POST', isArray:false},
delete:{method:'DELETE', isArray:false, interceptor:{response:resourceResponseHandler, responseError:resourceErrorHandler}},
});
}]);
Make $scope an argument in the functions and pass it with the method call in the controllers, or bundle it in the response object.

Angular ui-router get asynchronous data with resolve

I want to display a form with data corresponding to the edited item. I use ui-router for routing. I defined a state:
myapp.config(function($stateProvider) {
$stateProvider.
.state('layout.propertyedit', {
url: "/properties/:propertyId",
views : {
"contentView#": {
templateUrl : 'partials/content2.html',
controller: 'PropertyController'
}
}
});
In PropertyController, I want to set $scope.property with data coming from the following call (Google Cloud Endpoints):
gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
I don't know if I can use resolve because the data are returned asynchronously. I tried
resolve: {
propertyData: function() {
return gapi.client.realestate.get(propertyId).execute(function(resp) {
console.log(resp);
});
}
}
First issue, the propertyId is undefined. How do you get the propertyId from the url: "/properties/:propertyId"?
Basically I want to set $scope.property in PropertyController to the resp object returned by the async call.
EDIT:
myapp.controller('PropertyController', function($scope, , $stateParams, $q) {
$scope.property = {};
$scope.create = function(property) {
}
$scope.update = function(property) {
}
function loadData() {
var deferred = $q.defer();
gapi.client.realestate.get({'id': '11'}).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property = deferred.promise;
}
});
You need to read the docs for resolve. Resolve functions are injectable, and you can use $stateParams to get the correct value from your routes, like so:
resolve: {
propertyData: function($stateParams, $q) {
// The gapi.client.realestate object should really be wrapped in an
// injectable service for testability...
var deferred = $q.defer();
gapi.client.realestate.get($stateParams.propertyId).execute(function(r) {
deferred.resolve(r);
});
return deferred.promise;
}
}
Finally, the values for resolve functions are injectable in your controller once resolved:
myapp.controller('PropertyController', function($scope, propertyData) {
$scope.property = propertyData;
});
I think your controller function needs $stateParams parameter from which you can get your propertyId. Then you can use $q parameter and create promise to set $scope.property with something like this:
var deferred = $q.defer();
gapi.client.realestate.get(propertyId).execute(function(resp) {
deferred.resolve(resp);
});
$scope.property=deferred.promise;
Here is description of using promises for handling async calls.
Try this easy way to use resolve in proper way
State code:
.state('yourstate', {
url: '/demo/action/:id',
templateUrl: './view/demo.html',
resolve:{
actionData: function(actionData, $q, $stateParams, $http){
return actionData.actionDataJson($stateParams.id);
}
},
controller: "DemoController",
controllerAs : "DemoCtrl"
})
In the above code I am sending parameter data which I am sending in the url,For examples if i send like this /demo/action/5
this number 5 will go to actionData service that service retrieve some json data based on id.Finally that data will store into actionData You can use that in your controller directly by using that name
Following code return some JSON data based on id which iam passing at state level
(function retriveDemoJsonData(){
angular.module('yourModuleName').factory('actionData', function ($q, $http) {
var data={};
data.actionDataJson = function(id){
//The original business logic will apply based on URL Param ID
var defObj = $q.defer();
$http.get('demodata.json')
.then(function(res){
defObj.resolve(res.data[0]);
});
return defObj.promise;
}
return data;
});
})();
How about this:
function PropertyController($scope, $stateParams) {
gapi.client.realestate.get($stateParams.propertyId).execute(function(resp) {
$scope.property = resp;
});
}

Can't pass query to $resource service

I have a controller querying a PHP script (via a $resource service). The PHP returns JSON. The controller uses the input from ng-model called "userUsername". I can see that nothing is getting sent to the PHP file. I'm getting returned this in the console:
h {usernameTyped: null, $get: function, $save: function, $query: function, $remove: function…}
app.js
var userAppModule = angular.module("userApp", ["ngResource"]);
userAppModule.factory("User", function($resource) {
return $resource("php/login-process.php/:username", {}, {
query: {method: "GET"}
});
});
controllers.js
function LoginCtrl($scope, $route, $routeParams, $location, User) {
$scope.loginUser = function() {
$scope.userQuery = User.query({username: $scope.userUsername}, function(res) {
console.log(res);
});
}
}
I think I've probably got something wrong in the controller function.
You're almost right: the problem is the callback. the first paramater, res in your case, is actually the resource object.
You don't need that callback in this case, as ngResource is designed to be able to work without callbacks in the common case:
function LoginCtrl($scope, $route, $routeParams, $location, User) {
$scope.loginUser = function() {
$scope.user = User.query({username: $scope.userUsername});
// Just throwing a watch in here to see what happens
// to $scope.user
$scope.$watch('user', function(newValue, oldValue) {
console.log("user:", newValue, oldValue);
});
}
}
You'll see one entry show up immediately in console, that's the empty data that the $resource will assign to your scope object.
When the data eventually comes back from the server, it will be assigned to the $scope.user variable, and you should see a second entry to the console with the data from the server response.

Resources