@ember/routing: correct types for transitionTo (#42544)

- add types to support query-params-only transitions in both the `Route`
  and `RouterService` classes
- supply updated docs for each API as well
This commit is contained in:
Chris Krycho
2020-02-21 12:30:36 -07:00
committed by GitHub
parent b481f65c6d
commit 2dc6d2699d
6 changed files with 272 additions and 23 deletions

View File

@@ -1,6 +1,9 @@
// Type definitions for non-npm package @ember/routing 3.0
// Project: https://emberjs.com/api/ember/3.4/modules/@ember%2Frouting
// Definitions by: Mike North <https://github.com/mike-north>
// Chris Krycho <https://github.com/chriskrycho>
// Dan Freeman <https://github.com/dfreeman>
// James C. Davis <https://github.com/jamescdavis>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8

View File

@@ -1,8 +1,8 @@
import EmberObject from "@ember/object";
import ActionHandler from "@ember/object/-private/action-handler";
import Transition from "@ember/routing/-private/transition";
import Evented from "@ember/object/evented";
import { RenderOptions, RouteQueryParam } from "@ember/routing/types";
import EmberObject from '@ember/object';
import ActionHandler from '@ember/object/-private/action-handler';
import Transition from '@ember/routing/-private/transition';
import Evented from '@ember/object/evented';
import { RenderOptions, RouteQueryParam } from '@ember/routing/types';
import Controller, { Registry as ControllerRegistry } from '@ember/controller';
/**
@@ -189,19 +189,196 @@ export default class Route extends EmberObject.extend(ActionHandler, Evented) {
/**
* Transition the application into another route. The route may
* be either a single route or route path
* be either a single route or route path:
*
* ```javascript
* this.transitionTo('blogPosts');
* this.transitionTo('blogPosts.recentEntries');
* ```
*
* Optionally supply a model for the route in question. The model
* will be serialized into the URL using the `serialize` hook of
* the route:
*
* ```javascript
* this.transitionTo('blogPost', aPost);
* ```
*
* If a literal is passed (such as a number or a string), it will
* be treated as an identifier instead. In this case, the `model`
* hook of the route will be triggered:
*
* ```javascript
* this.transitionTo('blogPost', 1);
* ```
*
* Multiple models will be applied last to first recursively up the
* route tree.
*
* ```app/routes.js
* // ...
*
* Router.map(function() {
* this.route('blogPost', { path:':blogPostId' }, function() {
* this.route('blogComment', { path: ':blogCommentId' });
* });
* });
*
* export default Router;
* ```
*
* ```javascript
* this.transitionTo('blogComment', aPost, aComment);
* this.transitionTo('blogComment', 1, 13);
* ```
*
* It is also possible to pass a URL (a string that starts with a
* `/`).
*
* ```javascript
* this.transitionTo('/');
* this.transitionTo('/blog/post/1/comment/13');
* this.transitionTo('/blog/posts?sort=title');
* ```
*
* An options hash with a `queryParams` property may be provided as
* the final argument to add query parameters to the destination URL.
*
* ```javascript
* this.transitionTo('blogPost', 1, {
* queryParams: { showComments: 'true' }
* });
*
* // if you just want to transition the query parameters without changing the route
* this.transitionTo({ queryParams: { sort: 'date' } });
* ```
*
* See also [replaceWith](#method_replaceWith).
*
* Simple Transition Example
*
* ```app/routes.js
* // ...
*
* Router.map(function() {
* this.route('index');
* this.route('secret');
* this.route('fourOhFour', { path: '*:' });
* });
*
* export default Router;
* ```
*
* ```app/routes/index.js
* import Route from '@ember/routing/route';
* import { action } from '@ember/object';
*
* export default class IndexRoute extends Route {
* @action
* moveToSecret(context) {
* if (authorized()) {
* this.transitionTo('secret', context);
* } else {
* this.transitionTo('fourOhFour');
* }
* }
* }
* ```
*
* Transition to a nested route
*
* ```app/router.js
* // ...
*
* Router.map(function() {
* this.route('articles', { path: '/articles' }, function() {
* this.route('new');
* });
* });
*
* export default Router;
* ```
*
* ```app/routes/index.js
* import Route from '@ember/routing/route';
* import { action } from '@ember/object';
*
* export default class IndexRoute extends Route {
* @action
* transitionToNewArticle() {
* this.transitionTo('articles.new');
* }
* }
* ```
*
* Multiple Models Example
*
* ```app/router.js
* // ...
*
* Router.map(function() {
* this.route('index');
*
* this.route('breakfast', { path: ':breakfastId' }, function() {
* this.route('cereal', { path: ':cerealId' });
* });
* });
*
* export default Router;
* ```
*
* ```app/routes/index.js
* import Route from '@ember/routing/route';
* import { action } from '@ember/object';
*
* export default class IndexRoute extends Route {
* @action
* moveToChocolateCereal() {
* let cereal = { cerealId: 'ChocolateYumminess' };
* let breakfast = { breakfastId: 'CerealAndMilk' };
*
* this.transitionTo('breakfast.cereal', breakfast, cereal);
* }
* }
* ```
*
* Nested Route with Query String Example
*
* ```app/routes.js
* // ...
*
* Router.map(function() {
* this.route('fruits', function() {
* this.route('apples');
* });
* });
*
* export default Router;
* ```
*
* ```app/routes/index.js
* import Route from '@ember/routing/route';
*
* export default class IndexRoute extends Route {
* @action
* transitionToApples() {
* this.transitionTo('fruits.apples', { queryParams: { color: 'red' } });
* }
* }
* ```
*
* @param name the name of the route or a URL.
* @param models the model(s) or identifier(s) to be used while
* transitioning to the route.
* @param options optional hash with a queryParams property
* containing a mapping of query parameters. May be supplied
* as the only parameter to trigger a query-parameter-only
* transition.
* @returns the Transition object associated with this attempted
* transition
*/
transitionTo(name: string, ...object: any[]): Transition;
/**
* The name of the view to use by default when rendering this routes template.
* When rendering a template, the route will, by default, determine the
* template and view to use from the name of the route itself. If you need to
* define a specific view, set this property.
* This is useful when multiple routes would benefit from using the same view
* because it doesn't require a custom `renderTemplate` method.
*/
transitionTo(name: string, ...object: any[]): Transition;
transitionTo(name: string, ...modelsOrOptions: object[]): Transition;
transitionTo(options: { queryParams: object }): Transition;
// https://emberjs.com/api/ember/3.2/classes/Route/methods/intermediateTransitionTo?anchor=intermediateTransitionTo
/**

View File

@@ -138,16 +138,42 @@ export default class RouterService extends Service {
options?: { queryParams: object }
): Transition;
// https://emberjs.com/api/ember/2.18/classes/RouterService/methods/isActive?anchor=transitionTo
// https://emberjs.com/api/ember/release/classes/RouterService/methods/isActive?anchor=transitionTo
/**
* Transition the application into another route. The route may be
* either a single route or route path
* Transition the application into another route. The route may
* be either a single route or route path:
*
* See [transitionTo](https://api.emberjs.com/ember/release/classes/Route/methods/transitionTo?anchor=transitionTo) for more info.
*
* Calling `transitionTo` from the Router service will cause default query parameter values to be included in the URL.
* This behavior is different from calling `transitionTo` on a route or `transitionToRoute` on a controller.
* See the [Router Service RFC](https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md#query-parameter-semantics) for more info.
*
* In the following example we use the Router service to navigate to a route with a
* specific model from a Component.
*
* ```app/components/example.js
* import Component from '@glimmer/component';
* import { action } from '@ember/object';
* import { inject as service } from '@ember/service';
*
* export default class extends Component {
* @service router;
*
* @action
* goToComments(post) {
* this.router.transitionTo('comments', post);
* }
* }
* ```
*
* @param routeNameOrUrl the name of the route or a URL
* @param models the model(s) or identifier(s) to be used while
* transitioning to the route.
* @param options optional hash with a queryParams property
* containing a mapping of query parameters
* containing a mapping of query parameters. May be
* supplied as the only parameter to trigger a
* query-parameter-only transition.
* @returns the Transition object associated with this attempted transition
*/
transitionTo(
@@ -180,6 +206,7 @@ export default class RouterService extends Service {
modelsD: RouteModel,
options?: { queryParams: object }
): Transition;
transitionTo(options: { queryParams: object }): Transition;
// https://emberjs.com/api/ember/2.18/classes/RouterService/methods/isActive?anchor=urlFor
/**

View File

@@ -104,6 +104,27 @@ class InvalidRedirect extends Route {
}
}
class TransitionToExamples extends Route {
// NOTE: this one won't check that `queryParams` has the right shape,
// because the overload for the version where `models` are passed
// necessarily includes all objects.
transitionToModelAndQP() {
this.transitionTo('somewhere', { queryParams: { neat: true } });
}
transitionToJustQP() {
this.transitionTo({ queryParams: { neat: 'true' }});
}
transitionToNonsense() {
this.transitionTo({ cannotDoModelHere: true }); // $ExpectError
}
transitionToBadQP() {
this.transitionTo({ queryParams: 12 }); // $ExpectError
}
}
class ApplicationController extends Controller {}
declare module '@ember/controller' {
interface Registry {

View File

@@ -0,0 +1,20 @@
import RouterService from '@ember/routing/router-service';
declare let router: RouterService;
router.transitionTo('someRoute');
router.transitionTo('someRoute', { withModel: true });
router.transitionTo('someRoute', { withModel: true }, { queryParams: {} });
router.transitionTo(
'someRoute',
{ withModel: true },
{ withMultipleModels: 'still checks correctly' },
{ queryParams: {} },
);
// NOTE: we cannot check the validity of invocations with just route name and
// query params beyond that the second argument is an object of some sort,
// because TS will always resolve it to the `models` variant if the
// `queryParams` variant fails.
router.transitionTo('someRoute', { queryParams: { shouldWork: true } });
router.transitionTo({ queryParams: { areSupported: true } });
router.transitionTo({ queryParams: 'potato' }); // $ExpectError

View File

@@ -57,6 +57,7 @@
"index.d.ts",
"test/lib/assert.ts",
"test/route.ts",
"test/router.ts"
"test/router.ts",
"test/router-service.ts"
]
}
}