The success function of a $http.put doesn't have access to the this scope of the service it's being called inside. I need to update a property of the service in the call back from the PUT request.
This is a cut down example of what I'm trying to do in a service:
var myApp = angular.module('myApp', function($routeProvider) {
// route provider stuff
}).service('CatalogueService', function($rootScope, $http) {
// create an array as part of my catalogue
this.items = [];
// make a call to get some data for the catalogue
this.add = function(id) {
$http.put(
$rootScope.apiURL,
{id:id}
).success(function(data,status,headers,config) {
// on success push the data to the catalogue
// when I try to access "this" - it treats it as the window
this.items.push(data);
}).success(function(data,status,headers,config) {
alert(data);
});
}
}
Sorry if there are some errors in the JS, the main point is how do I access the service scope from inside the success callback?
EDIT : while the answer to this question was correct, I switched to the factory method as both Josh and Mark recommended it
As far as I know, you can't. But I wouldn't try to run the service that way anyway. Here is a cleaner way:
.factory('CatalogueService', function($rootScope, $http) {
// We first define a private API for our service.
// Private vars.
var items = [];
// Private methods.
function add( id ) {
$http.put( $rootScope.apiURL, {id:id} )
.success(function(data,status,headers,config) { items.push(data); })
.then(function(response) { console.log(response.data); });
}
function store( obj ) {
// do stuff
}
function remove( obj ) {
// do stuff
}
// We now return a public API for our service.
return {
add: add,
store: store,
rm: remove
};
};
This is a very common pattern of developing services in AngularJS and it doesn't require any use of this in these cases.
Create a closure over a variable (often called that) that is assigned to this so that your callback functions will have access to your service object:
app.service('CatalogueService', function($rootScope, $http) {
var that = this;
...
).success(function(data,status,headers,config) {
that.items.push(data);
Here is a Plunker that uses $timeout instead of $http to demonstrate.
Related
I implemented a hook function, where I attach some createdAt and updatedAt fields to the doc that is inserted to a collection. I can attach this to any collection like this:
export const insertHook = function (doc) {
try {
const user = Meteor.user();
doc.createdBy = user && user._id ? user._id : null;
doc.createdAt = new Date().getTime();
} catch (e) {
console.err(e);
}
};
Attaching the hook to the collection is basically passing it via a third option in the constructor:
class HookedCollection extends Mongo.Collection {
constructor(name, options, hooks={}) {
super(name, options);
this.insertHook = hooks.insertHook;
}
insert(doc, callback) {
if (this.insertHook && Meteor.isServer)
this.insertHook.call(this, doc);
}
}
export const MyDocs = new HookedCollection("mydocs", {}, {insertHook});
In a Meteor method I just do a normal insert:
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc);
}
});
Which creates basically the following error:
Error: Meteor.userId can only be invoked in method calls or publications.
I tried several ways of bind but always ended up in this error. Is there really no way at all to bind the userId to the function?
According to Meteor docs Meteor.userId() is available anywhere but publish functions (Server side Publish function).
You aren't using Meteor.userId() directly in the method but in a callback (see discussion in this github issue). You can pass the userId information to your callback function as a parameter from the method, for example:
// Using Meteor.userId()
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc, Meteor.userId());
}
});
// Or using this.userId
Meteor.methods({
insertDoc:function(doc) {
//check doc...
return MyDocs.insert(doc, this.userId());
}
});
As a general rule use Meteor.userId() in the client (that queries the database) and this.userId in the server. More information in this other question Meteor - Why should I use this.userId over Meteor.userId() whenever possible? and in Meteor forums
We are creating an ASP.NET MVC application which uses AngularJS framework. This is a multi-lingual application and resource values in each language are stored in the database.
On the server-side, we load all of our resources from the database into HttpRuntimeCache when the user logs in to the application and use it throughout our views.
We want to create an AngularJS factory service which we can use to access the resources in HttpRuntimeCache.
For example:
User clicks login
ASP.NET MVC Application loads resources into HttpRuntimeCache
User is redirected to root of application (Home/Index).
Angular application is initialized
When user views a page which needs these resources from HttpRuntimeCace, we will get the value via the service
When this service is called, it will first check if it has already loaded the resource dictionary
If it has not yet loaded, it will call an MVC action method, which will return the resource dictionary as JSON, and the service will use that response to load the dictionary
If it has already been loaded, it will just return the value from the dictionary based on the key which has been requested
What is the best way to achieve this scenario?
Below is what we have so far. The issue with our current solution is that the service is returning the value before the dictionary has a chance to load.
angular.module("MyApp").factory('cacheService', ['$rootScope', '$http', function ($rootScope, $http, $q) {
var obj = { resourcesLoaded: false };
obj.loadResourceDictionary = function () {
obj.resourcesLoaded = false;
$http.get("/MyApp/Cache/GetResourceDictionary")
.success(function (data) {
obj.resourceDictionary = data;
obj.resourcesLoaded = true;
});
}
obj.getResourceValue = function (resourceKeyName) {
if (!obj.resourcesLoaded) {
obj.loadResourceDictionary();
}
return obj.resourceDictionary[resourceKeyName];
}
return obj;
}]);
EDIT w/ Usage example in Angular controller (we also want to use this service in a directive):
angular.module("MyApp").controller("MyAppCtrl", ['cacheService', function (cacheService) {
var self = this;
self.test = function () {
var value = cacheService.getResourceValue('Err_lbl_UserExist');
}
}]);
<div ng-controller="MyAppCtrl as myAppCtrl">
<button ng-click="myAppCtrl.test()">Test Angular CacheService</button>
</div>
There you go:
angular.module("MyApp").factory('cacheService', ['$rootScope', '$http', function ($rootScope, $http, $q) {
var service = {
obj.getResourceValue = function () {
return $http.get("/MyApp/Cache/GetResourceDictionary")
.success(function (data) {
return data
});
}
}
return service;
}]);
And for your controller method:
angular.module("MyApp").controller("MyAppCtrl", ['cacheService', function (cacheService) {
var self = this;
self.test = function () {
var value = null;
cacheService.getResourceValue().then(function (data){
value = data['Err_lbl_UserExist'];
}
}
}]);
<div ng-controller="MyAppCtrl as myAppCtrl">
<button ng-click="myAppCtrl.test()">Test Angular CacheService</button>
</div>
$http.get is an asynch function , therefore will block until data is received. All you have to do is handle errors which is not implemented.
Context : I am using a Collection Params to call method from the Server to a C app. The C app does its stuff and then calls the server by RPC to send me the results. With the result, I get the Params ID to delete the corresponding element.
With the deletion of the Element of Params, the C app gets a removed message. I want to prevent this behavior to avoid overloading the C app of messages.
I've thinked about implementing the removed event into the Publish method on the server to prevent the server from informing the C app. I just want the C app to be inform about added events.
On the Meteor Doc, there is an example of implementation of added and removed but I don't understand it. Can someone help me ?
I've tried this (don't work at all) :
Meteor.publish('expert_mode_parameters', function ()
{
var self = this;
var handle = Expert_Mode_Parameters.find().observeChanges({
added: function ()
{
return Expert_Mode_Parameters.find();
},
removed: function ()
{
return [];
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
}
It looks like your goal is to subscribe to a data set but only receive added messages, not changed or removed.
The below code should do this:
Meteor.publish('expert_mode_parameters', function () {
var self = this;
var handle = Expert_Mode_Parameters.find().observe({
added: function (document) {
self.added("expert_mode_parameters", document._id, document);
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
}
The concept is, you're watching the results of Expert_Mode_Parameters.find() and then calling self.added(document) when there is a new item. The same thing can easily be expanded to include changed.
I have an angular controller that calls a service. The service is responsible for returning data from a json file.
controller:
function projectController($scope, ajaxServices) {
$scope.projects = ajaxServices.getProjects();
}
service:
projectManagerApp.factory('ajaxServices', function ($http) {
return {
getProjects : function () {
$http.get('projects.json', { data: {} }).success(function (data) {
if (window.console && console.log) {
console.log("objects returned: " + data.length); // shows # of items
}
return data //nothing ng-repeated, no console errors.
})
// Exact same data from json file hard-coded, works fine
// when not commented out.
// return [{ "id": 1, "name": "Project 1 }, { "id": 2, "name": "Project 2" }]
}
}
});
html: ng-repeat="project in projects"
In the success function I can see the data returned in the console log but if I try to return the data the ng-repeat ul element on my page is empty. In the same service if I simply return the same data logged to the console hard coded (outside of the success function, of course it works just fine.
How can I return the data into the ng-repeat using my ajax call?
I'm just as new to Plunker as I am Angular but here is my attempt at a Plunk:
http://plnkr.co/edit/ALa9q6
$http is asynchronous, therefore the call to getProjects will return nothing. Using $q you can receive an instance to a promise which will receive the data when available.
Using $q
Here an example using $q:
http://plnkr.co/edit/U72oJblvrVEgYt2mTmU2?p=preview
Using $resource
Alternatively, you can use $resource especially if your server code is RESTful, which requires adding the following dependency in your scripts:
//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.js
This is a 1st review of your code to use $resource: http://plnkr.co/edit/tLOAaXZHdGgWOok3Sdc8?p=preview
But you can simplify and shrink it more to this:
http://plnkr.co/edit/pKO6k6GxJ1RlO8SNvqUo?p=preview
This is the new app.js file:
angular.module('app', ['ngResource'])
.factory('ProjectsService', ['$resource', function($resource) {
return $resource('projects.json');
}])
.controller('ProjectsController', ['ProjectsService', '$scope', function(ProjectsService, $scope) {
$scope.projects = ProjectsService.query();
}]);
Find more information about $resource here:
http://docs.angularjs.org/api/ngResource.$resource
You need to use $q. Example is here
$http performs asynchronously and may or may not be finished at any given point in time which is why your return statement dont work.
Use $q or simply handle the promise inside the controller:
Service:
projectManagerApp.factory('ajaxServices', function ($http) {
return {
getProjects : function () {
return $http.get('projects.json', { data: {} })
}
}
});
Controller:
function projectController($scope, ajaxServices) {
ajaxServices.getProjects().success(function (data) {
if (window.console && console.log) {
console.log("objects returned: " + data.length);
}
$scope.projects = data
});
}
Plunk
If you want data to be loaded before the page
Is loaded you can use 'resolve' property
For the module.
Have a look in the docs for details.
My question is based on this topic in Angular Google group.
I want to provide a service which stores some basic data retrieved from the backend via $http, then I only need to fetch those data once. like,
var load = function() {
return $http.get(...).then(function(data) {
return data.user;
});
};
module.factory("userProvider", function() {
var user;
var getUser = function() {
if(!user) {
load().then(function(data) {
user = data;
});
}
return user;
};
return {
getUser : getUser
}
});
module.controller("UserController", ["userProvider", function UserController("userProvider") {
var user = userProvider.getUser();
// do something with user
}]);
The problem is that the promise chain ends in userProvider but not in controller, so the user is undefined the first time I use this controller since the data has not been returned.
How can I use such a storage service and return the data correctly? Thanks!
You can just create your own promise. Here is the modified code:
module.factory( "userProvider", function( $q ) {
// A place to hold the user so we only need fetch it once.
var user;
function getUser() {
// If we've already cached it, return that one.
// But return a promise version so it's consistent across invocations
if ( angular.isDefined( user ) ) return $q.when( user );
// Otherwise, let's get it the first time and save it for later.
return whateverFunctionGetsTheUserTheFirstTime()
.then( function( data ) {
user = data;
return user;
});
};
// The public API
return {
getUser: getUser()
};
});
Update: The solution below by #yohairosen is a great one for many circumstances, but not for all. In some circumstances, we would only want to cache the successful result, as I have done here. If, for example, a failure of this request indicates the user needs to log in first, we would not want the next call (presumably after logging in) to deliver the cached failure. In cases where the method isn't necessarily consistent from call-to-call in all circumstances, this method is better; in all other cases, #yohairosen's solution is simpler and recommended.
It's a bit of an overhead to create your own promise, angular's $http creates one for you anyway. What you're looking for is caching and http can handle it for you by passing cache:true to the service call.
So you can simply do something like this:
module.factory("userProvider", function() {
var getUser = function() {
return $http.get(..., {cache:true}).then(function(data) {
return data.user;
});
return {
getUser : getUser
}
});