Angular ui.router executing more than one state - javascript

I'm using AngularUI's router library and I have a strange issue: I have two distinct but similar states and controllers. What occurs is the state that I would expect runs, but then another state appears to run: it fetches the templateUrl and then the controller runs for the second state, which causes some weird behavior.
The states in question:
angular.module('app', ['ui.route'])
.config(function ($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('list', {
url: "/",
templateUrl: STATIC_URL + "js/eleagemot/views/list.html",
controller: 'questionListCtrl',
})
.state('detail', {
url: '/{username:[a-zA-Z0-9\\-]+}/{slug:[a-z0-9\\-]+}/',
templateUrl: STATIC_URL + "js/eleagemot/views/detailed_question.html",
controller: 'questionDetailCtrl'
})
.state('newQuestion', {
url: '/new-question/',
templateUrl: STATIC_URL + "js/eleagemot/views/edit_question.html",
controller: 'editQuestion'
})
.state('questionEdit', {
url: '/{username:[a-zA-Z0-9\\-]+}/{slug:[a-z0-9\\-]+}/edit/',
templateUrl: STATIC_URL + "js/eleagemot/views/edit_question.html",
controller: 'editQuestion'
})
.state('answerEdit', {
url: '/answer/{id:[0-9]+}/edit/',
templateUrl: STATIC_URL + "js/eleagemot/views/edit_answer.html",
controller: "editAnswer"
})
});
Exactly what happens is if I go to /answer/1/edit/ it first loads the edit_answer.html template and runs the editAnswer controller, as it should. But then it also loads the edit_question.html template and runs the editQuestion controller. I know because there's some Restangular code that runs correctly in the editAnswer controller and then some similar Restangular code that runs in the editQuestion controller, but that causes a 404 and then the router redirects to the root. Also putting some console logs at the top of each controller shows that the editAnswer controller runs and then the editQuestion controller.
This behavior is only seen when loading the url for the answerEdit state, and not for any of the other states. Why is this happening and how can I fix it?

Looks like you've got some regular expressions that match too easily. Note that /answer/{id:[0-9]+}/edit/ always matches the above url pattern: '{username:[a-zA-Z0-9\\-]+}/{slug:[a-z0-9\\-]+}/edit/

Related

AngularJS 1.5 Controllers in Separate Files

