Ionic can't pass information in $rootScope - javascript

I want to pass the data in $rootScope of users
Here is my factroty
userProvider.js
'use strict';
app
.factory('userProvider', function ($rootScope , $http , $location) {
var url='http://127.0.0.1:100/suitecrm/API/Login.php';
function logIn(user) {
$http.post(url,user)
.success(function (response) {
$rootScope.user=response.data;
$location.path('/profile');
console.log(response);
});
}
return {
logIn: logIn
}
});
console.log(response); show me data normally in the console
Here is the controller of the profile which i want to show information of current user
'use strict';
app
.controller('Profile', function ($scope ,userProvider, $rootScope) {
console.log($rootScope.user);
})
;
But in the console of chrome shows me undefined.
Here is Routing.js
'use strict';
var cacheActive=false;
app.config(function ($stateProvider) {
$stateProvider
.state('homapage', {
cache: cacheActive,
url: '/login',
templateUrl: 'js/views/homepage/Login.html',
controller:'homepageLogin'
})
.state('profile', {
cache: cacheActive,
url:'/profile',
templateUrl:'js/views/profiles/profile.html',
controller:'profile'
})
});
How can i pass the data in $rootScope and show them in the view of the controller?
Thank u

You haven't called the logIn() function from your userProvider service yet.
app
.controller('Profile', function ($scope ,userProvider, $rootScope) {
userProvider.logIn($scope.user);
console.log($rootScope.user);
})
But,
I suggest you do it without using $rootScope. What you can is from your factory, you may want to return the data after the call:
app.
factory('userProvider', function($http){
var url = 'http://127.0.0.1:100/suitecrm/API/Login.php';
function logIn(user) {
$http.post(url,user).success(function (response) {
return response;
});
}
return {
logIn: logIn
}
})
Then, again, without using $rootScope, on your Profile controller you can do:
app.
controller('Profile', function($scope, userProvider){
$scope.user = {...}
$scope.currentUser = userProvider.logIn($scope.user);
console.log($scope.currentUser)
})
For the location routing, please do it inside your controller, instead of the factory.

Related

AngularJS not routing properly

I am learning Angular, so here is my testapp : http://enrolin.in/test/#/students
Now here I want to search the database by name. So I created the php that returns exactly what I need. Here is the php : http://enrolin.in/test/login.php?p=fetchbyname&&name=ak You have to replace name in the url to anything you need to search. I also created a partial page that returns absolutely correct results, here is the page: http://enrolin.in/test/#/studentSearch/ak Everything was fine till now But here is the problem:
When I try to search in http://enrolin.in/test/#/students , angularJS does not route me to something like http://enrolin.in/test/#/studentSearch/ak but instead to the default that I have set in $routeProvider
Here is my angularJS (I have removed some unimportant code):
The route provider:
.config(function ($routeProvider) {
$routeProvider
.when("/students/:id", {
templateUrl: "templates/studentDetails.html",
controller: "studentDetailsController"
})
.when("/studentSearch/:name", {
templateUrl: "templates/studentSearch.html",
controller: "studentSearchController"
})
.otherwise({
redirectTo: "/home"
})
})
The Controller that passes the link:
.controller("studentsController", function ($scope, $http, $route,$location) {
$scope.searchStudent=function(){
if($scope.name){
$location.url("/studentsSearch/" + $scope.name);
}
else{
$location.url("/studentsSearch/");
}
}
$scope.reloadData=function(){
$route.reload();
}
$http.get("http://enrolin.in/test/login.php?p=fetchall")
.then(function (response) {
$scope.students = response.data;
})
})
The controller that fetches data and displays:
.controller("studentSearchController", function ($scope, $http, $routeParams) {
if($routeParams.name)
{
$http({
url: "http://enrolin.in/test/login.php?p=fetchbyname&&name=",
method: "get",
params: { name: $routeParams.name }
}).then(function (response) {
$scope.studs = response.data;
})
}
else
{
$http.get("http://enrolin.in/test/login.php?p=fetchall")
.then(function (response) {
$scope.students = response.data;
})
}
})
Previously everytime I wanted to put a link in html to route I used to write like courses But now when I want to put it in the function instead, I am not sure what to write. Please Help.
Hi #AkhilEshKhajuria,
You are not using the same name what you have mentioned in the routing config. Routing name is "/studentSearch/:name?" but you have used in the function as "/studentsSearch/".
Please try replacing $location.url("/studentsSearch/" + $scope.name); with $location.path("/studentsSearch/" + $scope.name);
Correct the naming issue and it should work.
I tried this and it works fine.

