This question already has answers here:
Angular - extending $resource subobject with custom methods
(4 answers)
add a custom function on angular $resource [duplicate]
(3 answers)
Closed 7 years ago.
I'd like to get objects from a server, something like:
var User = $resource('/user/:userId', {userId:'#id'});
I'd like User instances to all have certain methods to calculate derived data, without the server having to return it. For example, say for:
var aUser = User.get({userId: 43});
The server returns something like:
{id: 43, name: "Bob", alertTimestamp: 1447365544}
I'd like to be able to do something like:
if (aUser.alertTimePassed()) {
// do stuff
}
Is there a clean way to do this short of something like this, which seems hacky?
var alertTimePassed = function () {
var now = (new Date()).getTime() / 1000;
return now >= this.alertTimestamp;
};
var User = $resource('/user/:userId', {userId: '#id'}, {
get: {
method: "GET", url: '/user/:userId',
transformResponse: [angular.fromJson, function (obj) {
obj.alertTimePassed = alertTimePassed;
}]
}
});
If you create your $resource using a factory you can modify the $resource object before returning it from the factory.
app.factory('User', function ($resource) {
var User = $resource('/user/:userId', {userId: '#id'});
// add any methods here using prototype
User.prototype.alertTimePassed = function() {
// do stuff
};
return User;
});
Related
I'm new to angular so I apologise is this is a dumb question. I have a WebAPI method that creates data. I would like to return an integer from this method that indicates the new resource's id. If I return the whole object it works in my angular controller, yet when I only return the int I get an object with a prototype and I can't seem to get the id. Fiddler shows my integer value in the TextView, so the answer is there, its just a matter of reading it with angular (javascript).
Any suggestions will be appreciated.
My service looks like this:
(function () {
'use strict';
var app = angular.module('app');
app.factory('itemSvc', function ($resource) {
return $resource("api/item",
{},
{
createNew:
{
method: 'POST'
}
});
})
})();
and I call it like this:
itemSvc.createNew($scope.newItem).$promise.then(
function (item) {
var y = item;
$scope.items.push(item);
},
function (error) {})
You can create an instance of the resource, save it and obtain its id:
var newItem = new itemSvc($scope.newItem);
newItem.$save();
$scope.items.push(newItem.id);
I have an AngularJS factory method that is setting properties like this (specifically in the getPageInformation method):
function factoryMethod($http, $q) {
return function(id) {
return {
id: id,
getPageInformation: function() {
var deferred = $q.defer();
$http({
method: "post",
url: 'getInfo.php',
data: {
id: this.id
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function(successResponse) {
console.log(successResponse);
deferred.resolve('Fetched');
for (var attr in successResponse.data) {
this[attr] = successResponse.data[attr];
}
}, function(errorResponse) {
console.log(errorResponse);
deferred.reject('Unable to fetch');
});
return deferred.promise;
}
}
}
}
The problem is not with AngularJS, as you can see I am returning and object (from the curly braces syntax), but I need to be able to dynamically set the object properties. I did a loop through the appropriate info I got from the response, but it isn't setting it:
for (var attr in successResponse.data) {
this[attr] = successResponse.data[attr];
}
Instead, I think the problem is that (when I console.loged this it brought me the entire browser window object), this is failing to refer to the current instance. Is there any I can achieve what I'm doing or is this how it should be working and there is another problem?
The meaning of the this variable in Javascript is a pain. You can try searching online for the gory details. For this question I believe it suffices to say that this in callbacks is most probably not what you intuitively expect. You need to create a closure with the real this, commonly done as:
return {
...
getPageInformation: function() {
var self = this; // The closed variable here
...
$http({...})
.then(function(successResponse) {
...
for (var attr in successResponse.data) {
self[attr] = successResponse.data[attr]; // Use like this
}
...
This question already has answers here:
Can one AngularJS controller call another?
(14 answers)
Closed 9 years ago.
How do I store the items I've selected in a checkbox with other controllers?
My attempt (see the plnkr for views):
script.js (controllers)
var myApp = angular.module('myApp', []);
myApp.factory('CooSelection', function () {
return {selectedCoo: []}
})
function CooListCtrl($scope, CooSelection) {
$scope.coos = {"Coos": ["spark", "nark", "hark", "quark"]};
$scope.coo_list_selection = CooSelection;
$scope.checkSelection = function (item) {
if ($scope.coo_list_selection.indexOf(item) === -1) {
$scope.coo_list_selection.push(item);
} else {
$scope.coo_list_selection.splice($scope.coo_list_selection.lastIndexOf(item), 1);
}
}
}
CooListCtrl.$inject = ['$scope', 'CooSelection'];
function DebugCooList($scope, CooSelection) {
$scope.coo_selection = CooSelection;
}
DebugCooList.$inject = ['$scope', 'CooSelection'];
When you reference the CooSelection service, you are expecting an array, but the factory returns an object. You could do this instead:
myApp.factory('CooSelection', function () {
return []; // Return an array instead of an object.
})
Also, in your DebugCooList controller, your scope property does not match the name of the variable you are checking in the view. The controller code assigns to coo_selection but the view checks coo_list_selection, so you'll need to change one to match the other.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
How to return AJAX response Text? [duplicate]
(2 answers)
Closed 8 years ago.
initialize: function() {
var store = {};
var item = {};
var me = this;
Ext.Ajax.request({
url: "some_valid_url",
success: function(response) {
try {
var parser = new DOMParser();
var xml = parser.parseFromString(response.responseText, "text/xml");
store = Ext.create('Ext.data.Store', {
autoLoad: true,
fields: ['temp', 'low', 'high', 'desc', 'icon'],
data: xml,
proxy: {
type: 'memory',
reader: {
type: 'xml',
rootProperty: 'current',
record: 'day'
}
}
});
item = Ext.create("Ext.Container", {
var bla = "hello world",
})
} catch (err) {
//err
}
}
});
}
console.log("STORE AND ITEM");
console.log(item);
console.log(store);
Why item and store give back null objects?
However I can see that it's parsing some data, as if I put console.log in between store and item elements I will get a valid element.
STORE AND ITEM
Object {} ArticleWeatherList.js:105
Object {} ArticleWeatherList.js:106
Ajax is asynchronous which basically means that the code continues executing without waiting for the request to complete. Therefore, when you are trying to use item and store, the request has not yet completed and the success method which sets them has not yet run. The only ways round this are to force the request to happen synchronously (although this is generally a bad idea) or to put all the code that depends on the request into the success callback or functions that are called from within the success callback.
Because,
you have declared store and item as local variables inside intialize function. If you want access these variable outisde you must declare them in the global not in the function. The same can be achived omitting the var keywords in the declaration of the variables.
initialize: function () {
store = {};
item = {};
var me = this;
....
this should works but is a considered a bad practice.
I have a resource that returns an array from a query, like so:
.factory('Books', function($resource){
var Books = $resource('/authors/:authorId/books');
return Books;
})
Is it possible to add prototype methods to the array returned from this query? (Note, not to array.prototype).
For example, I'd like to add methods such as hasBookWithTitle(title) to the collection.
The suggestion from ricick is a good one, but if you want to actually have a method on the array that returns, you will have a harder time doing that. Basically what you need to do is create a bit of a wrapper around $resource and its instances. The problem you run into is this line of code from angular-resource.js:
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
This is where the return value from $resource is set up. What happens is "value" is populated and returned while the ajax request is being executed. When the ajax request is completed, the value is returned into "value" above, but by reference (using the angular.copy() method). Each element of the array (for a method like query()) will be an instance of the resource you are operating on.
So a way you could extend this functionality would be something like this (non-tested code, so will probably not work without some adjustments):
var myModule = angular.module('myModule', ['ngResource']);
myModule.factory('Book', function($resource) {
var service = $resource('/authors/:authorId/books'),
origQuery = service.prototype.$query;
service.prototype.$query = function (a1, a2, a3) {
var returnData = origQuery.call(this, a1, a2, a3);
returnData.myCustomMethod = function () {
// Create your custom method here...
return returnData;
}
}
return service;
});
Again, you will have to mess with it a bit, but that's the basic idea.
This is probably a good case for creating a custom service extending resource, and adding utility methods to it, rather than adding methods to the returned values from the default resource service.
var myModule = angular.module('myModule', []);
myModule.factory('Book', function() {
var service = $resource('/authors/:authorId/books');
service.hasBookWithTitle = function(books, title){
//blah blah return true false etc.
}
return service;
});
then
books = Book.list(function(){
//check in the on complete method
var hasBook = Book.hasBookWithTitle(books, 'someTitle');
})
Looking at the code in angular-resource.js (at least for the 1.0.x series) it doesn't appear that you can add in a callback for any sort of default behavior (and this seems like the correct design to me).
If you're just using the value in a single controller, you can pass in a callback whenever you invoke query on the resource:
var books = Book.query(function(data) {
data.hasBookWithTitle = function (title) { ... };
]);
Alternatively, you can create a service which decorates the Books resource, forwards all of the calls to get/query/save/etc., and decorates the array with your method. Example plunk here: http://plnkr.co/edit/NJkPcsuraxesyhxlJ8lg
app.factory("Books",
function ($resource) {
var self = this;
var resource = $resource("sample.json");
return {
get: function(id) { return resource.get(id); },
// implement whatever else you need, save, delete etc.
query: function() {
return resource.query(
function(data) { // success callback
data.hasBookWithTitle = function(title) {
for (var i = 0; i < data.length; i++) {
if (title === data[i].title) {
return true;
}
}
return false;
};
},
function(data, response) { /* optional error callback */}
);
}
};
}
);
Thirdly, and I think this is better but it depends on your requirements, you can just take the functional approach and put the hasBookWithTitle function on your controller, or if the logic needs to be shared, in a utilities service.