Angularjs factory http get gives 'undefined is not a function' - javascript

Here is my code. I get an error that says: TypeError: undefined is not a function:
I think the issue is my lack of understanding of promises, but if someone could enlighten me, I'd greatly appreciate it.
getProgramDetails: function (program) {
var _this = this;
this.getDetails(program).then(function() {
....
});
},
getDetails: function (program) {
var _this = this;
var deferred = $q.defer();
// Error occurs at this line
this.http.get({id: program.programID}).then(function(results) {
if (results && results.programID) {
_this.isNewProgram = false;
_this.selectedProgram = {
...
};
} else {
_this.isNewProgram = true;
_this.selectedProgram = {
...
};
}
deferred.resolve();
});
return deferred.promise;
},
http: $resource(
$window.detailsEndpoint',
{ id: '#id' },
{ //parameters default
update: {
method: 'PUT',
params: {}
},
get: {
method: 'GET',
params: {
id: '#id'
}
},
post: {
method: 'POST'
}
})

I don't think $resource has a .then() method like $http instead try pass the callback as the second argument
this.http.get({id: program.programID}, function(results){});
Or call its $promise
this.http.get({id: program.programID}).$promise.then(function(results){});

Related

Passing complex object from angular js factory to web api using $resource

I am passing an object to angular factory it is throwing error.
factory:
visitorApp.factory('loginRepository', function ($resource) {
return {
VerifyVisitor: $resource('/api/VisitorWeb/VerifyLogin', {}, {
query: { method: 'POST', params: {loginModel:loginModel}, isArray: true }
})
};
});
The complex object i am trying to pass is loginModel.
From controller call to factory.
visitorApp.controller('LoginController', function ($scope,$location,$route,loginRepository) {
$scope.submit = function (isValid) {
if (isValid) {
var loginModel = {
UserName: $scope.UserName,
PassWord: $scope.Password
};
var response = loginRepository.VerifyVisitor.query(loginModel);
alert(response);
}
}
});
Error: loginModel is not defined
Web Api Method which is being called.
[HttpPost]
public string VerifyLogin(UserLoginDomainModel loginModel)
{
var msg = _loginService.Login(loginModel);
return msg;
}
Is it the right way of using $resource to post a request and pass complex object.
Your service should look something like this:
visitorApp.factory('loginRepository', function ($resource) {
return {
VerifyVisitor: $resource('/api/VisitorWeb/VerifyLogin', {},
{
query: {
method: 'POST',
params: {loginModel: '#loginModel'},
isArray: true }
})
};
});
The parameter variables are enclosed in quotes and prefixed with an #.

Backbone + Promises - How to fetch after post

I have a model which has two functions - one posts, and one gets.
The fetch:
fetch: function (options) {
var self = this;
return P1Comm.get({
'dataType': 'json',
'processAjaxResponse': self.processAjaxResponse,
'onStatusInvalid': function (e) {
P1.log(e, 'status');
},
'onSuccess': options.success,
'onError': function (e) {
P1.log(e);
},
'sourceURL': P1.API_APPS_ROOT + 'v1.0/accepted-terms'
});
},
The post:
acceptTerms: function (appId) {
var self = this;
self.set({
'app_id': parseInt(appId,10)
});
self.createEdit(self.toJSON(), {
}).pipe(function () {
var def = $.Deferred();
var saveOpts = {
dataType: 'json',
stringify: true,
contentType: 'application/json',
processData: false,
processAjaxResponse: self.processAjaxResponse
};
self.save(self.toJSON(), saveOpts);
return def.promise();
});
},
Calling the post from the view: acceptAppTerms.acceptTerms(1);
These both work in their own right but I want to change this so that:
- Make the POST acceptTerms()
- Then make the GET fetch()
- Then call another function
I think it would be something like this (at least for the first two points):
acceptAppTerms.acceptTerms(id).then(function () {
this.launchApp(event, id);
});
But I get the error of Uncaught TypeError: Cannot read property 'then' of undefined
I am not that good at front-end - can someone point me in the right direction?
Thanks
UPDATE:
fetchAcceptedTerms: function () {
var self = this;
this.appAcceptedTerms = new T1AppAcceptedTerms();
this.acceptedTerms = new AppAcceptedTerms();
this.acceptedTerms.fetch({
success: function (data) {
if (data.meta.status === 'success') {
self.appAcceptedTerms.set(data.data);
}
}
});
},
Example below works but it breaks the setting of the data into the model.
The reason you get the error is because your acceptTerms doesn't return anything, or in other words returns undefined which doesn't have a then() method.
Your code should be something like:
fetch: function(options) {
var self = this;
return P1Comm.get({
dataType: 'json',
processAjaxResponse: self.processAjaxResponse,
onStatusInvalid: function(e) {
P1.log(e, 'status');
},
onSuccess: options.success,
onError: function(e) {
P1.log(e);
},
sourceURL: P1.API_APPS_ROOT + 'v1.0/accepted-terms'
});
},
acceptTerms: function(appId) {
var self = this;
self.set({
'app_id': parseInt(appId, 10)
});
return self.createEdit(self.toJSON(), {}).pipe(function() {
//---------------------------------------^ better use .then()
var saveOpts = {
dataType: 'json',
stringify: true,
contentType: 'application/json',
processData: false,
processAjaxResponse: self.processAjaxResponse
};
return self.save(self.toJSON(), saveOpts);
});
},
This code asummes that P1Comm.get, self.createEdit and self.save returns a promise object. If they don't then you need to create a Deferred object and return it's promise from these functions. And inside the success/error callbacks of their asynchronous actions you need to resolve/reject the promise accordingly.
I'd also like to mention that .pipe() is a deprecated method, you should use then() unless you're using ancient version of jQuery.