In Angular ui-router how to emit an event from one view to another view?

The use case is to change login button to text "logged in as xxx" after authentication.
I have devided my page to 3 views: header, content, footer. The login button is in the header view. When I click login, it transits to "app.login" state, and the content view changes to allow user input username and password.
Here's the routing code:
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/',
views: {
'header': {
templateUrl: 'static/templates/header.html',
controller: 'AppController'
},
'content': {
templateUrl: 'static/templates/home.html',
controller: 'HomeController'
},
'footer': {
templateUrl: 'static/templates/footer.html',
}
}
})
.state('app.login', {
url: 'login',
views: {
'content#': {
templateUrl : 'static/templates/login.html',
controller : 'LoginController'
}
}
})
The html template has code like this:
<li><span ng-if='loggedIn' class="navbar-text">
Signed in as {{currentUser.username}}</span>
</li>
LoginController set a $scope.loggedIn flag to true once authentication succeeded, but how can I populate that flag to the header view?
As I understand it I can't just use $scope.loggedIn in the html template as above because the $scope is different in two controllers. I know if LoginController is a child of AppController, then I can call $scope.$emit in LoginController with an event and call $scope.$on in AppController to capture it. But in this case the two controllers are for different views, how can I make them parent-child?
I know I can use $rootScope but as I'm told polluting $rootScope is the last resort so I'm trying to find a best practise. This must be a very common use cases so I must be missing something obvious.
You can use a factory to handle authentication:
app.factory( 'AuthService', function() {
var currentUser;
return {
login: function() {
// logic
},
logout: function() {
// logic
},
isLoggedIn: function() {
// logic
},
currentUser: function() {
return currentUser;
}
};
});
Than can inject the AuthService in your controllers.
The following code watches for changes in a value from the service (by calling the function specified) and then syncs the changed values:
app.controller( 'AppController', function( $scope, AuthService ) {
$scope.$watch( AuthService.isLoggedIn, function ( isLoggedIn ) {
$scope.isLoggedIn = isLoggedIn;
$scope.currentUser = AuthService.currentUser();
});
});
In such cases I typically opt to use a service to coordinate things. Service's are instantiated using new and then cached, so you effectively get a singleton. You can then put in a simple sub/pub pattern and you're good to go. A basic skeleton is as follows
angular.module('some-module').service('myCoordinationService', function() {
var callbacks = [];
this.register = function(cb) {
callbacks.push(cb);
};
this.send(message) {
callbacks.forEach(function(cb) {
cb(message);
});
};
}).controller('controller1', ['myCoordinationService', function(myCoordinationService) {
myCoordinationService.register(function(message) {
console.log('I was called with ' + message);
});
}).controller('controller2', ['myCoordinationService', function(myCoordinationService) {
myCoordinationService.send(123);
});
Do you use any serivce to keep logged user data? Basically serivces are singletons so they are good for solving that kind of problem without polluting $rootScope.
app.controller('LoginController', ['authService', '$scope', function (authService, $scope) {
$scope.login = function(username, password) {
//Some validation
authService.login(username, password);
}
}]);
app.controller('HeaderController', ['authService', '$scope', function (authService, $scope) {
$scope.authService = authService;
}]);
In your header html file:
<span ng-if="authService.isAuthenticated()">
{{ authService.getCurrentUser().userName }}
</span>

How to access and update resolve data of parent state inside onEnter method of child state of a ui router

So I have a ui-router state that looks like so:
Parent state
$stateProvider
.state('profile',{
url: '/profile',
views: {
'contentFullRow': {
templateUrl: 'ng/templates/profile/partials/profile-heading-one.html',
controller: function($scope, profile,misc){
$scope.profile = profile;
$scope.misc = misc;
}
},
'contentLeft': {
templateUrl: 'ng/templates/profile/partials/profile-body-one.html',
controller: function($scope, profile,misc){
$scope.profile = profile;
$scope.misc = misc;
}
},
'sidebarRight': {
templateUrl: 'ng/templates/profile/partials/todo-list-one.html',
controller: function($scope, profile,misc){
$scope.profile = profile;
$scope.misc = misc;
}
}
},
resolve: {
profile: function($http){
return $http({method: 'GET', url: '/profile'})
.then (function (response) {
console.log(response.data)
return response.data;
});
},
misc: function($http){
return $http({method: 'GET', url: '/json/misc'})
.then (function (response) {
console.log(response.data)
return response.data;
});
}
}
})
Child states
.state('profile.social', {
url: '/social',
controller:function($scope, profile, misc){
$scope.profile = profile;
$scope.misc = misc;
},
template: '<div ui-view></div>'
})
.state('profile.social.create',{
url: '/create',
onEnter: function($state){
//Will call a modal here...
//How do I access or update `$scope.profile`
//so far am doing this and it works
$state.$current.locals.globals.profile.first_name = 'My New name';
//Is there any better way of doing this?
}
})
Question
Since $scope is not available in onEnter method, how do I access or update $scope.profile
So far am doing something like:
onEnter: function($state){
$state.$current.locals.globals.profile.first_name = 'My New name';
}
This works but am wondering if there is a better way of doing this?
The correct thing to do is not try and access the controllers $scope from outside the controller. You should instead move your profile data to a service, and inject it into both the controller and the onEnter function (as needed). By separating profile data into a service, you can now access it from anywhere else too :)
For example:
.service('ProfileService', function(){
var state = {};
this.loadProfile = function(){
return $http({method: 'GET', url: '/profile'})
.then (function (response) {
console.log(response.data);
state.profile = response.data;
return state.profile;
});
};
this.getState = function(){
return state;
};
});
// the controller
controller: function($scope, ProfileService){
$scope.state = ProfileService.getState();
}
// on enter
onEnter: function($state, ProfileService){
var state = ProfileService.getState();
state.profile.first_name = 'New Name';
}
I wrapped the profile data in a container (state), so that the profile key itself can be changed. So inside your view you will need to reference your profile like so: state.profile.first_name.
Also inside your resolve you will also need to inject the service, and run the load function returning the associated promise (so that resolve actually works).
Without knowing your requirements it is hard to describe the best way to do this, but in summary, you should pull your profile data into its own service, and inject it whenever you need it. The service should also encapsulate any promises that resolve once the service data has loaded.

Pass $scope object to route, keep if refresh

Lets say i list all users in a list, when i click a user i want to route to a new view and get the data for the selected person.
What is the preferred way? Should i move the data i already got when i listed the users or should i create a new server call?
My first thought is to pass the data, but the problem with this is that the data the gets lost if the user refreshes the page.
What is the best practice to solve this?
Small example:
(function() {
var app = angular.module('app');
var controllerId = 'app.controllers.views.userList';
app.controller(controllerId, [
'$scope', 'UserService',function ($scope, userService) {
var vm = this;
vm.users = [];
userService.getAllUsers().success(function (data) {
vm.users= data.users;
});
var gotoUser = function(user) {
// Pass the user to UserDetail view.
}
}
]);
})();
<div data-ng-repeat="user in vm.users" ng-click="vm.gotoUser(user)">
<span>{{customer.firstname}} {{customer.lastname}}</span>
</div>
i now list the user details in UserDetail view, this view is now vulnerable against a browser refresh.
Typically most people just create a new server call, but I'll assume you're worried about performance. In this case you could create a service that provides the data and caches it in local storage.
On controller load, the controller can fetch the data from the service given the route params and then load the content. This will achieve both the effect of working on page refresh, and not needing an extra network request
Here's a simple example from one of my apps, error handling left out for simplicity, so use with caution
angular.
module('alienstreamApp')
.service('api', ['$http', '$q','$window', function($http, $q, $window) {
//meta data request functions
this.trending = function() {
}
this.request = function(url,params) {
var differed = $q.defer();
var storage = $window.localStorage;
var value = JSON.parse(storage.getItem(url+params))
if(value) {
differed.resolve(value);
} else {
$http.get("http://api.alienstream.com/"+url+"/?"+params)
.success(function(result){
differed.resolve(result);
storage.setItem(url+params,JSON.stringify(result))
})
}
return differed.promise;
}
}]);
I would say that you should start off simple and do a new server call when you hit the new route. My experience is that this simplifies development and you can put your effort on optimizing performance (or user experience...) where you will need it the most.
Something like this:
angular.module('app', ['ngRoute', 'ngResource'])
.factory('Users', function ($resource) {
return $resource('/api/Users/:userid', { userid: '#id' }, {
query: { method: 'GET', params: { userid: '' }, isArray: true }
});
});
.controller("UsersController",
['$scope', 'Users',
function ($scope, Users) {
$scope.loading = true;
$scope.users = Users.query(function () {
$scope.loading = false;
});
}]);
.controller("UserController",
['$scope', '$routeParams', 'Users',
function ($scope, $routeParams, Users) {
$scope.loading = true;
$scope.user = Users.get({ userid: $routeParams.userid }, function () {
$scope.loading = false;
});
$scope.submit = function () {
$scope.user.$update(function () {
alert("Saved ok!");
});
}
}]);
.config(
['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/users', {
templateUrl: '/users.html',
controller: 'UsersController'
})
.when('/users/:userid', {
templateUrl: '/user.html',
controller: 'UserController'
})
.otherwise({ redirectTo: '/users' });
}
]
);

