Save form before state change with uirouter - angularjs

I have multi steps form in my application and i would like to save it before go to the next step...
I did an simple example, but i'm not sure if it's the right approach.
I used resolve config and a service to access to the formData.
Config
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url : '/',
controller : 'appCtrl',
templateUrl : 'partials/home.html',
resolve : {
saveform : function($state, FormData){
}
},
})
.state('home.state1', {
url: "state1",
templateUrl : 'partials/item1.html',
resolve : {
saveform : function($state, FormData){
return FormData.save();
}
},
})
.state('home.state2', {
url: "state2",
templateUrl : 'partials/item2.html',
resolve : {
saveform : function($state, FormData){
return FormData.save();
}
}
})
});
Controller
app.controller('appCtrl', function($scope, $rootScope, $state, saveform, FormData){
$scope.formData = {};
$scope.tempData = {};
$rootScope.$on('$stateChangeStart', function(event){
console.log($scope.tempData , $scope.formData)
if(!_.isEqual($scope.tempData , $scope.formData)){
console.log('OK CHANGE DETECTED, SAVE')
var temp = {}
FormData.set(angular.copy($scope.formData));
}else{
console.log('NO CHANGE')
}
});
$rootScope.$on('$stateChangeSuccess', function(event){
var temp = FormData.get();
if(Object.keys(temp).length!=0){
FormData.cancel();
angular.copy(temp, $scope.tempData );
angular.copy(temp, $scope.formData );
}
});
})
Service
app.service('FormData', function($q, $timeout){
this.formdata = {};
this.save = function(){
if(Object.keys(this.formdata).length===0)
return false;
var deferred = $q.defer();
$timeout(function() {
this.formdata.test = "YEAH TEST";
deferred.resolve(this.formdata);
//this.formdata = {};
}.bind(this), 1000);
return deferred.promise;
}
this.cancel = function(){
this.formdata = {};
}
this.set = function(data){
this.formdata = data;
}
this.get = function(){
return this.formdata;
}
})
Example on codepen : http://codepen.io/anon/pen/RWpZGj

I would approach this differently.
You're using multiple states for one single form. Usually you only want to save complete form data. Therefore I would devide the form into multiple sections that you can show/hide. That way, upon clicking your "next" button you can validate each step separately and show the next step once the current one is valid.
Once you've reached the last step of your form, you can save the whole thing in one go.
If you insist on using separate states:
You're using the state change to trigger a save action. Instead, use the response of your FormData service to trigger a statechange. That way you can validate and save your data before you move on to the next step. It also prevents you from saving data when someone moves to another state that is not in your multi-step form.

Related

pass the value from one page to another using rootscope in angularJS

