$location.path(...) does not update page - javascript

I'm trying to move to a user's main page after successful login.
The browser's address bar is correctly updated by calling $location.path(), but the page is not reloaded.
I know it's a kind of a common problem, with several potential causes.
I think I am correctly inside angular lifecycle, since successful authentication is either triggered by an $http.post response or simulated via and angular $timeout (neither method works)
Adding $scope.apply() does not help anyway
Setting for example $location.absUrl('http://google.com') instead has no effect at all.
Here is the login controller:
var swControllers = angular.module('swControllers', []);
swControllers.controller('LoginCtrl', ['$scope', '$location', 'authentication',
function($scope, $location, authentication) {
authentication.ClearCredentials();
$scope.login = function () {
$scope.dataLoading = true;
authentication.Login($scope.email, $scope.password, function(response) {
$scope.dataLoading = false;
if (response.success) {
authentication.SetCredentials($scope.email, $scope.password);
$location.path('userHome');
//$location.absUrl('http://google.com');
//$scope = angular.element(document).scope();
//$scope.$apply();
} else {
$scope.error = response.message;
// $scope.dataLoading = false;
}
});
};
}]);
I have more general architectural concerns: is it cleaner to rely on ngRoute for this kind of stuff? Does this mean I'm limited to a single-page app?
Because I'm using Express + jade template views on the server side, and currently login page and userHome reside on two different templates.
I'm a bit dazzled by the interplay between server-side and client-side routing...

Related

how to do service call when trying to close the browser tab or window using AngularJs

am trying to implement service call when closing the browser tab or window. Is there any way to call RestApi trying to close the browser tab or window.
Are there any suggestions?
This is a bad idea for a few reasons. The closing of the browser does not mean they have left because they can just re-open or even refresh the page (yes refreshing the page is same as closing the browser). However to directly answer your question yes you can by using the injected $http provider in your controller (assuming you did not mean the angular 2+ but actually the old angular 1.x):
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
window.addEventListener('beforeunload', function () {
$http({ method : "GET", url : "your endpoint url here" })
.then(function (response) {
$scope.myVariable = response.data;
},
function (response) {
$scope.myVariable = response.statusText;
});
}
});
Please use the following code to run the function on close the browser
constructor() {
window.onbeforeunload = ()=>{
//call API here
}
}

Browser directly calling back-end URL instead of UI app URL

I am 3 weeks old to Angular so a little confused about how it works.
Setup: We have 1 container running 2 applications (UI and Backend) UI runs on 8080 and backend runs on 8181.
Issue: When we submit the credentials on the login page, browser is making a direct call to the back-end to check if the user exists. I traced this down by looking at the network calls in Browser's developer tools. Not sure how the framework is working but how does the browser directly call the back-end instead of the UI app. As the application will mature, back-end port won't be even open for outside communication.
Details - I am implementing a login page - It basically calls the UI app to render the login.html and once the user submits the page with user ID & password; it goes to the controller which goes makes an API call to check if the user exists in DB
Browser(ClientUIIP:8080) --> UI --> Backend(check if user exists)
I have a controller.js with below code
var serverPath = 'http://localhost:8181/backend'
//Backend API URL's
var users = '/users';
<code>
function LoginCtrl($scope, $state, $http, $rootScope){
this.cancel = $scope.$dismiss;
$scope.showLogin = true;
$scope.message = "";
this.submit = function(username, email){
$http.get(serverPath + users + "/" + username)
.success(function(response){
if(response.name === username){
$scope.currentUser = username;
window.localStorage.setItem('CurrentUser', JSON.stringify(username));
$scope.showLogin = false;
return $state.go('index.main')
}else{
$scope.message = "Login not valid, please try again";
}
});
};
this.logout = function () {
window.localStorage.setItem('CurrentUser', null);
$scope.currentUser = null;
return $state.go('index.login')
};
if(getCurrentUser() != null){
$scope.showLogin = false;
}
};
Please let me know if anybody has an idea what needs to be changed so the browser calls the UI and then UI can make the call to back-end if the user exists or not.
Thank you

Sending data (received from backend) from on html to another with angularJS