I have a hard time understanding this. I'm attempting to put controllers in separate files so that they only deal with 1 thing, ideally, a partial view
My folder structure is like this...
My app.js file is like this.
angular.module('mikevarela', ['ui.router', 'mikevarela.controller.home', 'mikevarela.controller.about', 'mikevarela.controller.audio'])
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: '../partials/home.partial.html',
controller: 'HomeController'
})
.state('about', {
url: '/about',
templateUrl: '../partials/about.partial.html',
controller: 'AboutController'
})
.state('audio', {
url: '/audio',
templateUrl: '../partials/audio.partial.html',
controller: 'AudioController'
});
});
and my controllers each have a module like this...
angular.module('mikevarela.controller.home', [])
.controller('HomeController', ['$scope', function($scope) {
$scope.title = 'Mike Varela Home Page';
}]);
My issues comes with the intial app declaration. I don't want to have to inject all the controllers in the main array app definition, that would be cumbersome and long winded. Isn't there a way to define the controller at the controller file. Kind of like this
angular.module('mikevarela', []).controller('HomeController', ['$scope', function($scope) {
// stuff here
}]);
Use angular.module('mikevarela').controller..... in subsequent files.
angular.module('mikevarela',[]).controller.....
is equivalent to redefining your app. The second param is requires array.
Quoting official angular.module docs
requires
(optional)
!Array.=
If specified then new module is being created. If unspecified then the module is being retrieved for further configuration.
About your Controllers...
I think you're loading the controllers incorrectly.
You don't need to declare controllers as a dependency. Rather stating module.controller('yourController)` makes that controller available throughout the module.
If your controllers are in separate files, all you need to do to make it available is load it in with a script tag. e.g.
<script src="app.js"></script>
<script src="controller1.js"></script>
<script src="controller2.js"></script>
About your Application Structure...
This is not related to your question, but just coming from someone who's developed using Angular, I'd recommend not grouping your application by controllers/ by rather by feature. See: https://scotch.io/tutorials/angularjs-best-practices-directory-structure

AngularJs ui-sref not working, and state abstracts

For some reason my ui-sref links are not updating and allowing me to change state on my app.
Can someone please tell me what i have done wrong? I have attached a plunkr link for the full code
App.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
'use strict';
// defaults to home
$urlRouterProvider.otherwise('/home');
// states
$stateProvider
.state('app', {
url: '',
abstract: true,
templateUrl: 'app.html',
controller: 'AppController'
})
.state('app.home', {
url: '/home',
templateUrl:'home.html',
controller: 'HomeController'
})
.state('app.settings', {
url: '/settings',
templateUrl: 'settings.html',
controller: 'SettingsController'
});
}]);
http://plnkr.co/edit/m77wrOU0sMLG0fmicTaK
If i navigate to /home, this works and if i go to /settings that also works. but the links are not generated on my pages?
Also, if i want to have multiple layouts, say i would like an admin layout and a normal user layout, maybe the admin layout would hide a few items on the page and show others, would this be best to be done using routing? I have about 6 different parts of the page, currently not setup as views, but i wonder if this is the route i should go down?
Is there anything wrong with having more than 1 abstract state in your stateProvider, or is that stupid?

Angular JS Routing - Links inside SPA

I have created a project with index.html with certain links to other pages. My routing works as intended but I'm wondering what's the best approach to go with when it comes to links on other pages.
To clarify it:
My index.html page has routes:
Feed
Bblog
Marketplace
Recruiting
Adverts
Now what I'm curious about is how do I for example route links inside these pages.
For example, my Bblog page has tabs which I want to be opened inside the same page. Now for example whenever I click some tab link, it redirects me to my index.html since my .otherwise route is set to /.
Not sure what engine or library you're using for your routing. Though I faced the same requirement not too long ago.
We're using ui-router for our routing. It's very similar to Angulars routing.
A snippet from our routing table contains something similar to this.
$stateProvider
.state('home', {
url: '/',
templateUrl: '/views/index',
})
.state('orders', {
url: '/orders',
templateUrl: '/views/orders',
})
.state('orderdetail', {
url: '/orders/detail/:id',
templateUrl: '/views/orderdetail',
})
.state('orderdetail.address', {
url: '/:addressId',
templateUrl: '/views/orderdetail',
})
Essentially you use the .dot notation to separate nested views. So the orderdetail.address is nested inside the orderdetail
This means that the routing above will go something allow you to see an overview of order details at /orders/detail/myOrderId and drill further in to, say, an address by visiting /orders/detail/myOrderId/myaddressId
If you're using ui-router then you will get more info on nested views on this link
If you're using angular ngRoute then the [ngRoute][3] docs and supporting plunker demonstrate how to stack up the routes.
So (from the plunker) -
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookController',
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
})
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterController'
});
this will give you /book/myBookId and /book/myBoodId/ch/myChapterId

Can't fix the hashtag in my angularjs app path

I know you guys are going to say "It's duplicated" but it doesn't work for me.
I'm doing exactly the same thing and here's what happens.
This is my config code:
portfolioApp.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/home', {
controller: 'portfolioController',
templateUrl: 'partials/home.htm'
})
.otherwise({redirectTo: '/home'});
if(window.history && window.history.pushState){
$locationProvider.html5Mode(true);
}
});
The URL without the locationProvider and setting path to "/" instead of "/home" would be:
http://localhost:8080/portfolio/#/
Okey, i would like to remove the hashtag and the "portfolio" path so that way it would be:
http://localhost:8080/home
With the config i've posted, it does it (the first time i refresh). But what happens? happens that if I refresh the page again with the new path... i get a 404 Tomcat Error.
In the first refresh when i write localhost:8080/portfolio and the path gets converted into "/home", I get in the console an error saying that the "partials/home.htm" can't be found 404.
So either way, it can't read my partial, and then when I refresh everything is broken.
How can I solve this? what am I doing wrong?
Make sure your including these to scripts in the header of your HTML document Where you are implementing your module. The two provided below are for implementing the most current version of angular as well as the most current version of the angular route script:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular-route.js"></script>
Make sure your injecting ngRoute into your module, here's an example of mine:
angular.module('controller', ['ngRoute','ngResource', 'ngCookies', 'ngSanitize'])
Here is an example of my config block, make sure you inject both routeProvider and locationProvider:
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider){
$routeProvider
.when('/', {
templateUrl: '../partials/member.php',
controller: 'Member',
access: 'member'
})
.when('/leaderboard', {
templateUrl: '../partials/leaderboard.html',
controller: 'Leaderboard',
access: 'member'
})
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}])
If your doing all of this and it still doesn't work post a plunker of your code. Thanks.

AngularJs reload page instead only load view

I have a simple app in AgularJS. I need to load view in route
$routeProvider.when('/articles/:url', {
templateUrl: 'partials/article.html',
controller: ArticleCtrl
});
But if i click on
Page reloads and after reload Angular try to load partials/article.html view, but fails on error "NetworkError: 404 Not Found - http://localhost:3000/clanky/partials/article.html"
I know that I can use "../partials/article.html" insted "partials/article.html", but I mean that isn't the core of problem.
There are my routes:
blog.config([
'$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/', {
templateUrl: 'partials/main.html',
controller: MainCtrl
});
$routeProvider.when('/login', {
templateUrl: 'partials/login.html',
controller: LoginCtrl
});
$routeProvider.when('/backend', {
templateUrl: 'partials/backend.html',
controller: BackendCtrl
});
$routeProvider.when('/articles/:url', {
templateUrl: 'partials/article.html',
controller: ArticleCtrl
});
return $routeProvider.otherwise({
redirectTo: '/'
});
}
]);
P.S.
If I try go to the any other route, for example, login, it partialy works, but reload is still here.
Thanks for yours answers
seems that I've got the same problem as you.
I don't know how to handle this but I think that's because the page was reloaded.
When you are not setting the html5Mode to true, then will be # at the url, and if you reload the page, the # will track the information to the $scope to work with the htmlHistory api so that the app can work properly even with the page refresh.
But once you set the html5Mode to true, then there's no # to store any $scope information, and when the page reload, angular can not found the accordingly scope so it return 404.
You can check $location section on angularjs guide, it has a section explaining why this happen.
Page reload navigation
The $location service allows you to change only the URL; it does not allow you to reload the page. When you need to change the URL and reload the page or navigate to a different page, please use a lower level API, $window.location.href.
Hope this can be helpful for you, and if you got any better answer, please let me know, I'm also waiting for the right way to solve it.
Start your templateUrl with a slash:
templateUrl: '/partials/article.html'
instead of
templateUrl: 'partials/article.html'

Categories