Data from $resource not synchronized to call from HTML

I'm able to get the print the array returned inside vm.getProduct() in the controller. But this not available in HTML.
Resource:
var DataProvider = function ($resource) {
return $resource('', {}, {
getProductTypeAhead: {
method: 'GET',
isArray: true,
url: '/productinterface?typeahead=:typeAhead',
params: {typeAhead: '#typeAhead'}
}
});
};
module.exports = ['$resource', DataProvider];
Service:
var DataService = function (DataProvider, $scope) {
this.getProductTypeAhead = function (typeAhead, callback) {
DataProvider.getProductTypeAhead({typeAhead: typeAhead},
function (data) {
callback(data);
}, function (data) {
// NOTIFY THE USER THAT THE REQUEST FAILED
callback(null);
});
};
};
module.exports = ['DataProvider', DataService];
Controller:
vm.getProduct = function ($viewValue) {
return DataService.getProductTypeAhead($viewValue, function (response) {
console.log(response);
return cleanResponse(response);
});
};
function cleanResponse(response) {
return JSON.parse(global.angular.toJson(response));
}
HTML:
<input type="text" class="form-control" id="product"
typeahead-min-length="3"
typeahead="product as product.legalName for product in vm.getProduct($viewValue)"
typeahead-template-url="app/main/templates/typeahead-ProductInterface-Template.html"
ng-model="vm.trade.PRODUCT_NAME">
However, if I go for $http.get() approach, I'm able to see the array in HTML.
vm.getProduct1 = function ($viewValue) {
return $http.get('/productinterface?typeahead=' + $viewValue).then(function (response) {
console.log(response.data);
return response.data;
});
};
I checked for the posts to synchronize $resource calls. No luck in that front.
Any pointers on this issue will be much appreciated.
TIA,
Bhushan
If you wanna use $resource with typeahead you can't use the normal callbacks. You need to return a promise.
Return a promise of a $resource request:
vm.getProduct = function ($viewValue) {
return DataService.getProductTypeAhead($viewValue, function (response) {
return response;
}).$promise;
};
Return a promise of a $resource request with callback;
vm.getProduct = function ($viewValue) {
return DataService.getProductTypeAhead($viewValue)
.$promise.then(function(response) {
return doSomethingWith(response);
});
});
};
See example - Asynchronous results
You are returning a promise not the value. That is vm.getProduct is a funciton that returns a promise not data.
vm.getProduct = function ($viewValue) {
return DataService.getProductTypeAhead($viewValue, function (response) {
console.log(response);
return cleanResponse(response);
});
};
function cleanResponse(response) {
return JSON.parse(global.angular.toJson(response));
}
You should set it to any vm property like this
vm.getProduct = function ($viewValue) {
return DataService.getProductTypeAhead($viewValue, function (response) {
console.log(response);
vm.myProperty = cleanResponse(response);
return cleanResponse(response);
});
};

Understanding the $resource factory and the # prefix

