I have a very simple address application in AngularJS. It connects to an API using $resource.
The ID I use is the mobilephone number of the person (I know it's not the best way to do it, but it's just an sample application to show a 3-tier application setup) So I've 2 pages with the same form:
The problem i'm facing is that it uses the same $resource for saving a new address and saving an edited address. When iḿ saving a new address it has to use the url http://127.0.0.1:5000/api/contacts/ without an id appended on it (the new ID it will get on the api/database side is the filled in mobile phone number)
When I edit an existing address and click the save button it has to use another url; http://127.0.0.1:5000/api/contacts/#mobilePhone.
So i've read the angular documentation on https://docs.angularjs.org/api/ngResource/service/$resource which states that you can override your paramDefaults in your actions. So that is what I try to do using this code:
$resource('http://127.0.0.1:5000/api/contacts/:id',{id:'#mobilePhone'},{
get: {
method: 'GET'
}, update: {
method: 'PUT'
}, save: {
method: 'POST',
id:''
}
},{});
which seems to be correct given the information. It appends the mobilePhone number on every GET and every PUT the get and update method respectively. When it calls the save method it should override the :id with an empty string, but it doesn't do that.
Clearly i'm doing something wrong.
If you need more code snipets let me know, I tried to keep it as clean as possible.
Update:
This is how I call the save method:
....
.controller('MovieCreateController',function($scope,$state,$stateParams,Movie){
$scope.movie=new Movie();
$scope.addMovie=function(){
$scope.movie.$save(function(){
$state.go('movies');
});
}
}
And this is the edit method:
....
.controller('MovieEditController',function($scope,$state,$stateParams,Movie){
$scope.updateMovie=function(){
$scope.movie.$update(function(){
$state.go('movies');
});
};
$scope.loadMovie=function(){
$scope.movie=Movie.get({id:$stateParams.id});
};
$scope.loadMovie();
});
There is not much code in your question, so I'll try to explain what you should do to use $resource.
Have a look at following code:
// The $resource service is a helper to create a 'constructor' function
// Contact below is a function
var Contact = $resource('http://127.0.0.1:5000/api/contact/:id',{id:'#mobilePhone'}, {
get: {
method: 'GET' // You don't need to override the GET
}, update: {
method: 'PUT'
}, save: {
method: 'POST'
}});
// Be sure to create an 'entity' from the Contact $resource
// The 'new' will create a $resource instance with $update, $save methods overridden methods
var contact = new Contact({name: 'foobar'});
contact.$save(); // Will send a POST request
contact.mobilePhone = 2; // This is your id !
contact.$update(); // Will send a PUT request
If your resources always have a RESTful representation, I suggest that you use, as per suggested at angular discussion on issue#9807:
resource.prototype.$save = function() {
if (this.id) {
return this.$update();
} else {
return this.$create();
}
};
..rather that always overriding your $resource methods.
This is how you need to call your save method
$scope.movie=new Movie();
$scope.addMovie=function(){
$scope.movie.$save(function(){
$state.go('movies');
});
}
This is how you need to call your edit method
$scope.movie=new Movie();
$scope.updateMovie=function(){
$scope.movie.$update({ "mobilePhone": $stateParams.id},function(){
$state.go('movies');
});
};
For that you need to create a factory for $resource.which is always recommendable
Try this
.factory('movie', ['$resource', function ($resource) {
return $resource('http://127.0.0.1:5000/api/contacts/:id',{id:'#mobilePhone'},{}, {
update: { method: 'PUT' },
query: {
method: 'GET',
isArray: false
}
})
}])
Inject your factory in your controller
.controller('MovieCreateController',['$scope','$state','$stateParams','movie',function($scope,$state,$stateParams,Movie){
}]);
Related
JSON/XML from REST
{
litm: "T00000245",
lotn: "00004"
}
<jdeSerials>
<litm>T00000245</litm>
<lotn>00004</lotn>
</jdeSerials>
AngularJS controller
//Searching a product with serial number/LOTN
$scope.searchProduct = function () {
var lotn = $scope.jdeSerials.lotn;
console.log("searchProduct---->" + lotn);//log-->searchProduct---->00004
$scope.JdeSerials = lotnService.get({id: lotn}, function() {
console.log($scope.jdeSerials);//log-->[object Object]
console.log($scope.jdeSerials.litm);//log-->undefined!!!!!
});
//var litm = $scope.jdeSerials.litm;
//$scope.jdeproduct = productService.get({id: litm});
};
AngularJS service
angular.module('lotnService', ['ngResource'])
.factory('lotnService', ['$resource',
function ($resource) {
console.log('------lotmService-----');
return $resource(
'http://localhost:8080/RMAServer/webresources/com.pako.entity.jdeserials/:id',
{},
{
update: { method: 'PUT', params: {id: '#lotn'} }
});
}]);
Question
How can I get a value to $scope.jdeSerials.litm? Is there any better idea to solve this like creating a service which handles this two GETs? I think that reason is the GET method is asynchronous, but what is the best solution to handle situations like this?
EDIT/update
I changed the service call like this:
$scope.JdeSerials = lotnService.get({id:lotn})
.$promise.then(function(jdeSerials) {
$scope.jdeSerials = jdeSerials;
console.log("1--------------->LITM:"+$scope.jdeSerials.litm);
});
I got the LITM, BUT I got the errormessage as well:
TypeError: Cannot read property 'then' of undefined
Try to create a get method in your resource.
angular.module('lotnService', ['ngResource'])
.factory('lotnService', ['$resource', function ($resource) {
return $resource( 'http://localhost:8080/RMAServer/webresources/com.pako.entity.jdeserials/:id',
{},
{
get: { method: 'GET', params: {id: '#lotn'}},
update: { method: 'PUT', params: {id: '#lotn'} }
});
}]);
Then in your controller, call method get from service:
lotnService.get({id:lotn}).$promise.then(
function(jdeSerials) {
$scope.jdeSerials = jdeSerials;
console.log("1--------------->LITM:"+$scope.jdeSerials.litm);
});
What angular js version are you using?
Does the following work ?
lotnService.get({id:lotn}).then(
function(jdeSerials) { ... }
);
(without the $promise)
I was browsing the docs and also angular-resource.js source for previous versions and it appears that the synthax has changed somewhere along the line.
On angular-resource.js 1.2.0 source:
The Resource instances and collection have these additional properties:
$promise: the {#link ng.$q promise} of the
original server interaction that created this * instance or
collection.
On 1.0.8 there is no mention of the $promise propery, however.
I am trying to develop my application in Laravel 4 and Angular JS, my application allows user to retrieve their Information through the system via Text Change.
Angular is used to pass data input from the user to Laravel which in turn retrieves the Information from the Database.
However Laravel is unable to retrieve the data passed from Angular.
View
<div data-ng-controller="ReservationController">
<input id='ERI' type='text' data-ng-model="scanRID" data-ng-change="queryRes()" name='exampleInput' maxlength='3' />
</div>
Angular Factory
app.factory('exampleFactory', function($http) {
var factory = {};
factory.getExample = function(scanRID) {
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param(scanRID)
});
};
return factory;
});
Angular Controller
app.controller('exampleController', function($scope, $http, exampleFactory) {
$scope.queryRes = function() {
if($scope.scanRID.length == 3) {
exampleFactory.getExample($scope.scanRID)
.success(function (data) {
// Do Something Here
})
.error(function (data) {
console.log(data);
});
}
};
});
Laravel 4 Routes
Route::get('Example', 'ExampleController#show');
Laravel 4 ExampleController
class ExampleController extends \BaseController {
public function show()
{
$id = Input::get('scanRID'); // This here might be wrong. It's always empty!
$data = ExampleModel::find($id); // Able to query and retrieve.
return Response::JSON($data); // Returns empty.
}
}
Laravel 4 ExampleModel
class ExampleModel extends Eloquent {
// The id of this table is what I want, but I can't retrieve it.
protected $fillable = ['id', 'ExampleData1', 'ExampleData2'];
protected $table = 'exampleTable';
}
I have searched everywhere for a solution, it seems that everyone is able to successfully make the Ajax call. I think there is something that I am missing out that I am unaware about.
I have also tried setting CSRF Token, but however, I do not think that is an issue. So my last resort is to turn to the experts and hope someone is able to help me.
On a side note, I am fairly new to Laravel and Angular, so if you do post a solution, please explain to me the issue as I would like to learn more about Angular and Laravel.
Thank you for reviewing my issue.
You are not passing the value of scanRID by scanRID parameter instead pass only the value without parameter. So you are try to get the value from scanRID using Input::get('scanRID'); but without having scanRID parameter. that should be the case ur not getting the value :)
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param({scanRID:scanRID}) //Change Here
});
OR
return $http({
method: "GET",
url: LARAVEL_CONTROLLER + 'Example',
params: {scanRID:scanRID} //Change Here
);
change like this
Just starting out on AngularJS & js (being a backend developer) and this is the first Javascript MVC framework which I have ever worked on so please go easy...
I have a restful json API which I would like to consume using AngularJS.
In order to implement this, I have created two factories, one singular for when a controller needs to query the factory with an id, and a plural factory for when id is not required.
e.g. below:
Controller to list all products:
app.controller('ProductController', function($scope, ProductsFactory) {
$scope.products = [];
init();
function init()
{
$scope.products = ProductsFactory.query();
}
});
Factories:
app.factory('ProductsFactory', function($resource) {
return $resource('/xxx/xxx/xxx/api/v1/product', {}, {
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
});
});
app.factory('ProductFactory', function($resource) {
return $resource('/xxx/xxx/xxx/api/v1/product/:id', {}, {
show: { method: 'GET' },
update: { method: 'PUT', params: { id: '#id' } },
delete: { method: 'DELETE', params: { id: '#id' } }
});
});
Question 1: Am I implementing the resource factory correctly?
Question 2: Is there a way of just using one factory for Products? e.g. allowing the factory to decide that id is not necessary for query and create, but is required for show, update and delete methods?
Yes, you can use just 1 factory :
app.factory('ProductFactory', function($resource) {
return $resource('/xxx/xxx/xxx/api/v1/product/:id', { id : '#id'}, {
update: { method: 'PUT' },
});
});
You don't need to specify the 'params' object to every requests. Defining it for the $resource is often enough (but if you want to overwrite it of course you can use it).
Notice that you don't need to define the CRUD methods since the $resource already implements them (as stated in the doc : http://docs.angularjs.or/api/ngResource.$resource).
At the moment, you just need to define a custom method update if you want to use "PUT" but a pull request is opened (https://github.com/angular/angular.js/pull/3416) and I hope we can expect to see it implemented soon.
I am trying to use ngResource to consume a firebase rest API. This API uses slightly different URLs for retrieving collections vs individual records. For example, I have a collection that I access with:
https://angular-resource-test.firebaseIO.com/systems.json
But if I want to access an individual system I use:
https://angular-resource-test.firebaseIO.com/systems/1.json
How do I specify a parameter-based URL for this in the resource declaration?
Normally I would do something like
app.factory('System', ['$resource', function($resource) {
var System = $resource(
'https://angular-resource-test.firebaseIO.com/systems/:id.json',
{id: "#id"},
{update: {method: "PUT"}}
);
return System;
}]);
But that fails for the collection because it lacks the trailing .json. Alternatively, when I specify a URL that works for the collection, the case of selecting individual rows fails. See this jsfiddle for an example:
http://jsfiddle.net/D5E6w/15/
There are 2 methods that send an HTTP GET in a resource.
get : Expect to receive an object
query : Expect to receive an array
Your api isn't very restful and because of this, you will need 2 resources to do what you want since they use different URI (see Carl's answer). I dont know if you can edit your REST service, but the good way to do it would be :
https://angular-resource-test.firebaseIO.com/systems/ for a query (expect an array)
https://angular-resource-test.firebaseIO.com/systems/:id for a get. (expect an object)
With this service, you could use your resource :
var System = $resource(
'https://angular-resource-test.firebaseIO.com/systems/:id',
{id: "#id"},
{update: {method: "PUT"}}
);
You would do your calls like this :
var systems = System.query({});
var system = System.get({id:1});
Your problem should be solved by using isArray: true for your query and get methods for System and SystemType. It has to do with how your json is formatted on the server-side. See angular docs for expanded discussion. http://jsfiddle.net/D5E6w/24/
{
update: { method: "PUT" },
query: { method: "GET", isArray: true },
get: { method: "GET", isArray: true }
}
Here is a working example with both a collection of systems and a single record. Note the isArray value for each. http://jsfiddle.net/D5E6w/51/
here is my solution with replacing the entire $resource URL. I needed that because I am using HAL Rest response and when paginating for instance, I wanted to replace the entire URL and not only params in it.
app.factory('$rest', ['$resource', 'HALParser', function($resource, HALParser) {
return function($url) {
$url = ($url == null) ? 'http://localhost:8000/:type' : $url;
return $resource($url, {type: ''}, {
update: { method:'PUT' },
get : {
method: 'GET',
transformResponse: [function(data) {
return (new HALParser()).parse(angular.fromJson(data));
}]
}
});
}
}]);
and then,
app.controller('VendorsController', ['$scope', '$rest',
function($scope, $rest) {
$scope.data = $rest().get({type: 'vendors'});
$scope.create = function() {
$rest().save({type: 'vendors'}, {name: $scope.item});
};
$scope.load = function($item) {
console.log($item);
};
$scope.page = function($url) {
$scope.data = $rest($url).get();
};
}]);
I simply wrapped $resource return from my service info a function with an argument URL.
New to AngularJS and trying to get a grasp of the framework, and trying to build a basic CRUD app. I can't seem to figure out what is needed to Update an existing record. Here is my service:
angular.module('appServices', ['ngResource']).
factory('App', function ($resource) {
var Item = $resource('App/:AppId', {
//Default parameters
AppId: '#id'
}, {
//Actions
query: {
method: 'GET',
isArray: true
},
getById: {
method: 'PUT'
},
update: {
method: 'POST'
}
});
return Item;
});
I can run a basic Get all query, and getById to populate an edit form, but that's where I'm stuck. Here is example code for getById
$scope.apps = App.query();
$scope.getEdit = function(AppId) {
App.getById({id:AppId}, function(app) {
$scope.original = app;
$scope.app = new App(app);
});
};
$scope.save = function() {
//What type of information should go here?
//Do I need to make changes to the appServices?
};
I guess, I'm just not sure what's next concerning Updating existing information, or how the "app" object gets passed to the API, can anyone point me in the right direction, or show me a quick update method?
This is a really messy way of handling save operations in angular. For one - you should not be using PUT operations for retrieval requests and secondly - all of this is already built-in to angular. See below.
var Item = $resource( 'App/Details/:AppId', { AppId: '#id' } );
var item = Item.get({ id: 1 }, function( data ) {
data.setAnothervalue = 'fake value';
data.$save();
);
What I'm doing here is retrieving an "Item" and then immediately saving it with new data once it's returned.
Angular JS provides a stack of defaults already, including query, save, remove/delete, get.etc. And for most RESTful APIs, you really shouldn't need to add much, if anything at all. See the resource docs for more information, particularly the information on defaults: http://docs.angularjs.org/api/ngResource.$resource
Additionally, once you get a handle on that - you may want to use $save for both create/update operations, but using POST/PUT (RESTful conventions). If you do, see my article that I wrote about not too long ago: http://kirkbushell.me/angular-js-using-ng-resource-in-a-more-restful-manner/
After doing a bit more research, and reviewing Daniel's link (thanks). I got it working.
Controller method:
$scope.save = function() {
$scope.app.update();
};
Service Factory:
var Item = $resource('App/Details/:AppId', {
//Default parameters
AppId: '#id'
}, {
//Actions
query: {
method: 'GET',
isArray: true
},
getById: {
method: 'PUT'
},
update: {
method: 'POST'
}
});
Item.prototype.update = function (cb) {
console.log(this.AppId);
return Item.update({ AppId: this.AppId },
angular.extend({}, this, { AppId: undefined }), cb);
};
return Item;