AngularJS : angular-ui-router always redirects to $urlRouterProvider.otherwise location

I'm trying to create an SPA where you have to be logged in to access almost everything. So naturally, the default screen you see is the login screen. However, after a user has logged in, no matter what the ui-sref is, ui-router redirects to the login page (even when the user is authenticated). Here is my ui-router code:
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();
Anytime I try to use $state.go(any state name here) or even type the address into the address bar, I am always redirected to the auth state. On the console the message is "Could not find http://localhost/#/" for every single route. I can type in http://localhost/#/users/5/subjects and I get the same message.
Here is one of my controllers doing a redirect:
(function () {
'use strict';
angular
.module('app')
.controller('AuthController', AuthController);
function AuthController($auth, $state, $http, $rootScope, $log) {
var vm = this;
vm.loginError = false;
vm.loginErrorText;
vm.login = function () {
var credentials = {
username: vm.username,
password: vm.password
};
$auth.login(credentials).then(function () {
return $http.get('api/authenticate/user');
}, function (error) {
vm.loginError = true;
vm.loginErrorText = error.data.error;
}).then(function (response) {
var user = JSON.stringify(response.data.user);
localStorage.setItem('user', user);
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
//$log.info('From AuthCtrl: ' + $rootScope.currentUser.id);
$state.go('subjects', {user_id:$rootScope.currentUser.id});
});
}
}
})();
Any ideas what I'm doing wrong? Thanks a lot for your time.
Update: Ok, I haven't found a way to fix it but I think I may have found a possible cause. It seems to only happen for the routes with parameters. For example, if I go to the users state, whose path is /users, there is no redirect. However, if I go to the subjects state, whose path is /users/:user_id/subjects, it does redirect. It's like the Url matching service can't recognize that /users/5/subjects matches /users/:user_id/subjects, so redirects. Any ideas how to work around this?
I found I didn't have a '/' at the beginning of my initial state url. Every time I navigated to the state, the missing '/' seemed to push it into the stateProvider.otherwise.
state1: 'opportunity'
state1Url : '/opportunity/' <== added initial forward slash to make it work.
state2: 'opportunity.create'
state2Url : 'create/'
The first path to be recognised will be the selected as the current location. This means that the order of your route definitions is crucially important. In your case you only have a single catch-all otherwise route definition and since all routes match this then all routes are directed to your login page ignoring any other route definitions you may have, including all your stateProvider state definitions.
One way to fix this is to remove the urlRouterProvider route definition altogether and instead use the *path syntax provided by ui-router to create an alternative otherwise state (which must be defined last for the same reasons given above).
Therefore your code might look something like this:
$stateProvider
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
})
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state("otherwise", {
url: "*path",
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
From experience, this is either due to the / missing at either the beginning or the end of the url route property definition.
Make sure for parent routes to add the initial forward slash to your routes.
.state('checkers', {
url: '/checkers/',
templateUrl: 'checkers.html',
controller: 'CheckersController',
title: 'Checker',
})
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();

Categories

Resources