Angular UI Router Wrong Template And Controller - angularjs

I have an odd issue where I use an ng-click to $state.go() to a state, and get the correct url but the wrong template and controller.
These are my states:
app.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/messages");
$stateProvider.state('message', {
url: "/messages/:to/:fro/:messageID",
templateUrl: 'message.html',
controller: 'MessageCtrl'
}).state('compose', {
url: "/messages/compose/a/message",
templateUrl: 'compose.html',
controller: 'ComposeCtrl'
});
});
This is the function triggered by my ng-click and I am in fact getting the console log so I know the right one is firing.
$scope.composeMe = function(){
console.log("You want to compose a message!");
$state.go("compose");
};
When I fire that function I get the url to change to http://example.com/messages/compose/a/message but I get the templateUrl and the controller of the message state.
Why is that? The other state is fired from a similar (ng-click derived) function and works perfectly.

The URL you are trying to go to matches the one you are actually going to.
How would ui-router know that "compose" isn't the to: field you want, "a" isn't the fro: field you want, and "message" isn't the messageID you want?
Move the more specific state definition above the one with URL parameters.

Related

UI Router query parameters not working correctly

I set UI Router up to have query parameters in the url, but when I did that it broke the search functionality on my app.
My states are like this:
.state('home', {
url: '/home',
templateUrl: 'home/home.html',
controller: 'HomeCtrl'})
.state('search', {
url: '/search?q',
templateUrl: 'search/search.html',
controller: 'SearchCtrl'
});
I have this code in both HomeCtrl and SearchCtrl:
$scope.searchTerms = $stateParams.searchTerms;
The HomeCtrl basically serves as an autocomplete/query page.
Then I have this:
$state.go('search', {q: $scope.searchTerms});
in my getSuggestions function on HomeCtrl and search function in SearchCtrl
In order for this to work, I have to search twice...? I enter the query and hit enter and the query appears in the url, then I have to hit enter again for the results to display. It doesn't work at all if I click the search button :(
If I comment out the $state.go line, search works but I loose the query parameters in the url and vice versa. It seems like $scope is getting turned off somehow?
Should I use ui-sref (it is more common) instead of $state.go for the linking? Am I suppose to put the $state.go code on the button element or something?
What am I doing wrong... so that search works and I have query parameters in the url?

$location.path or $location.url doesn't trigger the ngRoute controller

