Back Arrow and Angular Routing - Press Back Twice - angularjs

Angularv1.1.5
Site: http://tilsa.azurewebsites.net
I have a very simple route setup however when the user goes from the default/home route to the detail (pregunta) route and then clicks the back button nothing happens. The 2nd/3rd time the back button is clicked the user returns (chrome) to the default/home route. I'm not sure as to how or why this is happening.
$routeProvider.
when('/', {
templateUrl: '/js/app/partial/index.html',
controller: 'IndexCtrl'
})
.when('/pregunta/:id', {
templateUrl: '/js/app/partial/detalle.html',
controller: 'PreguntaDetalleCtrl'
}).
otherwise({
redirectTo: '/'
});
Here are the two relevant controllers. I've removed some of the code that doesn't seem relevant (polling for new info/etc):
// load the index list of questions, the actual questions are loaded in parent scope
.controller('IndexCtrl', ['$scope', 'services', 'data', '$modal', 'navigation', 'timeFunctions', function ($scope, services, data, $modal, navigation, timeFunctions)
{
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/';
$scope.url = encodeURIComponent($scope.noEncodeUrl);
// controls the back arrow visibility to go back
navigation.setReturn(false);
}])
.controller('PreguntaDetalleCtrl', ['$scope', '$routeParams', 'services', 'navigation', 'graphService', 'stringFx', '$timeout', 'timeFunctions', function ($scope, $routeParams, services, navigation, graphService, stringFx, $timeout, timeFunctions) {
$scope.notas = [];
$scope.comentario = '';
navigation.setReturn(true);
$scope.loadPregunta = function (id, loadComments)
{
services.preguntas.getDetalle(id).then(function (data)
{
$scope.safeApply(function ()
{
$scope.pregunta = data;
graphService.setProp('title', $scope.pregunta.pregunta);
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/pregunta/' + id;
$scope.url = encodeURIComponent($scope.noEncodeUrl);
$scope.preguntaText = stringFx.removeAccent('¿'+$scope.pregunta.pregunta+'?');
});
if (loadComments)
{
$scope.commentTracker = {
defaults: { },
skip: 0,
take: 20
};
$scope.$on('$destroy', function (e)
{
$scope.stopPolling();
});
$scope.startPolling = function ()
{
// scrollTimeout will store the unique ID for the $setInterval instance
return $scope.scrollTimeout = timeFunctions.$setInterval(poll, 10000, $scope);
// Function called on interval with scope available
function poll($scope)
{
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
}
}
$scope.stopPolling = function ()
{
return timeFunctions.$clearInterval($scope.scrollTimeout);
}
$scope.startPolling();
$scope.cargarAnteriores = function ()
{
//$scope.commentTracker.skip++;
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas, true).then(function (data)
{
$scope.safeApply(function ()
{
$scope.notas = $scope.notas.concat(data.notas);
$scope.masNotas = $scope.notas.length > 0;
});
});
}
$scope.cargarAnteriores();
}
});
}
$scope.notaNueva = function () {
//$scope.commentario;
if ($scope.comentario.length < 3)
{
alert('Escribe algo mas, no seas tacano con tus palabras');
return;
}
$scope.processing = true;
services.preguntas.insertNota($scope.pregunta.id, $scope.comentario, $scope.notas, false).then(function (data)
{
$scope.comentario = '';
$scope.processing = false;
$scope.loadPregunta($scope.pregunta.id, false);
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
});
}
$scope.loadPregunta($routeParams.id, true)
$scope.$on('updatedpregunta', function (event, obj)
{
$scope.loadPregunta(obj, false)
});
}]);

I had this issue as well! Turned ut that artur grzesiak was right! I had a iframe on my page that had a binding for its src-attribute.
<iframe src="{{selected.url}}"></iframe>
Since the default value of $scope.selected.url was null the first thing that happened was that it was loading a url called null.
After some research I found that there was a special directive for the iframe:
<iframe ng-src="{{selected.url}}"></iframe>
This change solved my is

