ember-link
Introduces a new Link primitive to pass around self-contained references to
routes, like URLs, but with state (isActive, ...) and methods (transitionTo,
...). Also brings along an accompanying template helper and component for easy
usage in templates.
ember-linkdoes to routing whatember-concurrencydid to asynchrony!
Installation
ember install ember-link
Usage
{{link}} Helper
The {{link}} helper returns a UILink instance.
Invocation Styles
Positional Parameters
Named Parameters
When passing a single model, you can use model instead of models:
Mix & Match
You can also mix and match the parameter styles, however you like.
fromURL
Instead of the positional & named link parameters described above, you can also
create a Link instance from a serialized URL.
fromURL is mutually exclusive with the other link parameters: route, model
& models, query
Parameters
In addition to the parameters shown above, the {{link}} helper also accepts a
preventDefault default parameter. It defaults to true and intelligently
prevents hard browser transitions when clicking <a> elements.
See @preventDefault and UILink.
💡 Pro Tips
Instead of using the {{#let}} helper, you can use the
<Link> component to achieve the same scoping effect, with
subjectively nicer syntax.
Even better yet, make Link / UILink a first-class
primitive in your app architecture! Instead of manually wiring up
Link#url and Link#transitionTo() every time, rather
create your own ready-to-use, style-guide-compliant link and button components
that accept @link as an argument instead of @href and @onClick.
This is akin to the popular async task button component concept.
<Link> Component
Similar to the the {{link}} helper, the <Link> component
yields a UILink instance.
Arguments
@route
Required.
The target route name.
Example
{{link-to}} equivalent
@models
Optional. Mutually exclusive with @model.
An array of models / dynamic segments.
Example
{{link-to}} equivalent
@model
Optional. Mutually exclusive with @models.
Shorthand for providing a single model / dynamic segment. The following two invocations are equivalent:
@query
Optional.
Query Params object.
Example
{{link-to}} equivalent
@fromURL
Optional. Mutually exclusive with @route, @model /
@models, @query.
Example
@preventDefault
Optional. Default: true
If enabled, the transitionTo and
replaceWith actions will try to call
event.preventDefault() on the first argument, if it is an
event. This is an anti-foot-gun to make <Link> just work<a> and
<button>, which would otherwise trigger a native browser navigation / form
submission.
Yielded Parameters
The <Link> component yields a UILink instance.
url
string
The URL for this link that you can pass to an <a> tag as the href attribute.
isActive
boolean
Whether this route is currently active, including potentially supplied models and query params.
In the following example, only one link will be is-active at any time.
isActiveWithoutQueryParams
boolean
Whether this route is currently active, including potentially supplied models, but ignoring query params.
In the following example, the first two links will be is-active simultaneously.
isActiveWithoutModels
boolean
Whether this route is currently active, but ignoring models and query params.
In the following example, both links will be is-active simultaneously.
transitionTo()
(event?: Event) => Transition
Transition into the target route.
If @preventDefault is enabled, also calls event.preventDefault().
replaceWith()
(event?: Event) => Transition
Transition into the target route while replacing the current URL, if possible.
If @preventDefault is enabled, also calls event.preventDefault().
Link
A Link is a self-contained reference to a concrete route, including models and
query params. It's basically like a
<LinkTo> / {{link-to}} component you can pass around.
You can create a Link via the LinkManager service.
UILink extends Link with some anti-foot-guns and conveniences. It
can also be created via the LinkManager service, but also via
the {{link}} helper and <Link> component.
Properties
isActive
Type: boolean
Whether this route is currently active, including potentially supplied models and query params.
isActiveWithoutQueryParams
Type: boolean
Whether this route is currently active, including potentially supplied models, but ignoring query params.
isActiveWithoutModels
Type: boolean
Whether this route is currently active, but ignoring models and query params.
url
Type: string
The URL for this link that you can pass to an <a> tag as the href attribute.
routeName
Type: string
The target route name of this link.
models
Type: RouteModel[]
The route models passed in this link.
queryParams
Type: Record<string, unknown> | undefined
The query params for this link, if specified.
Methods
transitionTo()
Returns: Transition
Transition into the target route.
replaceWith()
Returns: Transition
Transition into the target route while replacing the current URL, if possible.
UILink
UILink extends Link with anti-foot-guns and conveniences. This
class is meant to be used in templates, primarily through <a> & <button>
elements.
It wraps transitionTo() and replaceWith() to optionally accept an event
argument. It will intelligently
- call
event.preventDefault()to prevent hard page reloads - open the page in a new tab, when
Cmd/Ctrlclicking
It can be created via the LinkManager service, but also via
the {{link}} helper and <Link> component.
LinkManager
The LinkManager service is used by the {{link}} helper and
<Link> component to create UILink instances.
You can also use this service directly to programmatically create link references.
createLink(linkParams: LinkParams): Link
Returns: Link
interface LinkParams {
/**
* The target route name.
*/
route: string;
/**
* Optional array of models / dynamic segments.
*/
models?: RouteModel[];
/**
* Optional query params object.
*/
query?: QueryParams;
}createUILink(linkParams: LinkParams, uiParams: UILinkParams): UILink
Returns: UILink
interface UILinkParams {
/**
* Whether or not to call `event.preventDefault()`, if the first parameter to
* the `transitionTo` or `replaceWith` action is an `Event`. This is useful to
* prevent links from accidentally triggering real browser navigation or
* buttons from submitting a form.
*
* Defaults to `true`.
*/
preventDefault?: boolean;
}getLinkParamsFromURL(url: string): LinkParams
Returns: LinkParams
Use this method to derive LinkParams from a serialized, recognizable URL, that
you can then pass into createLink / createUILink.
Testing
In acceptance / application tests (setupApplicationTest(hooks))
your app boots with a fully-fledged router, so ember-link just works normally.
In integration / render tests (setupRenderingTest(hooks)) the
router is not initialized, so ember-link can't operate normally. To still
support using {{link}} & friends in render tests, you can use the
setupLink(hooks) test helper.
import { click, render } from '@ember/test-helpers';
import { setupRenderingTest } from 'ember-qunit';
import { module, test } from 'qunit';
import { setupLink, linkFor, TestLink } from 'ember-link/test-support';
import hbs from 'htmlbars-inline-precompile';
module('`setupLink` example', function (hooks) {
setupRenderingTest(hooks);
setupLink(hooks);
test('`<Link>` component works in render tests', async function (assert) {
await render(hbs`
<Link @route="some.route" as |l|>
<a
href={{l.url}}
class={{if l.isActive "is-active"}}
{{on "click" l.transitionTo}}
>
Click me
</a>
</Link>
`);
const link = linkFor('some.route');
link.onTransitionTo = assert.step('link clicked');
await click('a');
assert.verifySteps(['link clicked']);
});
});Related RFCs / Projects
ember-engine-router-service: Allows you to useember-linkinside enginesember-router-helpers- RFC 391 "Router Helpers"
- RFC 339 "Router link component and routing helpers"
- RFC 459 "Angle Bracket Invocations For Built-in Components"