Login.js:
app.controller('LoginFormController', ['$scope','$http','$rootScope', '$state', function($scope, $http, $rootScope, $state) {
$scope.user = {};
$scope.authError = null;
$scope.login = function() {
$scope.authError = null;
var emailId = $scope.user.email;
var password = $scope.user.password;
$http({
url: 'http://localhost:8090/login/login',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: 'email='+emailId+'&password='+password
//data: {'email': $scope.user.email, 'password': $scope.user.password}
}).then(function(response) {
console.log(response.data);
if (response.data.status == 'SUCCESS') {
$scope.user = response.data.user.firstName;
$rootScope.test = response.data.user.firstName;
console.log("check: ",$rootScope.test)
$state.go('app.dashboard');
} else {
//alert('invalid login');
$scope.authError = 'Email or Password not right';
}
}, function(x) {
$scope.authError = 'Server Error';
})
};
}])
I saved the value under $rootScope.test
Here Is my App.main.js:
'use strict';
angular.module('app').controller('AppCtrl', ['$scope','$rootScope',
function($scope, $rootScope) {
$scope.user5 = $rootScope.test;
}
]);
trying to print the rootscope
If I run this Code i am facing the error of $rootScope is undefined in the console. How to Resolve this
$rootScope is the parent of all $scope, each $scope receives a copy of $rootScope data which you can access using $scope itself.
Here is a modified version
https://jsfiddle.net/sedhal/g08uecv5/17/
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.obj = {
"name":"user1",
"bdate":"18/11/1994",
"city":"Ahmedabad"
};
})
.controller('myCtrl', function($scope, $rootScope) {
$scope.data = $rootScope.obj;
})
.controller('myCtrl2', function($scope, $rootScope) {
$scope.data1 = $rootScope.obj;
});
There is a useful answer to your question here: https://stackoverflow.com/a/18881189/9013688
Instead of passing directly your property on the $rootScope, you could emit an event which could be listened in an other part of your app like this:
if (response.data.status == 'SUCCESS') {
$rootScope.$emit('user-logged', response.data.user.firstName)
}
And then:
$rootScope.$on('user-logged', function(event, data){
do something with your data
})
Or you could use a service which is a good way to handle your data in all your app.
Please ensure that you take the advice of georgeawg, his suggestion seems to be the best way to implement this functionality in my opinion.
I want to suggest what might be wrong with your example.
If you can see that in the main App.main.js you have given the declaration as
angular.module('app').controller(...
But you are using a variable app in login.js like so.
app.controller(...
Which I am assuming you are creating somewhere else. Thus the set rootscope value is lost because there are two instances of the same app.
The solution to your problem will be to declare one variable app which will store the instance of the app. So the fix for your solution will be to modify App.main.js to be like so.
var app = angular.module('app');
app.controller(...
Also you need to remove any other arbitary var app = in your complete code, since these will create multiple instances of the same app.
I hope my explanation was understandable and the correct guess, please try out this solution!
In your main js add this.
app.run(['$rootScope', function($rootScope) {
$rootScope.test;
}]);
Here is a sample implementation of george's suggestion which is the proper way to handle this.
app.controller('LoginFormController', ['$scope','$http','$rootScope', '$state', 'stateService', function($scope, $http, $rootScope, $state, stateService) {
var userFirstName = 'John';
stateService.setFirstName('Bill');
userFirstName = stateService.getFirstName();
console.log(userFirstName); // result will be 'Bill'
}])
And the service which I usually call stateService
app.factory('stateService', [factory]);
function factory() {
var userFirstName = null;
stateService.getFirstName = function() {
return userFirstName;
};
stateService.setFirstName = function(firstName) {
userFirstName = firstName;
};
return stateService;
}

How to handle $rootscope and take user id of logged in user in AngularJS?

I want to find the ID of the logged in user and display it in a page. I am new to angular and I don't have much clue on how to handle a session..
I have an angular app which is connected to backend API (.net core).
I will show the instances where $rootScope is used in the website (login and authorization is already enabled). I need to get an understanding of this to learn the app.
In App.js :
//Run phase
myApp.run(function($rootScope, $state) {
$rootScope.$state = $state; //Get state info in view
//Should below code be using rootScope or localStorage.. Check which one is better and why.
if (window.sessionStorage["userInfo"]) {
$rootScope.userInfo = JSON.parse(window.sessionStorage["userInfo"]);
}
//Check session and redirect to specific page
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
if(toState && toState.data && toState.data.auth && !window.sessionStorage["userInfo"]){
event.preventDefault();
window.location.href = "#login";
}
if(!toState && !toState.data && !toState.data.auth && window.sessionStorage["userInfo"]){
event.preventDefault();
window.location.href = "#dashboard";
}
});
});
Users.js :
'use strict';
angular.module('users', []);
//Routers
myApp.config(function($stateProvider) {
//Login
$stateProvider.state('login', {
url: "/login",
templateUrl: 'partials/users/login.html',
controller: 'loginController'
});
//Factories
myApp.factory('userServices', ['$http', function($http) {
var factoryDefinitions = {
login: function (loginReq) {
$http.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
return $http.post('http://localhost:1783/api/token?UserName='+loginReq.username+'&password='+loginReq.password).success(function (data) { return data; });
}
}
return factoryDefinitions;
}
]);
//Controllers
myApp.controller('loginController', ['$scope', 'userServices', '$location', '$rootScope', function($scope, userServices, $location, $rootScope) {
$scope.doLogin = function() {
if ($scope.loginForm.$valid) {
userServices.login($scope.login).then(function(result){
$scope.data = result;
if (!result.error) {
window.sessionStorage["userInfo"] = JSON.stringify(result.data);
$rootScope.userInfo = JSON.parse(window.sessionStorage["userInfo"]);
//$localStorage.currentUser = { username: login.username, token: result.data };
//$http.defaults.headers.common.Authorization = 'Token ' + response.token;
$location.path("/dashboard");
}
});
}
};
}]);
I came to know that the information about the user will be available in $rootScope.userInfo. If so, how can I take a value inside it?
Please explain with an example if possible. Thanks in advance.
One:
myApp.controller('loginController', [
'$scope', 'userServices', '$location',
'$rootScope',
function($scope, userServices, $location, $rootScope) {
Inside the controller, $rootScope was injected which makes you have access to the userInfo in that controller.
so if you inject $rootScope into another controller and console.log($rootScope.userInfo) you would see the users data.
myApp.controller('anotherController', ['$scope', '$rootScope', function
($scope, $rootScope){
console.log($rootScope.userInfo) //you'd see the users data from sessionStorage
});
According to this post on quora
$scope is an object that is accessible from current component
e.g Controller, Service only. $rootScope refers to an object
which is accessible from everywhere of the application.
You can think $rootScope as global variable and $scope as local variables.
$rootScope Defn.
In your case, once the user is logged in a key "userInfo" in sessionStorage is created and the same data is copied to $rootScope.userInfo. To check the fields in the userInfo after login try
console.log($rootScope.userInfo);
and print it in the console or open your session storage in your browser debugger tools [for chrome open developer tools>applications>sessionstorage>domainname] to view the values in the "userInfo" key.
Suppose you have
{
uid: "10",
fullname: "John Doe"
}
you can access uid in the script using $rootScope.userInfo.uid or $rootScope.userInfo['uid'].
Just in case you are unable to read the code, here is an explanation
if (window.sessionStorage["userInfo"]) {
$rootScope.userInfo = JSON.parse(window.sessionStorage["userInfo"]);
}
is checking the user is logged in or not.
the factory
myApp.factory('userServices', ['$http', function($http) {
var factoryDefinitions = {
login: function (loginReq) {
$http.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
return $http.post('http://localhost:1783/api/token?UserName='+loginReq.username+'&password='+loginReq.password).success(function (data) { return data; });
}
}
is calling the server to get the userInfo object.
$scope.doLogin = function() {
if ($scope.loginForm.$valid) {
userServices.login($scope.login).then(function(result){
$scope.data = result;
if (!result.error) {
window.sessionStorage["userInfo"] = JSON.stringify(result.data);
$rootScope.userInfo = JSON.parse(window.sessionStorage["userInfo"]);
//$localStorage.currentUser = { username: login.username, token: result.data };
//$http.defaults.headers.common.Authorization = 'Token ' + response.token;
$location.path("/dashboard");
}
});
}
};
$scope.doLogin is calling the above factory and storing the userInfo object.

Browser reload doesn't reload current Angular view

The problem I'm having is that after a user logs-in or signs-up they are redirected to the games view, this happens within the createUser function or after a successful authentication; in either case the redirect is handled with $state.go('games').
This all works fine, however, if the user navigates away from the games view to any other state like createGame or dashBoard and then refreshes the browser in one of those views they are always redirected back to the games view. When I remove the $state.go('games') this doesn't happen, and reload will only reload the current view (like it should).
I have tried changing parameters on $state.go and tried using $state.transitionTo(), but nothing changes this behavior.
Is this just normal behavior for $state.go? If not, am I using it wrong, and are there other ways to redirect? What can I do to stop this behavior?
var game = angular.module('game', ['ui.router','firebase']);
game.config(['$stateProvider', '$locationProvider', function($stateProvider,$locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider.state('signUp', {
url: '/signUp',
templateUrl: '/templates/signUp.html'
});
$stateProvider.state('games', {
url: '/games',
templateUrl: '/templates/games.html',
controller: 'games.controller'
});
$stateProvider.state('login', {
url: '/login',
templateUrl: '/templates/login.html'
});
$stateProvider.state('dashboard', {
url: '/dashboard',
templateUrl: '/templates/dashboard.html',
controller: 'dashboard.controller'
});
$stateProvider.state('createGame', {
url: '/createGame',
templateUrl: '/templates/createGame.html',
controller: 'createGame.controller'
});
}]);
// Root reference to database
game.factory("Fire", function($firebaseAuth) {
var ref = new Firebase("https://money-game.firebaseIO.com/");
return ref;
});
// Gives access to auth methods
game.factory("Auth", ["$firebaseAuth", "Fire",
function($firebaseAuth, fire) {
return $firebaseAuth(fire);
}
]);
game.controller('app.controller', ['$scope', '$state', '$stateParams', 'Auth', 'Fire', function ($scope, $state, $stateParams, auth, fire) {
$scope.user = {
email : '',
password : ''
};
$scope.signUp = function() {
auth.$createUser($scope.user)
.then(function(userData) {
// After successful signup save a user record to users under their auth ID
fire.child('users').child(userData.uid).set({
name : $scope.user.name,
email : $scope.user.email,
joined : Date.now()
});
$state.go('games');
console.log("User " + userData.uid + " created successfully!");
})
.catch(function(error) {
console.error("Error: ", error);
});
};
$scope.login = function() {
auth.$authWithPassword($scope.user).catch(function(error) {
console.error("Authentication failed:", error);
});
};
$scope.logout = function() {
auth.$unauth();
window.location = '/';
};
auth.$onAuth(function(authData) {
if (authData) {
$scope.activeUser = authData;
// After user logs in find user record by auth id
fire.child('users').child(authData.uid).on('value', function(snapshot) {
// Checks if user exsists
if (snapshot.exists()) {
// sets scope user to the user data
$scope.user = snapshot.val();
// sets scope user id to the auth id
$scope.user.id = snapshot.key();
}
});
console.log("Logged in as:", authData.uid);
$state.go('games');
} else {
$scope.activeUser = false;
// $scope.user = '';
}
});
}]);
game.controller('games.controller', ['$scope', '$state', '$stateParams', 'Auth', '$firebaseArray','Fire', function ($scope, $state, $stateParams, auth, $firebaseArray, fire) {
$scope.games = $firebaseArray(fire.child('games'));
$scope.view = 'listView';
$scope.setCurrentGame = function(game) {
$scope.currentGame = game;
};
$scope.addPlayer = function(game) {
console.log(game.$id);
var ref = fire.child('players').child(game.$id);
ref.push({
id : $scope.user.id,
name : $scope.user.name,
email : $scope.user.email
})
};
// swap DOM structure in games state
$scope.changeView = function(view){
$scope.view = view;
}
}]);
game.controller('createGame.controller', ['$scope', '$state', '$stateParams', 'Auth', '$firebaseArray','Fire', function ($scope, $state, $stateParams, auth, $firebaseArray, fire) {
$scope.games = $firebaseArray(fire.child('games'));
$scope.createGame = function() {
if ($scope.format == 'Match Play') {
$scope.skinAmount = 'DOES NOT APPLY';
$scope.birdieAmount = 'DOES NOT APPLY';
}
$scope.games.$add({
name: $scope.gameName,
host: $scope.user.name,
date: $scope.gameDate,
location: {
course: $scope.courseName,
address: $scope.courseAddress
},
rules: {
amount: $scope.gameAmount,
perSkin: $scope.skinAmount,
perBirdie: $scope.birdieAmount,
format: $scope.format,
holes : $scope.holes,
time: $scope.time
}
})
// $state.go('games');
};
}]);
It's not about $state.go
It's actually about when you call it, $state.go does one simple thing : trigger a change in the routing state off your application. You just happen to do it everytime your application authenticates your user against your $firebaseAuth service, in the $onAuth handler.
A single page app is an app
When you refresh the page in the browser, the entire app reloads, starts again, boostraps everything to display your app. This startup process includes re-authenticating your user (I didn't quite see where it is done in your code, but it has to be done), thus triggering the $onAuth handler again… and ultimately execute $state.go('games') again.
Put your $state.go invocation elsewhere
You don't actually mean to do it every time your app authenticates the user, you rather want to do it when your user performs a successful login or sign-up action.
$authWithPassword returns a promise, you could do the state change in a success callback when the promise is resolved.
Hope that helps !

AngularJS controllerAs not binding input controls

I'm initializing ControllerAs on all my Routes using the UI Router. The issue i'm having is when i click on Edit record, it takes me to another page which also passes the ID from the current page to fetch the record from the service and data bind the html fields.
In my controller class. When i write
var vm = this; //DOES NOT DATABIND
var vm = $scope; // THIS WORKS, FIELDS GET POPULATED.
Not sure what I'm doing wrong. The first line of code should work because i'm using ControllerAs right ?
Can someone please advise.
Thank you
//UI ROUTER
.state('dashboard.editUser',
{
url: '/editUser/:UserId',
templateUrl: 'app/components/admin/user/editUser.html',
controller: 'editUserController',
controllerAs: 'tF',
ncyBreadcrumb:
{
parent: 'dashboard',
label: 'Edit User'
},
});
//EDIT CONTROLLER
(function () {
var injectParams = ['$stateParams', '$state', 'userService'];
function editUserController($stateParams, $state, userService) {
var vm = this;
//load user
var loadUser = function () {
var userId = $stateParams.UserId;
vm.user = null;
userService.GetUserDetail(userId)
.success(function (data) {
vm.user = data;
})
.error(function (error) {
vm.status = 'Error retrieving data! ' + error.message;
});
};
loadUser();
};
editUserController.$inject = injectParams;
angular
.module('fatApp.userModule')
.controller('editUserController', editUserController);
}());

Ionic: Passing array elements between controllers

I have a controller & view which computes and displays a specific element. When I click a button on the view, I want to open another which shows more information about the item.
What is the best way to pass the element to another controller for display?
Many thanks
You could build a service which get data from one controller and passing it to the other, like so:
.factory('shareData', function(){
var finalData;
return {
sendData: function(data) {
finalData = data;
},
getData: function() {
return finalData;
}
}
});
Another option you have is to create a shared varraible in your main controller('app' controller in ionic)
.controller('app', function($scope){
$scope.data;
})
.controller('app.controller1', function($scope){
$scope.data = "Hello World";
})
.controller('app.controller2', function($scope){
console.log($scope.data); //Hello World
});
Yet another option besides those included by Matan Gubkin is including the array in the $rootScope: this way you will be able to access it from everywhere.
#Matan Gubkin idea is close, but you should use the service to get the specific item that you desire.
.factory('shareData', function(){
var finalData = ["Hello World",'more data'];
return {
sendData: function(data) {
finalData = data;
},
getData: function() {
return finalData;
},
getItem:function(_id) {
return finalData[_id];
}
}
});
then in setting up your controllers
.controller('app.controller', function($scope){
})
.controller('app.controller-list', function($scope,shareData){
$scope.data = shareData.getData();
})
.controller('app.controller-detail', function($scope,$stateParams,shareData){
// use stateParams to get index
console.log(shareData.getItem($stateParams.index)); //
});
setting up your routes
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
controller: 'app.controller'
})
.state('app.controller-list', {
url: "/list",
})
.state('app.controller-detail', {
url: "/detail/:index", // <-- pass in index as stateParam
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/list');
});

Resources