I have a route defines as follows:
$routeProvider.
when('/projects/', {
controller: 'ProjectCtrl',
controllerAs: 'project_ctrl',
templateUrl: '/static/app/partials/project.html'
}).
After the login finishes I need the user to land on this link, hence in my controller I am using this:
vm.login = function(form) {
if (form.$valid) {
loginService.login(vm.loginFormData.username, vm.loginFormData.password);
loginService.setUpUser()
$location.url("/projects");
}
}
But unfortunately the controller associated with this view is not triggered, that is ProjectCtrl is not triggered. However when I click on the navigation link which uses in the dom, it works fine. Can someone please guide me here, may I am missing something conceptual.
Hence the larger question is how do I redirect a user in the controller using some APIs which also complies with ngRoute based controllers.
Try removing the last / in url so it matches $location.url("/projects");
$routeProvider.
when('/projects', {

Hash change not triggering ui-router change?

I have a link on my page (inside the scope of angular app 1) which changes the hash location
/app/#/location
I have another angular app (2) which is not reacting to the hash location change in the way I expect it to (ie by firing locationChangeStart, and changing state). I don't fully understand why. Anybody can explain this to me?
Edit 1: yes, there are two angular apps on the page, of different angular versions, both bootstrapped (sigh, don't ask). The ui-router configuration looks like this:
$urlRouterProvider.otherwise(($injector) => {
let $state = $injector.get("$state");
[... snip ...]
$state.go('list');
});
// Now set up the states
$stateProvider
.state('messaging', {
url: "/messaging/{param}",
templateUrl: "messaging.html",
controller: 'MessagingController'
})
.state('list', {
url: "/list",
templateUrl: "list.html",
controller: 'ListController'
});
Nothing really too fancy here, and before anybody asks, yes, I do need to check on a state in the otherwise.
The bootstrapping looks like this:
angular.element(document).ready(function() {
angular.element(document.getElementById('app-bootstrap')).prepend('<div ui-view></div>');
angular.bootstrap(document.getElementById('app-bootstrap'), ['app']);
});
Edit 2: this seems like it may be not an angular-related issue at all, as using the window 'hashchange' event directly doesn't seem to fire either; I've confirmed that window.onhashchange is the correct function.
(angular 1.4.7)

Angular UI Router, Sub views get unset after a change of state

I debated a while on this but I got a Plunk that reproduce it.
I have a state "Contact" that get loaded by default. with $state.transitionTo
Inside that state I have some views, they all get loaded and everything work.
If I click to change the state to "Home" by default or by "ui-sref" and in the "Home" state/template I have ui-sref="contacts". When we click back to set the state to contacts it should work, but all the sub views are now not being called properly.
It seems that when ui-sref call the state this one behave differently that when it is loaded by default.
Why $state.transitionTo(''); seems to work differently than ui-sref.
<script>
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){
// For any unmatched url, send to /
$urlRouterProvider.otherwise("/")
$stateProvider
.state('home', {
templateUrl: 'home.html',
controller: function($scope){
}
})
.state('contacts', {
templateUrl: 'contacts.html',
controller: function($scope){
}
})
.state('contacts.list', {
views:{
"":{
template: '<h1>Contact.List Working wi no Data defined.</h1>'
},
"stateSubView":{
template: '<h2>StateSubView Working</h2>'
},
"absolute#":{
template: '<h2>Absolute item</h2>'
}
}
});
});
myapp.controller('MainCtrl', function ($state) {
$state.transitionTo('contacts.list');
})
Q2:
Why is the Absolute tag that is under contact work when I add the view in the Index, but is not working when it is inside the contact.html file. Absolute reference work only with the Index and not if called everywhere?
"absolute#":{
template: '<h2>Absolute item</h2>'
}
I saw that in index.html you have an empty ui-view tag. What do you expect to go there? I think you can not do this. The router just doesn't know with which state (home or contacts) it should replace. Apparently it picks the second one (contacts). I'd suggest to put url: '/' in the home state and you'll see the difference.
This is for sure one issue.
Other than that:
You can't simply access views from contacts.list in contacts afaik.
The empty ui-view work as a wild card and can be use to switch across multiple route even if we have nested element. But if we have a nested view contact.list it can only be access if we put the whole path in ui-sref="contacts.list" because the list child of contact cannot be access only by using ui-sref="contacts"

Am I using states correctly?

I have an angular app with a homepage that shows a list of things. Each thing has a type. In the nav, there are selectors corresponding to each thing type. Clicking one of these selectors causes the home controller to filter the things shown to those of the selected type. As such, I see the selectors as corresponding to states of the home page.
Now, I'd like to map each of these states to a url route: myapp.com/home loads the home page in default (unfilitered) state, myapp.com/home/foo opens the home page with the foo-type selector activated, and switching from there to myapp.com/home/bar switches to the bar-filtered state without reloading the page.
It's that last bit - triggering "state" changes without reloading the page, that's been particularly tricky to figure out. There are numerous SO/forum questions on this topic but none have quite hit the spot, so I'm wondering if I'm thinking about this in the wrong way: Should I be thinking of these "states" as states at all? Is there a simpler approach?
Also, I'm open to using either ngRoute or ui.router - is there anything about one or the other that might make it simpler to implement this?
Using ui-router, you can approach it like this:
$stateProvider
.state('home', {
url: "/home",
controller: "HomeController",
templateUrl: "home.html"
// .. other options if required
})
.state('home.filtered', {
url: "/{filter}",
controller: "HomeController",
templateUrl: "home.html"
// .. other options if required
})
This creates a filtered state as a child of the home state and means that you can think of the URL to the filtered state as /home/{filter}. Where filter is a state parameter that can then be accessed using $stateParams.
Since you don't want to switch views, you inject $stateParams into your controller, watch $stateParams.filter, and react to it how you wish.
$scope.$watch(function () { return $stateParams.filter }, function (newVal, oldVal) {
// handle it
});

Resources