mgonto / restangular

AngularJS service to handle Rest API Restful Resources properly and easily
MIT License
7.87k stars 840 forks source link

Using restangular with a service returning JSON metadata containing a null value causes TypeError exception. #195

Closed BCkwilliams closed 11 years ago

BCkwilliams commented 11 years ago

TypeError: Cannot set property 'restangularRoute' of null at restangularizeBase (http://localhost:3000/assets/restangular.js?body=1:444:56) at restangularizeElem (http://localhost:3000/assets/restangular.js?body=1:543:35) at http://localhost:3000/assets/restangular.js?body=1:607:38 at .map..collect (http://localhost:3000/assets/underscore.js?body=1:100:29) at .each..forEach (http://localhost:3000/assets/underscore.js?body=1:87:24) at Function..map..collect (http://localhost:3000/assets/underscore.js?body=1:99:5) at $get.__this (http://localhost:3000/assets/restangular.js?body=1:604:45) at deferred.promise.then.wrappedCallback (http://localhost:3000/assets/angular.js?body=1:6847:59) at ref.then (http://localhost:3000/assets/angular.js?body=1:6884:26) at Object.$get.Scope.$eval (http://localhost:3000/assets/angular.js?body=1:8058:28) angular.js?body=1:5755

my javascript code includes the ResponseExtractor as: angular.module('myApp', ['restangular']) .config(function(RestangularProvider) { RestangularProvider.setBaseUrl("/services"); RestangularProvider.setListTypeIsArray(false); RestangularProvider.setRestangularFields({ id: "_id", route: "restangularRoute" }); RestangularProvider.setResponseExtractor(function(response) { var newResponse = response; if (angular.isArray(response)) { angular.forEach(newResponse, function(value, key) { newResponse[key].originalElement = angular.copy(value); }); } else { console.log(response); newResponse.originalElement = angular.copy(response); } return newResponse; }); });

The json data being returned is:

{"name":null,"internal":true,"description":"BC internal authenticate user (get BC Token)","base_url":"http://kong-rw.qanet.local/maitred/private","operations":{"authenticate":{"verb":"POST","urls":["user/authenticate"],"requires":[{"need":"EACH","options":[{"default":"{\"email_address\": \"$1\", \"password\": \"$2\", \"token_type\": \"CORPLOGIN\"}","location":"PARAMETER","description":"Login information","variables":[{"description":"Email address","tag":"BC_USER","location":"STRING_ENTRY"},{"description":"Password","tag":"BC_USER_PASSWORD","location":"STRING_ENTRY"}],"type":"SUBSTITUTION"}],"description":"Login data","type":"ALWAYS"},{"need":"INVISIBLE","options":[{"default":"Content-Type: application/json","location":"HEADER","description":"Content header","variables":null,"type":"CONSTANT"}],"description":"JSon format","type":"ALWAYS"}],"results":[{"tag":"BC_TOKEN","locator":"token","type":"SINGLE"}]}}}

The JSON spec specifically allows null to be returned as a value.

I've had luck by patching restangular.js code with the commented out lines, which successfully handle the json string, but would like a real fix. var processedData = _.map(data, function(elem) { // if (elem) { if (!this[config.restangularFields.restangularCollection]) { return restangularizeElem(this, elem, what); } else { return restangularizeElem(this[config.restangularFields.parentResource], elem, this[config.restangularFields.route]); } // } else { // return null; // } });

Thank you for your attention and your nice product! Keith

mgonto commented 11 years ago

Hey,

First of all thanks for using the library.

From the code that you're showing me, I think the problem is that you're doing a getList on an object, and the map is therefore iterating over the properties instead of a collection.

For the getList, it's important that you either query an Object, or that an Object is returned in setResponseExtractor. In that case, it'll work OK for you.

mgonto commented 11 years ago

If what you need isn't an array, you need not to call getList

BCkwilliams commented 11 years ago

Thanks - never saw an example of the get. That works fine.