AngularJs ui-route nested state “could not resolve state” error - angularjs

Here is my route config
$stateProvider.state('layout', {
abstract: true,
controller: "MenuCtrl",
templateUrl: "views/layout/MainLayout.html"
}).
state('layout.home', {
url: '/',
templateUrl: 'views/Main.html',
controller: 'MainCtrl'
}).state('layout.tag.add', {
url: '/addTag',
templateUrl: 'views/AddTag.html',
controller: 'AddTagCtrl'
})
Later I have in my code function:
var goToAddTagPage = function(){
$state.go('layout.tag.add');
};
When I call this function I get Could not resolve 'layout.tag.add' from state 'layout'. If I rename this to layout.addTag it works correctly. Nested tag causes issue. How I can correctly nest states like that?
EDIT: I have added empty state
state('layout.tag',{
}).
Now exception is gone. However the view is now not rendered. I get empty screen. I try to add abstract : true for state but it didn't helped.This state need some configuration?

In your state hierarchy, there really must be all three states.
state('layout', {...}
state('layout.tag', {...}
state('layout.tag.add', {...}
Because a '.' (dot) in the state name simply represents hierarchy (parents, grand parents)
But once we add new parent between grand-parent and child, we need to be sure, that
parent contains a view "target" for a child.
child explicitly uses absolute view naming to target grand-parent
So, this would work (and I would prefer that, because we gain inheritance parent-child)
state('layout.tag', {
template: '<div ui-view ></div>'
...
}
So now, there is the element <div ui-view ></div> injected into grand-parent, and also serves as an anchor/target for child.
Check the doc:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
The second approach is to use absolute view naming and skip grand parent
.state('layout.tag.add', {
url: '/addTag',
views: {
'#layout': { // target unnamed view anchor in grand-parent
templateUrl: 'views/AddTag.html',
controller: 'AddTagCtrl'
}
}
})
View Names - Relative vs. Absolute Names
Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.

You may(dont quote me on this, but try) need to introduce an intermediate layout.tag state if you want to use this hierarchy.
UI router could be failing on 'dot-notation' based nesting because youre skipping a state essentially.
Update: Based on the regex in this snippet from the source
var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
It does look for a parent state named 'layout.tag'.
So you will either need to accept the 'layout.addTag' hierarchy or introduce an intermediate 'layout.tag' state

Related

ui-router require in multiple sibling views

How it is
I'm working on an application which has a little but more complex form than usual. I've splitted it up by having different views and controllers, but still keeping them inside a form element.
It has different sections but the payload of the request should contains all the information when I do the POST request. So basically that's why it has one main form (well it's not about having it inside a form or not, I could even have it without a form and then collecting models and submitting it by including all model's data into one main).
Question
My question is:
Does ui-router has a kind of required field to set? Just in order to have the required controller's functions and scope.
Does anyone knows a better solution?
Code
The code is just an example, this is not literally the code I have.
View (sectionMain.html)
<form>
<div ui-view="section1"></div>
<div ui-view="section2"></div>
<div ui-view="section3"></div>
<button type="submit">Submit</button>
</form>
State definition
$stateProvider.state('myState', {
url: '/mstate',
parent: 'stateForAuthenticatedUsers',
views: {
//this will be loaded what I have above. Just a part of the page.
'content': {
templateUrl: 'modules/myModule/views/sectionMain.html',
controller: 'Section1Ctrl as vm'
},
'section1#myState': {
templateUrl: 'modules/myModule/views/section1.html',
controller: 'Section1Ctrl as vmState1'
},
'section2#myState': {
templateUrl: 'modules/myModule/views/section2.html',
controller: 'Section2Ctrl as vmState2'
},
'section3#myState': {
templateUrl: 'modules/myModule/views/section3.html',
controller: 'Section3Ctrl as vmState3'
},
}
});
Controller (for example State2Ctrl)
function State2Ctrl($scope) {
//stuffs here. Need to refer parent's scope somehow
$scope.vm.someVariableOrObject
}
My concern
My only concern is that trusting a value vm which has been defined in the state's configuration doesn't give me the feeling that it's a well composed structure. I'd really need something I can require to, to have it explicitly instead of blindly trusting in a value vm.
Also my views contains the vm.someVariable in order to refer the parent's scope. But I don't like it.
Why not simply use your $rootScope? An easy way to achieve what you are looking for, without passing in a $scope every time.

Angular-ui-router child state with multiple parent states

Using Angular-ui-router, is there a possibility to define a child-state that has multiple parent states:
$stateProvider
.state("parent1", {
url: '/',
templateUrl: 'parent1.html'
})
.state('parent2', {
url: '/parent2',
templateUrl: 'parent2.html'
})
//Make accessible from every state parent-state
.state("child", {
url: 'child',
//TODO parents: ['parent1', 'parent2']
onEnter: function() {
//do something;
}
})
Example:
Your Angular app has a directive that is used multiple times in different states in your app. The directive itself includes a link which redirects to a different state. Depending on where the directive is used, it should append its child-state to the current active parent-state.
It just doesn't seem right to define states for each permutation like state1.child, state2.child etc.. There needs to be a better way.
This kind of hierarchy would go against the DOM tree structure, which by definition doesn't allow multiple parents of same element.
Moreover, it is error (and headache) prone and could easily result in the multiple inheritance diamond problem, as child state do inherit from parent state in some cases.
It sounds like a directive, and not a state, would be the better solution for what you're looking for.
EDIT:
Just saw that there's a closed issue on this, which is closed because he reached the same conclusion (that a directive is the better way)

ui-route substates without nested views

As you can notice in the following code, I would like to have a view for the parent "colors" state (which will show a table with list of colors), and then each color should have it's own view, not inherited from "colors".
So the hierarchy should only apply to URL's, not to views.
Any idea ?
.state('colors', {
url: "/colors",
templateUrl: 'views/colors/colors.html'
})
.state('colors.yellow', {
url: "/yellow",
templateUrl: 'views/colors/yellow.html'
})
I understand that you've found your answer. But let me append other approach and extend your solution with some more dynamic stuff. It'll a bit overcome your question, but could help you to see the magic around UI-Router
I created an example here
Firstly, we can have this kind of parent template (colors.html)
<div ui-view="">
// the content of the parent template, e.g. list of colors
</div>
So, because the ui-view="" is defined on the parent root element, child will in fact replace it. And what's more, we would gain $scope inheritance:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
And now even more. Let's imagine that we would have more colors, not only yellow. That could lead to change in the approach, and color could become parameter:
.state('colors', {
url: "/colors",
...
})
.state('colors.color', {
url: "/:color",
...
That's a big change, because now we can have url like /colors/yellow or /colors/red and all will be managed by stat colors.color
Let's continue, using the solution from here: Trying to Dynamically set a templateUrl in controller based on constant - and we can even have many templates, different by each color name.
We can then define them as constants inside of the angular module:
.value('myTemplates', {
"yellow" : "views/colors/yellow.html",
"red" : "views/colors/red.html",
"default" : "views/colors/default.html",
}
)
And our child state could use them in the run-time, based on the parameter. This would be the call:
<a ui-sref="colors.color({color: 'yellow'})">
<a ui-sref="colors.color({color: 'red'})">
<a ui-sref="colors.color({color: 'white'})">
And this will be the adjusted child state:
.state('colors.color', {
url: "/:color",
templateProvider: ['$templateRequest', '$stateParams', 'myTemplates',
function($templateRequest, $stateParams, myTemplates) {
var templateName = myTemplates[$stateParams.color]
|| myTemplates["default"];
return $templateRequest(templateName);
}],
})
Check that all in action here

Angular UI-Router use template based on passed value

I have a state in my Angular 1.3 app:
.state('department.filetransfer', {
url: '/filetransfer/:deptID',
templateUrl: function($stateParams, $log) {
return 'modules/department/templates/department.filetransfer.'+$stateParams.deptID+'.html';
},
controller: 'DeptFiletransferCtrl as ftc',
data: {
pageTitle: 'filetransfer',
access: 'public'
}
});
And then in my application I have a link such as
ui-sref="department.filetransfer.accounting"
But I get a console error
Error: Could not resolve 'department.filetransfer.accounting' from state 'department'
What did I miss?
There is a working example
The way how to pass param is via an object {paramName1:value1, paramName2: value2}:
ui-sref="department.filetransfer({deptID:accounting})"
The above example is suitable when accounting as a variable in $scope.
In case, that we need to pass string value, we should do it like this:
ui-sref="department.filetransfer({deptID:'accounting'})"
As documented here
ui-sref
A directive that binds a link (<a> tag) to a state. If the state has an associated URL, the directive will automatically generate & update the href attribute via the $state.href() method. Clicking the link will trigger a state transition with optional parameters.
Also middle-clicking, right-clicking, and ctrl-clicking on the link will be handled natively by the browser.
You can also use relative state paths within ui-sref, just like the relative paths passed to $state.go(). You just need to be aware that the path is relative to the state that the link lives in, in other words the state that loaded the template containing the link.
You can specify options to pass to $state.go() using the ui-sref-opts attribute. Options are restricted to location, inherit, and reload.

ui-router nested views access to multiple controller

with ui-router, any child and grandchild of a state has access to their parent and grandparents controller correct?
So for example a .state(resources.resource.rates) the .rate state controller has access to the $scope.objects in resource and resources (which all have their own controllers) right?
On the presumption I have a html set up where everything is a nested view within resources with a ui-view="content2". However I have another page within resource called rates I would like to open up in the same nested view as resources but also gets access to the resource controller as well.
.state('resources.resource.rates', {
url: '/rates',
views:{
"content2":{
templateUrl: 'templates/rates.html',
controller: 'CreateRatesCtrl'
}
}
})
my ng-href in my view links to the /resources/{{resource.Id}}/rates but doesn't open up the rates page in the resources ui-view.
so what I tried was setting the view to content2#resources like so
views:{
"content2#resources":{
templateUrl: 'templates/rates.html',
controller: 'CreateRatesCtrl'
}
}
This works in the sense that the html now populates the rates.html in the resources ui-view and with the ng-href as resources/{{resource.Id}}/rates but unfortunately I only have access to the resources scope objects. I have a resourceId that I need from the resource controller. Does setting content2#resources limit my scope access to only the resources controller in this case? If so how can I make it so I can get access to the resource controller as well?
Quoted from ui-router wiki :
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
This should be enough to understand what nesting of states and/or views gives and doesn't give you.

Resources