All Projects → buschtoens → ember-link

buschtoens / ember-link

Licence: MIT license
Link primitive to pass around self-contained route references. It's {{link-to}}, but better!

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language
HTML
75241 projects
Handlebars
879 projects

Projects that are alternatives of or similar to ember-link

Ember Apollo Client
🚀 An ember-cli addon for Apollo Client and GraphQL
Stars: ✭ 257 (+414%)
Mutual labels:  ember, emberjs, ember-addon
ember-best-language
🏳 A FastBoot-enabled addon to detect the best language for your user.
Stars: ✭ 18 (-64%)
Mutual labels:  ember, emberjs, ember-addon
Ember Accessibility
An EmberJS addon to help identify accessibility violations during development
Stars: ✭ 29 (-42%)
Mutual labels:  ember, emberjs, ember-addon
ember-deep-tracked
Deep auto-tracking for when you just don't care, and want things to work (at the cost of performance in some situtations)
Stars: ✭ 20 (-60%)
Mutual labels:  ember, emberjs, ember-addon
ember-on-modifier
Implements the `{{on eventName this.someAction}}` element modifier from https://github.com/emberjs/rfcs/blob/master/text/0471-on-modifier.md
Stars: ✭ 37 (-26%)
Mutual labels:  ember, emberjs, ember-addon
ember-gridstack
Ember components to build drag-and-drop multi-column grids powered by gridstack.js
Stars: ✭ 31 (-38%)
Mutual labels:  ember, emberjs, ember-addon
Ember Cli Foundation 6 Sass
Stars: ✭ 65 (+30%)
Mutual labels:  ember, emberjs, ember-addon
ember-changeset-conditional-validations
Conditional validations for ember-changeset-validations
Stars: ✭ 26 (-48%)
Mutual labels:  ember, emberjs, ember-addon
Ember Cli Addon Docs
Easy, beautiful docs for your OSS Ember addons
Stars: ✭ 162 (+224%)
Mutual labels:  ember, emberjs, ember-addon
Ember Cli Bundle Analyzer
Analyze the size and contents of your Ember app's bundles
Stars: ✭ 78 (+56%)
Mutual labels:  ember, emberjs, ember-addon
ember-credit-card
"make your credit card form dreamy in one line of code"
Stars: ✭ 89 (+78%)
Mutual labels:  ember, emberjs, ember-addon
ember-foxy-forms
Ember Addon for Making Foxy Forms
Stars: ✭ 27 (-46%)
Mutual labels:  ember, emberjs, ember-addon
ember-headlessui
gavinjoyce.github.io/ember-headlessui/
Stars: ✭ 76 (+52%)
Mutual labels:  ember, emberjs, ember-addon
ember-render-helpers
Complimentary render template helpers to the render modifiers
Stars: ✭ 19 (-62%)
Mutual labels:  ember, emberjs, ember-addon
glimmer-apollo
Ember and Glimmer integration for Apollo Client.
Stars: ✭ 32 (-36%)
Mutual labels:  ember, emberjs, ember-addon
Docfy
Build fully personalized documentation sites; write content and demos in Markdown.
Stars: ✭ 48 (-4%)
Mutual labels:  ember, emberjs, ember-addon
ember-event-helpers
Complimentary event template helpers to the {{on}} modifier
Stars: ✭ 33 (-34%)
Mutual labels:  ember, emberjs, ember-addon
ember-shadow-dom
Write templates for your components inside of a Shadow DOM root.
Stars: ✭ 26 (-48%)
Mutual labels:  ember, emberjs, ember-addon
Ember Styleguide
This is a UI addon that intends to help standardize the Ember family of websites and make it easier to make the Ember website an Ember app.
Stars: ✭ 69 (+38%)
Mutual labels:  ember, emberjs, ember-addon
ember-cli-string-helpers
Set of the String helpers extracted from DockYard's ember-composable-helpers.
Stars: ✭ 73 (+46%)
Mutual labels:  ember, emberjs, ember-addon

ember-link

CI npm version Download Total Ember Observer Score code style: prettier
Dependabot enabled dependencies Status devDependencies Status

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-link does to routing what ember-concurrency did to asynchrony!

/r/whatjawsdid

Installation

ember install ember-link

Usage

{{link}} Helper

The {{link}} helper returns a UILink instance.

Invocation Styles

