diff options
| -rw-r--r-- | src/services.js | 2 | ||||
| -rw-r--r-- | src/widgets.js | 78 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 59 |
3 files changed, 138 insertions, 1 deletions
diff --git a/src/services.js b/src/services.js index 3543b2ea..7742b174 100644 --- a/src/services.js +++ b/src/services.js @@ -616,7 +616,7 @@ function switchRouteMatcher(on, when, dstName) { * Watches $location.hashPath and tries to map the hash to an existing route * definition. It is used for deep-linking URLs to controllers and views (HTML partials). * - * $route is typically used in conjunction with ng:include widget. + * $route is typically used in conjunction with {@link angular.widget.ng:view ng:view} widget. * * @example <p> diff --git a/src/widgets.js b/src/widgets.js index 3b0ffab7..4585629a 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -944,3 +944,81 @@ angularWidget("@ng:repeat", function(expression, element){ }); */ angularWidget("@ng:non-bindable", noop); + + +/** + * @ngdoc widget + * @name angular.widget.ng:view + * + * @description + * # Overview + * `ng:view` is a widget that complements the {@link angular.service.$route $route} service by + * including the rendered template of the current route into the main layout (`index.html`) file. + * Every time the current route changes, the included view changes with it according to the + * configuration of the `$route` service. + * + * This widget provides functionality similar to {@link angular.service.ng:include ng:include} when + * used like this: + * + * <ng:include src="$route.current.template" scope="$route.current.scope"></ng:include> + * + * + * # Advantages + * Compared to `ng:include`, `ng:view` offers these advantages: + * + * - shorter syntax + * - more efficient execution + * - doesn't require `$route` service to be available on the root scope + * + * + * # Example + * Because of the nature of this widget, we can't include the usual live example for it. Instead + * following is a code snippet showing the typical usage: + * + <pre> + <script> + angular.service('routeConfig', function($route) { + $route.when('/foo', {controller: MyCtrl, template: 'foo.html'}); + $route.when('/bar', {controller: MyCtrl, template: 'bar.html'}); + }, {$inject: ['$route'], $eager: true}); + + function MyCtrl() {}; + </script> + <div> + <a href="#/foo">foo</a> | <a href="#/bar">bar</a> | <a href="#/undefined">undefined</a><br/> + The view is included below: + <hr/> + <ng:view></ng:view> + </div> + </pre> + */ +angularWidget('ng:view', function(element) { + var compiler = this; + + if (!element[0]['ng:compiled']) { + element[0]['ng:compiled'] = true; + return injectService(['$xhr.cache', '$route'], function($xhr, $route, element){ + $route.onChange(function(){ + var src, scope; + + if ($route.current) { + src = $route.current.template; + scope = $route.current.scope; + } + + if (src) { + $xhr('GET', src, function(code, response){ + element.html(response); + compiler.compile(element)(element, scope); + scope.$init(); + }); + } else { + element.html(''); + } + }); + }); + } else { + this.descend(true); + this.directives(true); + } +}); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index 48b9f8a1..27c5cb82 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -785,5 +785,64 @@ describe("widget", function(){ expect(element.text()).toEqual(''); }); }); + + + describe('ng:view', function() { + var rootScope, partialScope, $route, $location, $browser; + + beforeEach(function() { + rootScope = angular.scope(); + partialScope = angular.compile('<ng:view></ng:view>', rootScope); + partialScope.$init(); + $route = rootScope.$service('$route'); + $location = rootScope.$service('$location'); + $browser = rootScope.$service('$browser'); + }); + + afterEach(function() { + dealoc(partialScope); + }); + + + it('should do nothing when not routes are defined', function() { + $location.updateHash('/unknown'); + rootScope.$eval(); + expect(partialScope.$element.text()).toEqual(''); + }); + + + it('should load content via xhr when route changes', function() { + $route.when('/foo', {controller: angular.noop, template: 'myUrl1'}); + $route.when('/bar', {controller: angular.noop, template: 'myUrl2'}); + + expect(partialScope.$element.text()).toEqual(''); + + $location.updateHash('/foo'); + $browser.xhr.expectGET('myUrl1').respond('<div>{{1+3}}</div>'); + rootScope.$eval(); + $browser.xhr.flush(); + expect(partialScope.$element.text()).toEqual('4'); + + $location.updateHash('/bar'); + $browser.xhr.expectGET('myUrl2').respond('angular is da best'); + rootScope.$eval(); + $browser.xhr.flush(); + expect(partialScope.$element.text()).toEqual('angular is da best'); + }); + + it('should remove all content when location changes to an unknown route', function() { + $route.when('/foo', {controller: angular.noop, template: 'myUrl1'}); + + $location.updateHash('/foo'); + $browser.xhr.expectGET('myUrl1').respond('<div>{{1+3}}</div>'); + rootScope.$eval(); + $browser.xhr.flush(); + expect(partialScope.$element.text()).toEqual('4'); + + $location.updateHash('/unknown'); + rootScope.$eval(); + expect(partialScope.$element.text()).toEqual(''); + }); + }); }); |
