Skip to content

Commit 908866b

Browse files
committed
Merge branch 'master' of github.com:angular-ui/ui-router
2 parents ed4c9a6 + 258e4b5 commit 908866b

File tree

6 files changed

+143
-20
lines changed

6 files changed

+143
-20
lines changed

README.md

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ To evolve the concept of an [angularjs](http://angularjs.org/) [***route***](htt
3939
## Resources
4040

4141
* [In-Depth Overview](https://github.com/angular-ui/ui-router/wiki)
42+
* [API Quick Reference](https://github.com/angular-ui/ui-router/wiki/Quick-Reference)
4243
* [FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions)
4344
* [Sample App](http://angular-ui.github.com/ui-router/sample/) ([Source](https://github.com/angular-ui/ui-router/tree/master/sample))
4445
* [Generated Docs](http://angular-ui.github.com/ui-router/build/doc/)
4546

4647
## Quick Start
48+
49+
### Setup
50+
4751
1. Get ui-router:
4852
>* with bower: `bower install angular-ui-router`
4953
>* fork this repo
@@ -65,9 +69,109 @@ To evolve the concept of an [angularjs](http://angularjs.org/) [***route***](htt
6569
var myapp = angular.module('myapp', ['ui.state'])
6670
```
6771

68-
3. Add one or more `ui-view` to your app, give them names.
72+
### Nested States & Views
73+
74+
The great majority of ui-router's power is its ability to nest states & views.
75+
76+
1. Follow [Setup](https://github.com/angular-ui/ui-router#setup) instructions above.
77+
78+
2. Add a `ui-view` to your app.
79+
>
80+
```html
81+
<!-- index.html -->
82+
<body>
83+
<div ui-view></div>
84+
<!-- Also a way to navigate -->
85+
<a href="#/route1">Route 1</a>
86+
<a href="#/route2">Route 2</a>
87+
</body>
88+
```
89+
90+
3. Add some templates. These will plug into the `ui-view` within index.html. Notice that they have their own `ui-view` as well! That is the key to nesting states and views.
91+
>
92+
```html
93+
<!-- route1.html -->
94+
<h1>Route 1</h1>
95+
<hr/>
96+
<a href="#/route1/list">Show List</a>
97+
<div ui-view></div>
98+
```
99+
```html
100+
<!-- route2.html -->
101+
<h1>Route 2</h1>
102+
<hr/>
103+
<a href="#/route2/list">Show List</a>
104+
<div ui-view></div>
105+
```
106+
107+
4. Add some child templates. *These* will get plugged into the `ui-view` of their parent state templates.
108+
```html
109+
<!-- route1.list.html -->
110+
<h3>List of Route 1 Items</h3>
111+
<ul>
112+
<li ng-repeat="item in items">{{item}}</li>
113+
</ul>
114+
```
115+
```html
116+
<!-- route2.list.html -->
117+
<h3>List of Route 2 Things</h3>
118+
<ul>
119+
<li ng-repeat="thing in things">{{thing}}</li>
120+
</ul>
121+
```
122+
123+
5. Now let's wire it all up. Set up your states in the module config:
124+
>
125+
```javascript
126+
myapp.config(function($stateProvider, $urlRouterProvider){
127+
//
128+
// For any unmatched url, send to /route1
129+
$urlRouterProvider.otherwise("/route1")
130+
//
131+
// Now set up the states
132+
$stateProvider
133+
.state('route1', {
134+
url: "/route1",
135+
templateUrl: "route1.html"
136+
})
137+
.state('route1.list', {
138+
url: "/list",
139+
templateUrl: "route1.list.html",
140+
controller: function($scope){
141+
$scope.items = ["A", "List", "Of", "Items"];
142+
}
143+
})
144+
.state('route2', {
145+
url: "/route2",
146+
templateUrl: "route2.html"
147+
})
148+
.state('route2.list', {
149+
url: "/list",
150+
templateUrl: "route2.list.html",
151+
controller: function($scope){
152+
$scope.things = ["A", "Set", "Of", "Things"];
153+
}
154+
})
155+
})
156+
```
157+
158+
4. See this quick start example in action.
159+
>**[Go to Quick Start Plunker for Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview)**
160+
161+
5. This only scratches the surface! You've only seen Nested Views.
162+
>**[Dive Deeper!](https://github.com/angular-ui/ui-router/wiki)**
163+
164+
165+
### Multiple & Named Views
166+
167+
Another handy feature is the ability to have more than one view per template. Please note: 95% of the time Nested States & Views is the pattern you'll be looking for, opposed to using multiple views per template.
168+
169+
1. Follow [Setup](https://github.com/angular-ui/ui-router#setup) instructions above.
170+
171+
2. Add one or more `ui-view` to your app, give them names.
69172
>
70173
```html
174+
<!-- index.html -->
71175
<body>
72176
<div ui-view="viewA"></div>
73177
<div ui-view="viewB"></div>
@@ -77,7 +181,7 @@ var myapp = angular.module('myapp', ['ui.state'])
77181
</body>
78182
```
79183

80-
4. Set up your states in the module config
184+
3. Set up your states in the module config:
81185
>
82186
```javascript
83187
myapp.config(function($stateProvider, $routeProvider){
@@ -118,10 +222,10 @@ myapp.config(function($stateProvider, $routeProvider){
118222
})
119223
```
120224

121-
5. See this quick start example in action.
122-
>**[Go to Quick Start Plunker](http://plnkr.co/edit/vDURUN?p=preview)**
225+
4. See this quick start example in action.
226+
>**[Go to Quick Start Plunker for Multiple & Named Views](http://plnkr.co/edit/vDURUN?p=preview)**
123227
124-
6. This only scratches the surface! You've only seen Named Views and Parallel Views. Learn more about `state()` options, Nested Views, URL routing options, backwards compatibility, and more!
228+
5. This only scratches the surface! You've only seen Named Views and Parallel Views.
125229
>**[Dive Deeper!](https://github.com/angular-ui/ui-router/wiki)**
126230
127231
## Developing

src/state.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
269269
$state.href = function (stateOrName, params, options) {
270270
options = extend({ lossy: true }, options || {});
271271
var state = findState(stateOrName);
272-
var nav = options.lossy ? state.navigable : state;
273-
274-
if (!nav || !nav.url) throw new Error("State '" + state + "' " + (
275-
options.lossy ? "does not have a URL or navigable parent" : "is not navigable"
276-
));
277-
return nav.url.format(normalize(state.params, params || {}));
272+
var nav = (state && options.lossy) ? state.navigable : state;
273+
return (nav && nav.url) ? nav.url.format(normalize(state.params, params || {})) : null;
278274
};
279275

280276
function resolveState(state, params, paramsAreFiltered, inherited, dst) {

src/stateDirectives.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ function $StateRefDirective($state) {
1818
if (newVal) params = newVal;
1919
if (!nav) return;
2020

21-
try {
22-
element[0][attr] = $state.href(ref.state, params, { lossy: true });
23-
} catch (e) {
21+
var newHref = $state.href(ref.state, params, { lossy: true });
22+
23+
if (!newHref) {
2424
nav = false;
25+
return false;
2526
}
27+
element[0][attr] = newHref;
2628
};
2729

2830
if (ref.paramExpr) {
@@ -37,6 +39,7 @@ function $StateRefDirective($state) {
3739

3840
element.bind("click", function(e) {
3941
$state.transitionTo(ref.state, params);
42+
scope.$apply();
4043
e.preventDefault();
4144
});
4245
}

src/urlMatcherFactory.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
161161
nPath = this.segments.length-1,
162162
values = {}, i;
163163

164+
if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
165+
164166
for (i=0; i<nPath; i++) values[params[i]] = decodeURIComponent(m[i+1]);
165167
for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
166168

test/stateSpec.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,8 @@ describe('state', function () {
256256

257257
describe('.href()', function () {
258258
it('aborts on un-navigable states', inject(function ($state) {
259-
expect(function() { $state.href("A"); }).toThrow(
260-
"State 'A' does not have a URL or navigable parent"
261-
);
262-
expect(function() { $state.href("about.sidebar", null, { lossy: false }); }).toThrow(
263-
"State 'about.sidebar' is not navigable"
264-
);
259+
expect($state.href("A")).toBeNull();
260+
expect($state.href("about.sidebar", null, { lossy: false })).toBeNull();
265261
}));
266262

267263
it('generates a parent state URL when lossy is true', inject(function ($state) {

test/urlMatcherFactorySpec.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ describe("UrlMatcher", function () {
4040
.toBeNull();
4141
});
4242

43+
it('.exec() throws on unbalanced capture list', function () {
44+
var shouldThrow = {
45+
"/url/{matchedParam:([a-z]+)}/child/{childParam}": '/url/someword/child/childParam',
46+
"/url/{matchedParam:([a-z]+)}/child/{childParam}?foo": '/url/someword/child/childParam'
47+
};
48+
49+
angular.forEach(shouldThrow, function(url, route) {
50+
expect(function() { new UrlMatcher(route).exec(url, {}); }).toThrow(
51+
"Unbalanced capture group in route '" + route + "'"
52+
);
53+
});
54+
55+
var shouldPass = {
56+
"/url/{matchedParam:[a-z]+}/child/{childParam}": '/url/someword/child/childParam',
57+
"/url/{matchedParam:[a-z]+}/child/{childParam}?foo": '/url/someword/child/childParam'
58+
};
59+
60+
angular.forEach(shouldPass, function(url, route) {
61+
expect(function() { new UrlMatcher(route).exec(url, {}); }).not.toThrow();
62+
});
63+
});
64+
4365
it(".format() reconstitutes the URL", function () {
4466
expect(
4567
new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?from')

0 commit comments

Comments
 (0)