I'm very new to AngularJS and programming aswell, so it can be easy question for you but I'm struggling with it for plenty of hours and can't get my thinking straight.
So, my goal is simple, to have facebook login on my app (webpage), I'm using Ciul angular-facebook module, this actually works but not in the way I want it. When user loggs I want to show his name and photo, but now I have to manually reload page, then it shows, until then it won't. Also I'm storing logged user into localStorage (works ok).
The problem is that after logging in the data aren't updated. In my html code I tried to call them from controller or service, but both had old data, and I don't know how to update it without reloading the page. The most interesting part is when I try it with simple variable and it works like a charm.
Here is my Service
app.factory('mainService', ['$window', '$location', '$route', '$filter', 'Facebook', function (win, $location, $route, $filter, Facebook) {
var scope = {
fbLogin: false,
fbUid: 0,
fbAccessToken: 0,
vkLogin: false
};
var user = {};
if(localStorage.getItem('user') != null) {
user = JSON.parse(localStorage.getItem('user'));
} else {
user = null;
}
return {
scope : scope,
user : user,
fbLogin : function () {
Facebook.login(function (response) {
scope.fbLogin = response.status;
scope.fbAccessToken = response.authResponse.accessToken;
scope.Uid = response.authResponse.userID;
Facebook.api('/me?fields=id,name,email,picture', function (response) {
localStorage.setItem('user', JSON.stringify(response));
});
});
console.log('setting user');
user = JSON.parse(localStorage.getItem('user'));
},
fbLogout : function () {
Facebook.logout(function (response) {
});
user = null;
localStorage.removeItem('user');
},
removeAuth : function () {
Facebook.api({
method: 'Auth.revokeAuthorization'
}, function (response) {
Facebook.getLoginStatus(function (response) {
scope.fbLogin = response.status;
});
});
}
};
}]);
Here is my Controller
app.controller('IndexController', ['$scope', '$location', '$http', 'mainService', function ($scope, $location, $http, mainService) {
$scope.ms = mainService;
$scope.user = mainService.user;
$http({
method: 'GET',
url: 'backend/api/v1/getItems.php',
headers: { "Content-Type": 'text/json; charset="utf-8"' }
})
.success(function(data, status) {
//alert("status is : "+status);
if(status === 200) {
$scope.items = data;
}
})
.error(function(data, status) {
$scope.items = [];
});
}]);
Calling login function < li >< a href="#/" ng-click="ms.fbLogin()">Login using Facebook
Calling data in html {{user.name}} or {{ms.user.name}}
Well, the same problem is also with logging out.
Hey so it's hard for me to say this is the exact problem but from the looks of it everything you have so far is actually pretty good.
There's only one problem I spot and it just boils down to understanding objects are bound to variables by reference. If you're not sure what that means I would say do some research on that first since it's a fundamental part of javascript.
What seems to be the problem here is that you are setting 'user' to an empty object in your service. In your controller you are then assigning $scope.user to that same object empty.
However in your login function you are assigning user to the new JSON from the storage. First, this should also be inside the success callback, right after you set the localstorage.
I'm not familiar with that module but I'm going to assume that those are async functions. Therefore you're grabbing that local storage data before it's even been set.
Also, by setting 'user' to a new object you have updated it's value in the service but not in the controller.
Because it's bound by reference, 'user' in the service now points to a new object, while $scope.user is still pointing to that original empty object.
To solve this problem you can do two things:
Handle the callback differently so that you reassign $scope.user to the new data.
Or you can take advantage of object reference.
You can keep most of your code the same, but in your service, instead of assigning the data to 'user', assign it to a property on user.
Facebook.api('/me?fields=id,name,email,picture', function (response) {
localStorage.setItem('user', JSON.stringify(response));
user.data = response;
});
Since both the service and the controller are referencing the same object, you will have access to that new data property on $scope.user.
HTML:
<span>{{user.data.name}}</span>
Related
My English is very bad,at first Controller,I post data form The server,and i got a $rootScope.YD for Transfer data .but when i use the YD in the second page,it does't work.can you help me ?
.controller('yd_homeCtrl',function($rootScope, $scope, $state, $http,$ionicActionSheet, $ionicModal)
{
$scope.getReadList = function ()
{
var url = $rootScope.rootUrl + "/read.php";
var data = {
"func":"getReadList",
"unionid":$rootScope.userinfo.unionid,
"fr":1
};
encode(data);
$rootScope.LoadingShow;
$http.post(url, data).success(function (response)
{
$rootScope.LoadingHide();
$rootScope.YD=response.data.result[0];
$state.go('yd_improve')
}).error(function (response, status)
{
$rootScope.LoadingHide();
$rootScope.Alert('连接失败![' + response + status + ']');
return;
});
}
})
.controller("yd_improveCtrl",function($rootScope, $scope, $state, $http, $ionicActionSheet, $ionicModal, $stateParams, $interval, $sce, $ionicHistory,$ionicSlideBoxDelegate)
{
$scope.text="";
angular.forEach($rootScope.YD,function(value,key){
if(key==0)
{
//alert(1111111);
//alert(value.text);
$scope.text=value.text;
alert($scope.text);
}
});
});
there is app.js state:
.state('yd_improve', {
cache: false,
url: '/yd_improve/:id',
onExit: function ()
{
var v = document.getElementById("audio");
v.pause();
v.src = "";
},
templateUrl: 'templates/yd_improve.html',
controller: 'yd_improveCtrl'
})
Use $broadcast service. It's the most reliable solution for broadcasting events and passing parameters between controllers.
Don't rely on storing data in service because on page refresh, service also gets refreshed and you will lose the data stored in service's variable. So maintaining state in service is highly unrecommended. Use $broadcast instead.
You have a couple of options. Create a service which stores the information. In first controller set the data. And in second controller get the data.
Or you can use $broadcast. This publishes an event with a name you give it and the data you parse through. You do this on first controller. And on second controller listen for the event using $rootScope.on.
But I'd recommend using a service its best solution in terms of memory and performance.
I have a view within my App which does a database pull to show a user images they have previously uploaded.
The problem is that another view allows them to upload new images, but when switching back to the view of their uploaded images, they have to do a full page refresh to see their new uploads.
The question is how can I force the $http.get to run every time the view is loaded?
This is what I am trying but is not doing what I think it should:
capApp.controller('myUploadedPhotos', function ($scope, $http) {
$scope.nameFilter = "";
$http.get("/ajax/myUploadedPhotos.php", { cache: false})
.success(function(response) {
$scope.photos = response;
});
});
Is there a way to do this?
Your code looks correct so possibly the request is cached from the server? You can try appending a random string to your url to break the cache. e.g.
"/ajax/myUploadedPhotos.php" + new Date().getTime()
After thinking about it, I think you can also remove the { cache: false} because Angular also won't be able to cache the request if the timestamp changes. The old requests would just be sitting around somewhere taking up memory.
I'm not quite understand your question, but there isn't any issues with next initialization behaviour:
(function(angular) {
var capApp = angular.module('yourModule', []);
capApp.controller('myUploadedPhotos', ['$scope', '$http',
function ($scope, $http) {
$scope.nameFilter = "";
$scope.actions = angular.extend($scope.actions || {}, {
init: function () {
return $http.get("/ajax/myUploadedPhotos.php", {cache: false}).then(
function (response) {
$scope.photos = response;
}, function (reason) {
console.log('Error occured: ' + reason);
});
}
});
// You could even use it in $watch
$scope.actions.init();
}
]);
})(angular);
I have a controller that takes the user input and post's it to my API and captures the response which happens to be the user data. That's great however I need to access the user data on the dashboard which has a different controller. I can't figure out how to abstract the API call into a service and fetch the response from the dashboard controller. Is there an easier way? The login functionality works perfect however the res.data.user appears inaccessible. My question is how do I make the response accessible from a diffefrent controller?
Controller (signup):
angular.module('app').controller('Signup', function ($scope, $http, toastr, $location, $auth) {
// Submit request to Sails.
$http.post('/api/user/email', {
email: $scope.signupForm.email.toLowerCase(),
password: $scope.signupForm.password
})
.then(function onSuccess(res){
$auth.setToken(res.data.token)
$location.path('/dashboard')
$scope.user = res.data.user;
})
})
HTML (dashboard):
<div class="container" ng-show="isAuthenticated()" ng-controller="Dashboard">
<p>License: {{user.license}}</p>
</div>
Have a factory or service that stores user data to pass around. Inject it in the original
.factory('storeData', function () {
var userData = {}; //it's empty, thats ok
function dataAll () {
return userData;
};
return dataAll;
}
.controller('Signup', function ($scope, $http, toastr, $location, $auth, ***storeData***) {
//remove *** obviously
var importedData = storeData();
// or storeData.dataAll() for some reason OP said this didn't work but it works in my code
$http.post('/api/user/email', {
email: $scope.signupForm.email.toLowerCase(),
password: $scope.signupForm.password
})
.then(function onSuccess(res){
$auth.setToken(res.data.token)
$location.path('/dashboard')
will modify factory object that was returned to contain res.data.user
instead of blank. javascript does not make copies, but instead links
back to the original factory object. So changes take place on the
factory object.
importedData = res.data.user
$scope.user = res.data.user;
})
})
new controller example
.controller('test', function ($scope, storeData) {
console.log(storeData() + 'see if data shows up');
}
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
})
}])
I have looked, and assume this is simple, but just couldn't figure out the API documentation for this.
Assume I have a controller that pulls data when first called (I'm leaving out a ton, of course):
myCtrl = function ($scope, Data) {
$scope.data = [];
data_promise = Data.getData(); //a $http service
data_promise.success(function (data) {
$scope.data = data;
});
}
That works great, and when the page loads I get $scope.data populated exactly as I need it. However, of course, the user may wish to update the data. Assume a simple service "Data.save()" called when a server clicks a "save" button on a form:
myApp.factory('Data', function ($http) {
save: function (data) {
$http({
method: 'POST',
url: 'someURL',
data: data,
}).success(function(){
//something here that might trigger the controller to refresh
});
};
});
What would I put in the success callback that might re-instantiate the controller so that it has the most up-to-date data from the server? Currently I am having to refresh the page to get the updated data. I am not worried right now about minimizing server calls by cashing results and changes. I just need to get this to work first.
Thanks!
You do not need to refresh. Simply change the updated data in the ControllerScope.
This should work.
myApp.factory('Data', function ($http) {
save: function (data, $scope) {
$http({
method: 'POST',
url: 'someURL',
data: data,
}).success(function(){
$scope.data = newData;
//something here that might trigger the controller to refresh
});
};
});
// in your controller
Data.save(data, $scope);
But: You shouldn't do this way. This looks messy. Use services or events which you watch to wait for the changes comming back from the service.
OK, although I am sure there is a better answer I have one for me. Essentially I am taking the important parts of the controller and placing them in the success callback. In order to keep it from looking messy, I have wrapped all the parts of the controller that need be updated in a named function.
myCtrl = function ($scope, Data, $q) {
// Binding the Data
var updateAll;
updateAll = function () {
$scope.data1 = [];
$scope.data2 = [];
$scope.data3 = [];
$scope.data4 = [];
service_promise1 = Data.getData1(); //a $http service
service_promise2 = Data.getData2();
service_promise3 = Data.getData3();
service_promise4 = Data.getData4();
$q.all([service_promise1,service_promise2,service_promise3,service_promise4])
.then(function([service1,service2,service3,service]){
$scope.data1 = service1 + service2 //I know this isn't valid Javascript
// just for illustration purposes
$scope.data2 = service2 - service1 + service 3
//etc...
});
};
updateAll();
//Form Section
$("#myForm').dialog({
buttons: {
Save: function () {
Data.save(data).success(function(){
updateAll();
});
}
}
});
}
Breaking this down, I have wrapped all the assignments to scope objects that rely on services into the updateAll function and invoke it on instantiation of the myCtrl. In the form that updates the data I call the updateAll() function upon success of the Data.save() function.
Not exactly brain surgery, I'll admit, but I had gotten confused with $scope.$apply() and thinking about just calling myCtrl(). That somehow seemed like the "Angular" way, but neither worked. I guess the controller function gets run only once on page refresh and there is no Angular way to call it again.