Given the following service:
vdgServices.factory('UserService', ['$resource',
function($resource) {
return $resource('api/users/:id', {}, {
doGet: {
method: 'GET',
params: { id: '#userId' }
},
doPost: {
method: 'POST',
params: { id: '#userId' }
},
doPut: {
method: 'PUT',
params: { id: '#userId' }
},
doDelete: {
method: 'DELETE',
params: { id: '#userId' }
}
});
}]);
I observe the following requested URLs:
var params = { userId: 42 };
var onSuccess = function() { console.log("OK"); };
var onError = function() { console.log("KO"); };
UserService.doGet(params, onSuccess, onError);
// requests api/users?userId=42
UserService.doPost(params, onSuccess, onError);
// requests api/users/42
UserService.doPut(params, onSuccess, onError);
// requests api/users/42
UserService.doDelete(params, onSuccess, onError);
// requests api/users?userId=42
Can anybody explain why the :id URL parameter gets sometimes replaced by 42, sometimes not?
Ideally, I would like it to be replaced for any method, i.e. that the requested URL becomes "api/users/42" everytime.
AngularJS $resource
If the parameter value is prefixed with # then the value of that parameter will be taken from the corresponding key on the data object (useful for non-GET operations).
You have put params in the wrong place, you should implement like this
.factory('UserService', function($resource) {
return $resource('api/users/:id', { id: '#id' }, {
doGet: {
method: 'GET'
},
doPost: {
method: 'POST'
},
doPut: {
method: 'PUT'
},
doDelete: {
method: 'DELETE'
}
});
});
Lets test it
describe('userApp', function () {
var UserService
, $httpBackend
;
beforeEach(function () {
module('userApp');
});
beforeEach(inject(function (_UserService_, _$httpBackend_) {
UserService = _UserService_;
$httpBackend = _$httpBackend_;
}));
describe('User resource - api/users', function () {
it('Calls GET – api/users/{id}', function() {
$httpBackend.expectGET('api/users/42').respond(200);
UserService.doGet({id: 42});
$httpBackend.flush();
});
it('Calls POST - api/users/{id}', function() {
$httpBackend.expectPOST('api/users/42').respond(200);
UserService.doPost({id: 42});
$httpBackend.flush();
});
it('Calls PUT - api/users/{id}', function() {
$httpBackend.expectPUT('api/users/42').respond(200);
UserService.doPut({id: 42});
$httpBackend.flush();
});
it('Calls DELETE - api/users/{id}', function() {
$httpBackend.expectDELETE('api/users/42').respond(200);
UserService.doDelete({id: 42});
$httpBackend.flush();
});
});
});
jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/vbAtL/

AngularJS $resource & cache factory

I have implemented angular $resource with custom functions and parameters as follows:-
.factory('CandidateService', ['$resource', function ($resource) {
return $resource("api/:action/:id", {},
{
'getCandidates': { method: "GET", params: { action: "Candidate" }, isArray: true },
'getCandidate': { method: 'GET', params: { action: "Candidate", id: "#id" } }
});
}]);
And I am consuming this in the controller as follows:-
.controller('Controller', ['CandidateService', function ($scope, CandidateService) {
$scope.candidateList = [];
CandidateService.getAll(function (data) {
$scope.candidateList = data;
});
}]);
This is working absolutely fine. Now I need to cache the data from the api into the CandidateService Factory so it is not loaded eveytime I move between the controllers.
So I thought i would do something as follows:-
.factory('CandidateService', ['$resource', function ($resource) {
var Api = $resource("api/:action/:id", {},
{
'getCandidates': { method: "GET", params: { action: "Candidate" }, isArray: true },
'getCandidate': { method: 'GET', params: { action: "Candidate", id: "#id" } }
});
var candidateDataLoaded = false;
var candidateData = [];
return {
getCandidates: function () {
if (!candidateDataLoaded) {
Api.getAll(function (data) {
angular.copy(data, candidateData);
});
}
return candidateData;
}
}
}]);
But I just cant get this to work. I think it has something to do with angular factory being a singleton.
Is my approach correct to implement the caching?
You can use the $cacheFactory object.
See : http://docs.angularjs.org/api/ng.$cacheFactory
You can cache $http request like that :
var $httpDefaultCache = $cacheFactory.get('$http');
If you want to retrieve a specific url in cache do :
var cachedData = $httpDefaultCache.get('http://myserver.com/foo/bar/123');
$You can clear the cache too :
$httpDefaultCache.remove('http://myserver.com/foo/bar/123');
or :
$httpDefaultCache.removeAll();
Complete post here : Power up $http with caching

Categories

Resources