I feel like tons of people do this all the time, and yet I have been unsuccessful in finding similar examples.
I am getting data from the backend using angularJS ($http.post) to a controller in a javascript file, and presenting it in one html. The data is received after sending a search query from that html. Now, I want to "export" that data to another JS file and to present some of it again, in a new html. The problem is that I have access to that data only through the Search Controller during the searching session, and I probably need to store it somehow or send it to another controller/ JS file.
Unfortunately, I cannot use $cookies. Also, I am trying to avoid sending a new request through the server if I don't have to.
I have read a some about services in angular, however, I am new to angular (and UI in general), and for some reason was unable to implement this for my specific case.
Here is an example of the relevant controller, after getting a search request from the html page:
app.controller('SearchCtrl', ['$scope', '$http',
function($scope, $http) {
$scope.searchJSON = {
searchToken: [],
searchOption: []
$scope.sendSearch = function() {
//preparing JSON to send request to server
$scope.searchJSON["searchToken"] = this.search.searchToken;
$scope.searchJSON["searchOption"] = this.search.searchOption;
var json = $scope.searchJSON;
//sending and getting response (JSON object)
$http.post("http://some_host", json)
.success(function(response) {
$scope.collections = response.searchResults;
});
};
}]);
So the data I am interested in passing on to another JS file is in $scope.collections , which is a JSON file (I don't want use the same JS file for both html pages, so was hoping to call that data from a new controller in a new JS file).
Will appreciate any answers, leads, or similar examples from the web. Thank folks!
One possible way to solve this is by using sessionStorage/localStorage on $window. You can store your data there and after redirecting to another file, you can use it by invoking.
You are right to bring up services because that is how I would personally implement this. Create a service to handle the search request and also to store the result via promises:
angular.module('yourModule')
.factory('searchService', function($http, $q) {
var searchService = {
resultsPromise: null,
sendSearch: function(token, option) {
var dfd = $q.defer();
var json = {
searchToken: token,
searchOption: option
};
$http.post("http://some_host", json).then(
function(response) {
// resolve your deferred
dfd.resolve(response.data);
},
dfd.reject
);
this.resultsPromise = dfd.promise;
return dfd.promise;
}
};
return searchService;
});
Then in your current controller, just do:
app.controller('SearchCtrl', ['$scope', 'searchService',
function($scope, searchService) {
$scope.searchJSON = {
searchToken: [],
searchOption: []
$scope.sendSearch = function() {
searchService.sendSearch($scope.searchJSON.searchToken, $scope.searchJSON.searchOption);
};
Then in your other file, simply look at the currentResults of the same service:
app.controller('OtherCtrl', function($scope, searchService) {
if (searchService.resultsPromise) {
searchService.resultsPromise.then(function(results) {
$scope.results = results;
});
}
});
You can ditch the $http service and use $resource instead. From there you can use $cacheFactory as seen in this post: How to cache an http get service in angularjs
An alternative solution is http://jmdobry.github.io/angular-cache/ which works well with ngResource and can also easily be configured to sync to localStorage, so requests don't need to be re-done after page refresh.
`$resource('my/kewl/url/:key', { key: '#key' }, {
'get': { method: 'GET',
cache: $angularCacheFactory('MyKewlResourceCache', {
storageMode: 'localStorage' })
}
});`

Angularjs: Interceptor redirection creates double rendering

I am stuck with this strange behaviour. None of the Google hits seem to return any mention of a similar case so I am assuming that I am doing something seriously wrong.
My interceptor does react on the 401 status and redirects to the appropriate route but at the same time it renders the previous route too. So I have a very ugly flash of one template then the other. I am simply trying to test my authentication: if authenticated then render the table, otherwise render the login form. It does redirect to the login form but still flashes the table template for a fraction of a second.
angular.module('Basal', ['ngRoute', 'Basal.users'])
.config(function($routeProvider, $locationProvider, $httpProvider) {
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
$routeProvider.
when('/', {
templateUrl: '/tpl/table.html',
controller: 'MainCtrl'
}).
when('/login', {
templateUrl: '/tpl/login-form.html',
controller: 'MainCtrl'
}).
otherwise({
redirectTo: '/'
});
});
angular.module('Basal.users', [])
.controller('MainCtrl', function($scope, getJson) {
getJson.fetch(function (d){
$scope.computers = d;
});
})
.factory('getJson', function($http, $location) {
return {
fetch: function (c) {
$http.get("/json")
.success(function(data) {
console.log("getJson: Success!");
c(data);
})
.error(function() {
console.log("getJson: Failure!");
});
}
}
})
.factory('authInterceptor', function($q, $location) {
return {
'responseError': function(response) {
if (response.status === 401) {
$location.path('/login');
}
return $q.reject(response);
}
}
});
Now, when I hit '/' on the browser Angular does two requests on the background: one is to fetch the table template and insert it in to the view and the other is to get the JSON data.
On the server side I put session restriction only on the data request! The template is just a static file. To put restriction on the template I need to drag it through the server routing and this is not, I believe, how Angular does things. But if I do create server side route for the template and put session restriction on it then double rendering disappears.
I am confused about how this works. Fetching the template and fetching the data is done asynchronously in parallel. So while JSON request triggers 401 and redirects to the login template, the original table template is still going through and being rendered empty. Hence I get all the ugly double rendering. This is a race of some kind. How do I stop it? Maybe my interceptor is wrong? Isn't the Angular interceptor supposed to stop any further processing?
As a related issue, the otherwise function on the $routeProvider does not work either. If I put a non-existent URL, I get 404 from the server but Angular routing does not catch it. Is this how it is supposed to be? If a URL change happens in Angular and then I hit reload on the browser, I get an ugly 404 instead of a nice redirect. Am I supposed to handle 404 in an interceptor too? Then what is the point of otherwise?
Thanks.

Angular - Loading a view without changing the url

I'm working on a project which has several views, which some of the views may not be accessible to a given user. What I want to do is that, if a user navigates to a url that is restricted (due to his permissions), lets say '/project/manhatten/security' I want to display a view (an html partial) which simply says 'Access Denied'.
But I want to display the view without changing the url. I want the url to stay '/project/manhatten/security', so the user can copy the url and give it to someone with enough permission, and it would work fine.
What is the best way to achieve this ? Can I still use ng-view or a combination of ng-view and ng-include ?
Thanks in Advance.
I don't know of a way on how to restrict access to a specific view in angular. I think you shouldn't restrict views. What you should do is restrict access to your api. So if a user doesn't have the privilege to delete a project. Simply send a 401 from the server when he calls the api. On the client side handle this 401 in angular with an $http interceptor.
I would do the following:
In your index.html create an element/directive to display the error message
index.html
<div ng-controller=ErrorMessageCtrl ng-show=error.message>
{{error.message}}
<ng-view></ng-view>
The ErrorMessageCtrl will get notified when an access denied error occured:
.controller('ErrorMessageCtrl', function ($scope) {
$scope.error = {}
$scope.$on('error:accessDenied', function(event, message) {
$scope.error.message = message
})
})
Create an interceptor service to handle http 401 auth error:
.factory('authErrorInterceptor', function ($q, $rootScope) {
return {
response: function (response) {
return response
},
responseError: function(rejection) {
if (rejection.status === 401) {
$rootScope.$broadcast('error:accessDenied', 'Access Denied')
}
return $q.reject(rejection)
}
}
})
add the interceptor service to $httpProvider
.config(function ($httpProvider, $routeProvider) {
$httpProvider.interceptors.push('authErrorInterceptor')
$routeProvider.when('/project/manhatten/security', {
template: '<div><h1>Secure Page</h1>secure data from server: {{data}}</div>',
controller: 'SecureDataCtrl'
})
})
$http.get('/api/some/secure/data') returns a 401 http status code
.controller('SecureDataCtrl', function ($scope, $http) {
$scope.data = 'none'
$http.get('/project/manhatten/security')
.success(function (data) {
$scope.data = 'secure data'
})
})
Please keep in mind that this was hacked in 10 minutes. You need to do some refactoring. Like creating an errorMessage directive which gets the error notification service injected and not broadcasting the error message to the scope.

Categories

Resources