After i have updated a user in angularjs I will update the $scope.user with the new userdata. One way is to do an new api request to get all users. But i think, its better to update the $scope. I found $apply, but i'am not sure how to use it.
app.controller('UserCtrl', function( $scope, APIService ) {
$scope.users = APIService.query({ route:'users' });
$scope.activate = function( user ) {
var updateUser = {};
if(user.activated) {
updateUser.activated = false;
} else {
updateUser.activated = true;
}
APIService.update({ route:'users', id: user._id }, updateUser, function(updatedUser) {
//update $scope.users with new data from updatedUser;
});
}
});
The updatedUser looks like this:
{"__v":0,"_id":"535aa89d8b2766d012e14c21","activated":true,"role":"user","local":{"surname":"Carter","prename":"Rob","password":"459DS","email":"test"},"$promise":{},"$resolved":true}
the service:
app.service("APIService", function( $resource ) {
return $resource('http://localhost:3000/api/:route/:id', { route: "#route", id: "#id" }, {
'update': { method: 'PUT' }
});
});
If you're using ngRoute use:
$route.reload()
Causes $route service to reload the current route even if $location hasn't changed.
As a result of that, ngView creates new scope, reinstantiates the controller.
and if you're using ui-router:
$state.reload();
However I think a better way would be to recall the $resource call again:
var getUsers=function(){
$scope.users = APIService.query({ route:'users' });
}
You can call it initially like this:
getUsers();
and in the callback of your update:
APIService.update({ route:'users', id: user._id }, updateUser, function(updatedUser) {
getUsers();
});
EDIT
I'm not sure what you mean by "updating the scope"? You mean rerunning the controller again? In that case you're making a fresh API call again, the same thing as just calling the getUsers() method I suggested. If you mean you want to update an array that sits on your $scope with the new data of the user, rather than calling the server to get the entire users again, then in the callback of your update method do something like this:
angular.forEach($scope.users,function(user,key){
if(user._id == updatedUser._id){
$scope.users[key]=updatedUser;
}
})
Related
I am using angularJS localstorage and i injected this very well but problem in code.
I want when localstorage gets new data, so that its call a function $scope.getAllContact() automatically.
I am trying to solve this issue coz, for example, i opened two tab in browser, if i change anything in one tab, the latest change should reflect in other tab too without any reload and refresh.
At first see my code:
app.controller('crudCtrl', function($scope, $http, $timeout, $localStorage, $sessionStorage) {
$scope.getAllContact = function() {
var data = $http.get("http://127.0.0.1:8000/api/v1/contact/")
.then(function(response) {
$scope.contacts = response.data;
// show something if success
}, function(response) {
//show something error
});
};
$scope.getAllContact();
// below method will post data
$scope.formModel = {};
$scope.onSubmit = function () {
$http.post('http://127.0.0.1:8000/api/v1/contact/', $scope.formModel)
.then(function(response) {
$localStorage.name = response.data;
$timeout(function() {
$scope.successPost = '';
}, 4000);
//below $scope will push data in client site if the request is success
$scope.contacts.push(response.data);
//if any error occurs, below function will execute
}, function(response) {
// do nothing for now
});
};
});
above in $scope.onSubmit() methond, i send the submitted data to localstorage too, so i need if localstorage gets new data, it should execute $scope.getAllContact() always automatically without any refresh.
Can anyone fix me this issue?
Use the storage event:
angular.element(window).on("storage", $scope.getAllContact);
$scope.$on("$destroy", function() {
angular.element(window).off("storage", $scope.getAllContact);
});
For information, see
MDN Window API Reference - storage_event
MDN Web API Reference - StorageEvent
I am using Ionic Framework and Firebase is my BaaS.
Controller:
.controller('ProfileCtrl', function($scope, AuthService, DatabaseService) {
console.info('** ProfileCtrl **');
var user = firebase.auth().currentUser;
$scope.public = {};
DatabaseService.getUserPublicInfo(user.uid)
.then(function(infoSnap) {
infoSnap.forEach(function(item) {
$scope.public[item.key] = item.val();
});
});
})
Service:
.service('DatabaseService', function($q) {
this.getUserPublicInfo = function(uid) {
return firebase.database().ref('/users/'+uid+'/public/').once('value');
}
}
In my HTML view I have the following:
<div><h3>{{public.firstname}} {{public.lastname}}</h3></div>
No error and when debugging, $scope.public.firstname as the correct value in it but nothing is displayed.
I have a button in my HTML view ; when I click on it, it changes page but just before page switches, I see the firstname appearing. When I go back to my view, the firstname is well displayed.
I tried to wrap getUserPublicInfo in $scope.$apply() in my controller but I get the "$digest already in progress" error...
Please, help, it's driving me crazy !
Thanks in advance
To resove, "$digest already in progress" error... put $scope .$appy inside timeout service.
DatabaseService.getUserPublicInfo(user.uid)
.then(function(infoSnap) {
infoSnap.forEach(function(item) {
$scope.public[item.key] = item.val();
$timeout(function(){
$scope.$apply()
},1)
});
});
Edit 1: Try this to avoid using $scope.$apply(). I haven't tested it. But it should work.
DatabaseService.getUserPublicInfo(user.uid)
.then(function(infoSnap) {
$scope.updatePublic(infoSnap)
});
});
$scope.updatePublic = function (infoSnap) {
infoSnap.forEach(function(item) {
$scope.public[item.key] = item.val();
})
}
I used $q to create a promise. By doing the initial Firebase promise is resolved within the Angular scope:
this.getUserPublicInfo = function(uid) {
console.info('getUserPublicInfo - get user public information for uid: '+uid);
var deferred = $q.defer();
firebase.database().ref('/users/'+uid+'/public/').once('value')
.then(function(snap) {
deferred.resolve(snap);
})
.catch(function(error) {
deferred.reject(error);
});
return deferred.promise;
}
I have a pretty standard app which will display news items from a remote JSON feed. So basically I have decided to poll the remote server and store the JSON in localStorage (to enable offline usage). For the moment, I have a manual page/view I must click on to update the localStorage , this works fine.
The problem is that after I use my temporary manual update page, I then go to the news page/view and it is not updated. To view the current JSON contents I must hit refresh (while still developing in the browser.)
I'm totally new to Angular and have tried to find solutions to this myself - $watch or reload: true seem to be suggested as fixes, but I cannot get them to work in my case.
Route
.state('tab.news', {
url: '/news',
reload: true,
views: {
'news-tab': {
templateUrl: 'templates/news_home.html',
controller: 'newsCtrl'
}
}
})
factory
angular.module('schoolApp.services', [])
.factory('newsService', function($q) {
var newsHeadlines =localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail status
var newsHeadlinesObj = JSON.parse(newsHeadlines);// convert to an object
console.log("factory newsService ran");
return {
findAll: function() {
var deferred = $q.defer();
deferred.resolve(newsHeadlinesObj);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var deferred = $q.defer();
var newsItem = newsHeadlinesObj[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
Controller
schoolApp.controller('newsCtrl', function($scope, newsService) {
console.log ( 'newsCtrl ran' );
newsService.findAll().then(function (newsHeadlinesObj) {
$scope.newsHeadlinesObj = newsHeadlinesObj;
}, function(error){
console.log(error)
});
})
Looking at my console, the first time I read the news, the factory then controller run, but if I go to pull more data down, then go hack to news, only the controller runs, unless I refresh, then both run again.
I do not need the news view to update 'live' while still on it (but if that can be easilly done all the better) - just to pick up new data when you go back to news after being elsewhere in the app.
Thank you.
Factories return singletons and only run once. The object newsService is cached by angular. The var declarations for newsHeadlines and newsHeadlinesObj will only ever run once; meaning your promise returning methods will always resolve the promise with the same data that was retrieved when your factory was first instantiated. You should put them in a function and call it from your find methods on the singleton object.
.factory('newsService', function($q) {
function getHeadlines() {
var newsHeadlines = localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail
return JSON.parse(newsHeadlines);// convert to an object
}
return {
findAll: function() {
var headlines = getHeadlines();
var deferred = $q.defer();
deferred.resolve(headlines);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var headlines = getHeadlines();
var deferred = $q.defer();
var newsItem = headlines[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
PS - I'm sure you know and are planning to do things differently later or something, but just in case you don't: Using promises here is pointless and you have no need for $q here. You could simply return the data instead of returning the promises.
I solved this withouut promises, I just used $rootScope in the factory and $scope.$on in the controller; when I change the factory, i use $rootScope.$broadcast to tell the controller that I change it.
.factory('dataFactory', ['$http', '$rootScope', function ($http, $rootScope) {
var dataFactory = {
stock: null,
getStock: getStock
}
function getStock() {
$http.get("/api/itemfarmacia/").then(function success(res) {
dataFactory.stock = res.data;
$rootScope.$broadcast('changingStock'); //Ones who listen this will see it
}, function error(err) {
console.log("Bad request");
})
}
return dataFactory;
}])
and in the controller
.controller('atencion', ["$scope", "$state", "dataFactory", function ($scope, $state, dataFactory) {
$scope.stock = dataFactory.stock; //At first is null
dataFactory.getStock(); //wherever you execute this, $scope.stock will change
$scope.$on('changingStock', function () {//Listening
$scope.stock = dataFactory.stock; //Updating $scope
})
}])
Say I need to include a GroupId parameter to every request the user makes, but I don't want to modify every service call to include that. Is it possible to make that GroupId appended automatically to all requests, whether it is POST or GET query string?
I have been looking into the interceptor request function, but can't figure out how to make the change
** Edit **
Current working sample below is a combo of Morgan Delaney and haimlit's suggestions (I think it is a combom anyway). The basic idea is that if the request is a POST, modify config.data. For GET, modify params. Seems to work so far.
Still not clear on how the provider system works in Angular, so I am not sure if it is entirely approriate to modify the data.params properties here.
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function ($rootScope, $q, httpBuffer) {
return {
request: function (config) {
if (config.data === undefined) {
//Do nothing if data is not originally supplied from the calling method
}
else {
config.data.GroupId = 7;
}
if (config.method === 'GET') {
if (config.params === undefined) {
config.params = {};
}
config.params.GroupId = 7;
console.log(config.params);
}
return config;
}
};
} ]);
} ]);
If your example works, great. But it seems to lack semantics IMHO.
In my comments I mentioned creating a service but I've set up an example Plunker using a factory.
Plunker
Relevant code:
angular.module( 'myApp', [] )
.factory('myHttp', ['$http', function($http)
{
return function(method, url, args)
{
// This is where the magic happens: the default config
var data = angular.extend({
GroupId: 7
}, args );
// Return the $http promise as normal, as if we had just
// called get or post
return $http[ method ]( url, data );
};
}])
.controller( 'myCtrl', function( $scope, $http, myHttp )
{
// We'll loop through config when we hear back from $http
$scope.config = {};
// Just for highlighting
$scope.approved_keys = [ 'GroupId', 'newkey' ];
// Call our custom factory
myHttp( 'get', 'index.html', { newkey: 'arg' }).then(function( json )
{
$scope.config = json.config;
});
});
I'm new to AngularJS and I have a bunch of controllers which are performing CRUD operations. These controllers all are making request to the same base URL, what is the right way to pass this URL to each controller without rewriting it everywhere or just making some global? Since I am new to to Angular I was wondering if there was some built in facility in $http that already does this and I just need to set that value.
The first problem is you don't have to perform your CRUD operations in a controller.
CRUD is a job for the Model part of MVC.
First, you can use Constant :
angular.module('MyApp')
.constant('ENV_URL','https://test.com');
And you can inject and call this constant where you need it.
Next, your CRUD have to be placed in a factory like this :
angular.module('myApp').factory('usersFact',function ($resource,ENV_URL)
{
var User = $resource(ENV_URL, { userId: '#id' }, {
get: {
method: 'GET',
isArray: false,
headers: { 'Accept': 'what you need' }
},post : {}, put :{}
}
});
return {
getSummary : function (id, cb) {
var summary = User.getSummary({ userId: id },
function () {
console.log(summary);
});
return summary;
}
};
}
);
And, in your controller your can now use your data :
angular.module('MyApp').controller('ProfilCtrl',
function ($scope,usersFact,) {
$scope.userInfo = usersFact.getSummary(userId, function () {
console.log($scope.userInfo)
});
}
);
Well I guess you should create a service and do the heavy-lifting there and expose methods that the controllers could call.
There are Two ways to do this...
The first method is by $rootscope. The $rootscope is the parent of all $scopes in all controllers so any property you assign to $rootscope will be accessible by any $scope.
By running the below code you can then access "http://myurl.com" in any controller using $scope.url
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.url = "http://myurl.com";
});
The other way is to use a service/factory like this...
app.factory('globalurl', function() {
return "http://myurl.com";
});
then write all your controllers like this...
function Ctrl1($scope, globalurl) {
$scope.url = globalurl();
}