It seems that the Angular side of your app is fine.
99% the problem is caused by some external library. For sure there is some problem with this script kVEquaeit4R (it seens to be a facebook plugin), as it fails to load some resource (404 error): The resource you are looking for has been removed, had its name changed, or is temporarily unavailable. and as a consequence a couple of further errors are generated (look at the console). And in turn it prevents the app from calling window.location.hostname.replace what actually is present in the kVEquaeit4R script.
So my suggestion is as follow: remove this fb plugin from your site and check if the routing works properly...

Related

AngularJS Dynamically Load Controller and Template based on Route

I am trying to dynamically load BOTH a template and controller based on the route (in Angular 1.6), pulling from the current directory architecture.
app
|__login.module.js
|__home.controller.js
|__login.factory.js
|__home.view.html
|__404.view.html
|__index.html
Currently, the below code works to pull the proper template, but the controller won't load:
angular.module('common.auto-loading-routes', []).config(function($routeProvider){
function getTemplate(routeParams){
var route = (routeParams.current) ? routeParams.current.params.route : routeParams.route;
var directory = route.split('/');
directory = directory.filter(function(entry) { return entry.trim() != ''; });
var page = directory[directory.length - 1];
directory.splice(-1,1);
directory = directory.join('/');
return directory + '/' + page +'.view.html';
}
function getController(routeParams){
//I think I need a promise here, but I don't know how to write it
routeParams = routeParams.route.split('/').join('.');
return routeParams + 'controller';
}
$routeProvider.when('/:route*', {
controller: function($routeParams) { //does not work
return getController($routeParams);
},
templateUrl: function($routeParams) { //works
return getTemplate($routeParams);
},
resolve: {
check: function($route, $http, $location){
return $http.get(getTemplate($route)).then(function(response){
if (response.status !== 404){
return true;
} else {
$location.path('404');
}
});
}
}
}).otherwise('/404');
});
I understand that controllers need to be present at the start of the app, but I am unable to write a proper resolve with a promise statement.
Can someone help me right a resolve with a simple promise that returns a string of the controller name based on the route?
I was able to get it working by not including the controller in the routing at all. Instead I put the ng-controller attribute in the view I was loading.
This worked!
angular.module('common.auto-loading-routes', []).config(function($routeProvider){
function getTemplate(routeParams){
var route = (routeParams.current) ? routeParams.current.params.route : routeParams.route;
var directory = route.split('/');
directory = directory.filter(function(entry) { return entry.trim() != ''; });
var page = directory[directory.length - 1];
directory.splice(-1,1);
directory = directory.join('/');
return directory + '/' + page +'.view.html';
}
$routeProvider.when('/:route*', {
templateUrl: function($routeParams) { //works
return getTemplate($routeParams);
},
resolve: {
check: function($route, $http, $location){
return $http.get(getTemplate($route)).then(function(response){
if (response.status !== 404){
return true;
} else {
$location.path('404');
}
});
}
}
}).otherwise('/404');
});
In the HTML of that view, I just put ng-controller="home.controller"(or whatever controller is appropriate)

Redirection error in angularjs