Positional Parameters
{{#let
  (link
    "blogs.posts.post"
    @post.blog.id
    @post.id
    (query-params showFullPost=true)
  )
  as |l|
}}
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Read the full "{{@post.title}}" story on our {{@post.blog.name}} blog!
  </a>
{{/let}}
Named Parameters
{{#let
  (link
    route="blogs.posts.post"
    models=(array @post.blog.id @post.id)
    query=(hash showFullPost=true)
  )
  as |l|
}}
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Read the full "{{@post.title}}" story on our {{@post.blog.name}} blog!
  </a>
{{/let}}

When passing a single model, you can use model instead of models:

{{#let
  (link
    route="blogs.posts"
    model=@post.blog.id
  )
  as |l|
}}
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Read more stories in the {{@post.blog.name}} blog!
  </a>
{{/let}}
Mix & Match

You can also mix and match the parameter styles, however you like.

{{#let
  (link
    "blogs.posts.post"
    @post.blog.id
    @post.id
    query=(hash showFullPost=true)
  )
  as |l|
}}
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Read the full "{{@post.title}}" story on our {{@post.blog.name}} blog!
  </a>
{{/let}}
fromURL

Instead of the positional & named link parameters described above, you can also create a Link instance from a serialized URL.

{{! someURL = "/blogs/tech/posts/dont-break-the-web" }}
{{#let (link fromURL=this.someURL) as |l|}}
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Read the next great post.
  </a>
{{/let}}

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.

<Ui::LinkButton @link={{link "subscribe"}}>
  Become a Premium member
</Ui::LinkButton>
<a
  href={{@link.url}}
  class="btn"
  {{on "click" @link.transitionTo}}
  ...attributes
>
  {{yield}}
</a>

<Link> Component

Similar to the the {{link}} helper, the <Link> component yields a UILink instance.

<Link
  @route="some.route"
  @models={{array 123}}
  @query={{hash foo="bar"}}
as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Click me
  </a>
</Link>

Arguments

@route

Required.

The target route name.

Example

<Link @route="some.route" as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Click me
  </a>
</Link>

{{link-to}} equivalent

{{#link-to "some.route"}}
  Click me
{{/link-to}}
@models

Optional. Mutually exclusive with @model.

An array of models / dynamic segments.

Example

<Link @route="some.route" @models={{array someModel someNestedModel}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Click me
  </a>
</Link>

{{link-to}} equivalent

{{#link-to "some.route" someModel someNestedModel}}
  Click me
{{/link-to}}
@model

Optional. Mutually exclusive with @models.

Shorthand for providing a single model / dynamic segment. The following two invocations are equivalent:

<Link @route="some.route" @model={{someModel}} />
<Link @route="some.route" @models={{array someModel}} />
@query

Optional.

Query Params object.

Example

<Link @route="some.route" @query={{hash foo="bar"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Click me
  </a>
</Link>

{{link-to}} equivalent

{{#link-to "some.route" (query-params foo="bar")}}
  Click me
{{/link-to}}
@fromURL

Optional. Mutually exclusive with @route, @model / @models, @query.

Example

<Link @fromURL="/blogs/tech/posts/dont-break-the-web" as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Click me
  </a>
</Link>
@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™️ with <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.

<Link @route="some.route" as |l|>
  <a href={{l.url}} {{on "click" l.transitionTo}}>
    Click me
  </a>
</Link>
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.

<Link @route="some.route" @models={{array 123}} @query={{hash foo="bar"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    One
  </a>
</Link>

<Link @route="some.route" @models={{array 123}} @query={{hash foo="quux"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActive "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Two
  </a>
</Link>
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.

<Link @route="some.route" @models={{array 123}} @query={{hash foo="bar"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActiveWithoutQueryParams "is-active"}}
    {{on "click" l.transitionTo}}
  >
    One
  </a>
</Link>

<Link @route="some.route" @models={{array 123}} @query={{hash foo="quux"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActiveWithoutQueryParams "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Two
  </a>
</Link>

<Link @route="some.route" @models={{array 456}} @query={{hash foo="quux"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActiveWithoutQueryParams "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Three
  </a>
</Link>
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.

<Link @route="some.route" @models={{array 123}} @query={{hash foo="bar"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActiveWithoutModels "is-active"}}
    {{on "click" l.transitionTo}}
  >
    One
  </a>
</Link>

<Link @route="some.route" @models={{array 456}} @query={{hash foo="quux"}} as |l|>
  <a
    href={{l.url}}
    class={{if l.isActiveWithoutModels "is-active"}}
    {{on "click" l.transitionTo}}
  >
    Two
  </a>
</Link>
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 / Ctrl clicking

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].