After adding data in location table, clicking on the save button should redirect it to list of data in location table.But ,it stays in the same page after adding.The same path is given to modify location,it works fine. whereas the same path does not redirect when add location.
function locationController($scope, $state, $rootScope, locationServices,$location, locations, location, primaryLocation, $stateParams,locationTypes, countries) {
var vm = this;
$scope.locations = locations.data;
$scope.location = location.data;
if (primaryLocation.data && primaryLocation.data[0])
$scope.primaryLocation = primaryLocation.data[0];
if (!$scope.location) {
var location = {};
if ($stateParams.accountId) {
$scope.location = {accountId: $stateParams.accountId };
} else {
$scope.location = location;
}
}
$rootScope.title = "Locations";
$scope.locationslist = "views/locations.html";
$scope.addOrModifyLocation = function (location) {
if (location._id) {
locationServices.modifyLocation(location).then(function (response) {
$location.path('/account/locations/contacts/' + location.accountId + '/' +location.accountId);
// $state.reload();
})
} else {
location.status = 'ACTIVE';
locationServices.addLocation(location).then(function (response) {
$location.path('/account/locations/contacts/' + location.accountId + '/' +location.accountId);
})
}
};
If you want angular to know about your $location update, you have to do it like this :
$rootScope.$apply(function() {
$location.path("/my-path"); // path must start with leading /
});
If you're using ui-router, a cleaner approach would be to use
$state.go('stateName', {'accountId' : location.accountId, });
edit :
If you have errors that happen during a state change, you can see it by adding the following code in your app after declaring your module :
angular.module("appName").run([
"$rootScope",
function ($rootScope) {
$rootScope.$on("$stateChangeError", function(error) {
console.log(error);
});
}
]);

AngularJS: Append to url the page number while paginating

I am working on a application where i am paginating through some records by making calls to the server like random/api/endpoint?page=1/2/3
Now i while i paginate,
i need to append the page i am requesting to the url like http://www.paginate.com/somedata/{1/2/3} and on opening this url it should also fetch that specific page in the view {meaning if i navigate to hhtp://www.paginate.com/somedata/4 then the app/view should reflect data from the api call random/api/endpoint?page=4}.
Presently i am using angular-route 1.4.12 with the same version of AngularJS. Very new to angular (2 days), any help will be greatly appreciated.
EDIT : What i want to do ?
When i click next while paginating, it should append the pageNumber to the url.
route.js
angular
.module('mainRouter', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/somedata/:page', {
templateUrl: 'partials/somedata.html',
controller: 'PaginationCtrl',
controllerAs: 'vm',
reloadOnSearch: false
}).
otherwise( { redirectTo: "/somedata/1" });
}
]);
PaginationCtrl.js
angular
.module('controllers.Pagination', [])
.controller('PaginationCtrl', PaginationCtrl);
PaginationCtrl.$inject = ['$routeParams', 'paginationService'];
function PaginationCtrl ($routeParams, paginationService) {
var vm = this;
vm.paginationData = {
perPage: 10,
currentPage: 1
};
vm.isLoading = false;
vm.paginate = paginate;
paginate(vm.paginationData.currentPage);
calculateTotalPages();
function calculateTotalPages () {
paginationService.findAll(0, 0)
.success(function (res) {
var paginationData = vm.paginationData || {};
paginationData.totalPages = Math.ceil(res.count / paginationData.perPage);
})
.error(function (res) {
console.log('Error trying to get the total number of pages', res);
});
}
function paginate (pageNumber, perPage) {
vm.isLoading = true;
var paginationData = vm.paginationData || {};
if (! perPage) {
perPage = paginationData.perPage;
}
console.log($routeParams);
paginationService.findAll(perPage, pageNumber)
.success(function (res) {
paginationData.items = res.documents;
vm.isLoading = false;
})
.error(function (res) {
console.log('Error fetching more Logs', res);
});
}
}
PaginationService.js
angular
.module('services.Pagination', [])
.service('paginationService', PaginationService);
PaginationService.$inject = ['$http', 'Constants'];
function PaginationService ($http, Constants) {
// console.log($http);
this.findAll = function (perPage, page) {
var url = Constants.baseUrl + '/sms/get/data';
if (page > 0) {
url += '?page=' + page;
}
return $http.get(url);
};
}
directive being used
var app = angular.module('directives.Pagination', []);
app.directive('pagination', [function () {
return {
restrict: 'E',
template: '<div class="ui pagination menu"> \
<a class="icon item" ng-click="vm.previous()"><i class="left arrow icon"></i></a> \
<div class="icon item">{{ vm.paginationData.currentPage }} / {{ vm.paginationData.totalPages }}</div> \
<a class="icon item" ng-click="vm.next()"><i class="right arrow icon"></i></a> \
</div>',
scope: '=',
link: function (scope, element, attrs) {
var vm = scope.vm;
vm.paginationData.currentPage = 1;
vm.next = function () {
vm.paginationData.currentPage++;
if (vm.paginationData.currentPage > vm.paginationData.totalPages) {
vm.paginationData.currentPage = vm.paginationData.totalPages;
}
vm.paginate(vm.paginationData.currentPage);
};
vm.previous = function () {
vm.paginationData.currentPage--;
if (vm.paginationData.currentPage < 1) {
vm.paginationData.currentPage = 1;
}
vm.paginate(vm.paginationData.currentPage);
};
}
};
}]);
You should be able to access your :page parameter via $routeParams, which you've already injected in your controller.
Just call paginate with $routeParams.page instead of your default of 1.
In order to update the url as you go (in such a way that allows the user to copy the url for later use), without updating the route and re-initializing the controller, you can just call $location.search({page: page}). When this is called with reloadOnSearch set to false (as you've already done) it shouldn't re-initalize the controller.
Lastly, in case its not clear, you'll have to update the URL at the same time you make your API call. There isn't a built in angular way to do this, but it should be pretty straightforward.

How to pass data between views using stateParameters (not routes) in AngularJS

I have some views in my applications, and I have hard time to show the data when moving from one view to another.
I have a list of news and when I click on the particular news I want the view for that particular news to be shown. Here is my code:
My app.js :
.state('app.list', {
url: "/list",
views: {
'appScreen': {
templateUrl: "list.html",
controller: 'List.Ctrl'
}
}
})
.state('app.singleview', {
url: "/list/:newsId",
views: {
'appScreen': {
templateUrl: "single.html",
controller: 'SingleCtrl'
}
}
})
My controllers:
ListCtrl.$inject = ['$http', '$scope', 'datacontext'];
function ListCtrl( $http, $scope, datacontext) {
$scope.list = [];
datacontext.getPosts().then(function (posts) {
console.log('posts', posts);
$scope.list= posts;
}, function(reason) {
alert(reason);
});
The following controller is the one which will show me the single news and I have written some code but is not correct. In the URL I get the ID but I can't manage to show the data for that ID.
SingleCtrl.$inject = ['$scope', '$stateParams', 'datacontext'];
function ListNewCtrl($scope, $stateParams, datacontext) {
$scope.New = getNewsById($stateParams.newsId);
function getNewsById(id) {
datacontext.getPosts().then(function(posts) {
var found = null;
for (var i = 0; i < posts.length; i++) {
if (posts[i].id == id) {
found = posts[i];
break;
}
}
return found;
})
}
};
So in this controller what I am trying to do is get the ID and match it with postsId, and then show the data accordingly but it does no seem to work
You're confused with the asynchronism. The code should be
getNewsById($stateParams.newsId);
function getNewsById(id) {
datacontext.getPosts().then(function(posts) {
var found = null;
for (var i = 0; i < posts.length; i++) {
if (posts[i].id == id) {
$scope.New = posts[i];
break;
}
}
});
}
So that, when the success callback is executed, the New scope variable is initialized by the found post.
That said, I have a hard time understanding why you're getting a whole list of posts from the backend instead of using a REST service returning a single post by ID. If you did, it would be reduced to
function getNewsById(id) {
datacontext.getPost(id).then(function(post) {
$scope.New = post;
});
}

using ng-include for a template that has directives that use data retrieved by XHR

I am using ng-include in order to include a persistent menu, that exists in all of the views of my SPA.
The problem is that I want to display different options and content in this menu per each user type(admin, guest, user etc.), and this requires the service function authService.loadCurrentUser to be resolved first.
For the purpose of managing this content easily and comfortably, I have created a simple directive, that takes an attribute with the required access level, and at the compile phase
of the element, if the permissions of the given user are not sufficient, removes the element and it's children.
So after failing miserably at trying to make the ng-include go through the routeProvider function, I've tried to use ng-init, but nothing seems to work, the user role remain undefined at the time that I am logging it out.
I am thinking about trying a new approach, and making the entire menu a directive that includes the template that is suitable for each user type, but first I would like to try and solve this matter.
Directive:
'use strict';
/* Directives */
angular.module('myApp.directives', []).
directive('restrict', function(authService){
return{
restrict: 'A',
prioriry: 100000,
scope: {
// : '#'
},
link: function(){
// alert('ergo sum!');
},
compile: function(element, attr, linker){
var user = authService.getUser();
if(user.role != attr.access){
console.log(attr.access);
console.log(user.role);//Always returns undefined!
element.children().remove();
element.remove();
}
}
}
});
Service:
'use strict';
/* Services */
angular.module('myApp.services', []).
factory('authService', function ($http, $q) {
var authServ = {};
var that = this;
that.currentUser = {};
authServ.authUser = function () {
return $http.head('/users/me', {
withCredentials: true
});
},
authServ.getUser = function () {
return that.currentUser;
},
authServ.setCompany = function (companyId) {
that.currentUser.company = companyId;
},
authServ.loadCurrentUser = function () {
var defer = $q.defer();
$http.get('/users/me', {
withCredentials: true
}).
success(function (data, status, headers, config) {
console.log(data);
that.currentUser.company = {};
that.currentUser.company.id = that.currentUser.company.id ? that.currentUser.company.id : data.main_company;
that.currentUser.companies = [];
for (var i in data.roles) {
that.currentUser.companies[data.roles[i]['company']] = data.roles[i]['company_name'];
if (data.roles[i]['company'] == that.currentUser.company.id){
that.currentUser.role = data.roles[i]['role_type'];
that.currentUser.company.name = data.roles[i]['company_name'];
// console.log(that.currentUser.role);
}
}
// defer.resolve(data);
defer.resolve();
}).
error(function (data, status, headers, config) {
that.currentUser.role = 'guest';
that.currentUser.company = 1;
defer.reject("reject");
});
return defer.promise;
}
return authServ;
});
Menu controller:
angular.module('myApp.controllers', []).
controller('menuCtrl', function($scope, $route, $location, authService){
//TODO: Check if this assignment should be local to each $scope func in order to be compliant with 2-way data binding
$scope.user = authService.getUser();
console.log($scope.user);
// $scope.companies = $scope.user.companies;
$scope.companyOpts = function(){
// var user = authService.getUser();
if(typeof $scope.user.company == 'undefined')
return;
var companies = [];
companies[$scope.user.company.id] = $scope.user.company.name;
for(var i in $scope.user.companies){
if(i != $scope.user.company.id){
companies[i] = $scope.user.companies[i];
}
}
// console.log(companies);
// if(nonCurrentComapnies.length > 0){
console.log(companies);
return companies;
// }
}
$scope.$watch('user.company.name', function(company){
for(var i in $scope.user.companies)
if(company == $scope.user.companies[i].id)
authService.setCompany(i);
});
$scope.$watch(function(){return authService.getUser().company; }, function(company){
//Refresh the page on company change here, first time, and each time the user changes the select
// $scope.companyOpts();
// $scope.currentComapany = company;
})
;})
Main SPA HTML page:
<div ng-init="authservice.loadCurrentUser" ng-include src="'partials/menu.html'"></div>
menu element that should be visible only to the admin:
<ul class="left" restrict access="admin">
<li>You are the admin!</li>
</ul>
Thanks in advance for any assistance!
I personally would do the "reverse" way. Which mean: I will add the menu in when the user role is "admin", or "user", etc...
This way, you can do something like this in the "restrict" directive:
...
var roleListener = $scope.$watch('user.role', function (newVal, oldVal) {
if (newVal == $scope.access) {
// add the menu items
// supposed that loadcurrentuser be called only once
// we should clear the watch
roleListener();
} else {
// personally, I would remove the item here too
// so the menu would be added or removed when user.role update
}
});
...
One more thing, for just display menu base on the user role, you can use ngSwitch, something like this:
<ul class="left" ng-switch="user.role">
<li ng-switch-when="admin">You are the admin!</li>
<li ng-switch-when="user">You are the user!</li>
<li ng-switch-default><img src="some-thing-running.gif"/>Your menu is loading, please wait...</li>
</ul>
And let the magical AngularJS binding render up the menus for you!
The call to authServ.getUser should also return a promise by calling internally
authServ.loadCurrentUser
which should be modified a bit to check if the user context exists to avoid making another API call and always returning resolve with the user context:
defer.resolve(that.currentUser);
Loading the user context should also be done early on as this enables the authorization of the app. The app.run function can be used for this purpose.
hope it helps others.

Resources