Skip to content

Commit

Permalink
Merge tag 'v2.0.0-beta.6' into release
Browse files Browse the repository at this point in the history
**BREAKING CHANGE:**

* **compiler:** avoid using au class to find targets (#1768) ([0d30998](0d30998)).
* ***:** rename resolveAll -> onResolveAll (#1764) ([fdf0747](fdf0747))

**Features:**

* **di:** ability to use newInstance()/forScope() with interface (#1767) ([a0d39e9](a0d39e9))
* **all:** allow injection of implementation (#1766) ([a60db13](a60db13))
* **templating:** allow deactivate when activating (#1729) ([1c9c97c](1c9c97c))
* **bindable:** support getter/setter (#1753) ([4279851](4279851))
* **ui-virtualization:** enhance implementation & publish package (#1759) ([7a2f17f](7a2f17f)). Thanks [@Lakerfield](https://github.com/Lakerfield)

**Refactorings:**

* ***:** cleanup up unused code & decouple interface from default impl (#1761) ([7a71d43](7a71d43))
* **router:** add warning for unsupported behavior (#1757) ([ce87339](ce87339)). The router used to allow container traversal to
find resources when a string is used as a route to find the corresponding component.
Going forward this will be invalid and resources must be registered either locally
or globally to be routable. A warning is added first so app that accidentally used
this behavior can detect the invalid usages and fix accordingly first. This behavior will be removed in a near future release.
  • Loading branch information
AureliaEffect committed May 21, 2023
2 parents c4b0dd9 + 6b01d30 commit 4b10aa7
Show file tree
Hide file tree
Showing 226 changed files with 6,830 additions and 4,037 deletions.
14 changes: 12 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ executors:
docker:
- image: "cimg/node:<< parameters.node >>-browsers"



docker_release:
working_directory: *working_dir
resource_class: large
Expand Down Expand Up @@ -845,6 +843,12 @@ workflows:
path: "packages/__e2e__/i18n"
requires:
- build_release
- e2e_test:
<<: *filter_ignore_develop_release
name: e2e_virtual_repeat
path: "packages/__e2e__/ui-virtualization"
requires:
- build_release
- tacho_benchmark_prep:
<<: *filter_ignore_develop_release
requires:
Expand Down Expand Up @@ -996,6 +1000,12 @@ workflows:
path: "packages/__e2e__/i18n"
requires:
- build_release
- e2e_test:
<<: *filter_ignore_develop_release
name: e2e_virtual_repeat
path: "packages/__e2e__/ui-virtualization"
requires:
- build_release

# - lint_packages:
# <<: *filter_only_tag
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"dependencies": {
"tachometer": "0.7.0"
},
"version": "2.0.0-beta.5"
"version": "2.0.0-beta.6"
}
27 changes: 27 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

<a name="2.0.0-beta.6"></a>
# 2.0.0-beta.6 (2023-05-21)

### BREAKING CHANGE:

* **compiler:** avoid using au class to find targets (#1768) ([0d30998](https://github.com/aurelia/aurelia/commit/0d30998)).
* ***:** rename resolveAll -> onResolveAll (#1764) ([fdf0747](https://github.com/aurelia/aurelia/commit/fdf0747))

### Features:

* **di:** ability to use newInstance()/forScope() with interface (#1767) ([a0d39e9](https://github.com/aurelia/aurelia/commit/a0d39e9))
* **all:** allow injection of implementation (#1766) ([a60db13](https://github.com/aurelia/aurelia/commit/a60db13))
* **templating:** allow deactivate when activating (#1729) ([1c9c97c](https://github.com/aurelia/aurelia/commit/1c9c97c))
* **bindable:** support getter/setter (#1753) ([4279851](https://github.com/aurelia/aurelia/commit/4279851))
* **ui-virtualization:** enhance implementation & publish package (#1759) ([7a2f17f](https://github.com/aurelia/aurelia/commit/7a2f17f)). Thanks [@Lakerfield](https://github.com/Lakerfield)


### Refactorings:

* ***:** cleanup up unused code & decouple interface from default impl (#1761) ([7a71d43](https://github.com/aurelia/aurelia/commit/7a71d43))
* **router:** add warning for unsupported behavior (#1757) ([ce87339](https://github.com/aurelia/aurelia/commit/ce87339)). The router used to allow container traversal to
find resources when a string is used as a route to find the corresponding component.
Going forward this will be invalid and resources must be registered either locally
or globally to be routable. A warning is added first so app that accidentally used
this behavior can detect the invalid usages and fix accordingly first. This behavior will be removed in a near future release.


<a name="2.0.0-beta.5"></a>
# 2.0.0-beta.5 (2023-04-27)

Expand Down
1 change: 1 addition & 0 deletions docs/user-docs/TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
* [Logging](getting-to-know-aurelia/logging.md)
* [Building plugins](developer-guides/building-plugins.md)
* [Web Components](developer-guides/web-components.md)
* [UI virtualization](developer-guides/ui-virtualization.md)
* [Errors](developer-guides/error-messages/README.md)
* [0001 to 0015](developer-guides/error-messages/0001-to-0015/README.md)
* [AUR0001](developer-guides/error-messages/0001-to-0015/aur0001.md)
Expand Down
2 changes: 0 additions & 2 deletions docs/user-docs/aurelia-packages/dialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,6 @@ Each dialog instance goes through the full lifecycle once.

## V1 Dialog Migration

* `DialogService` is no longer injectable. Inject `IDialogService` instead.
* `DialogController` is no longer injectable. Inject `IDialogController` instead.
* `viewModel` setting in `DialogService.prototype.open` is changed to `component`.
* `view` setting in `DialogService.prototype.open` is changed to `template`.
* `keyboard` setting in `DialogService.prototype.open` is changed to accept an array of `Enter`/`Escape` only. Boolean variants are no longer valid. In the future, the API may become less strict.
Expand Down
68 changes: 55 additions & 13 deletions docs/user-docs/components/bindable-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ description: >-
libraries.
---

# Bindable properties
## Bindable properties

When creating components, sometimes you will want the ability for data to be passed into them. The `@bindable` decorator allows you to specify one or more bindable properties for a component.
When creating components, sometimes you will want the ability for data to be passed into them instead of their host elements. The `@bindable` decorator allows you to specify one or more bindable properties for a component.

The `@bindable` attribute also can be used with custom attributes as well as custom elements. The decorator denotes bindable properties on components on the view model of a component.

Expand Down Expand Up @@ -72,7 +72,7 @@ export class NameComponent {

You can then use the component in this way,\``<name-component first-name="John" last-name="Smith"></name-component>`

### Calling a change function when bindable is modified
## Calling a change function when bindable is modified

By default, Aurelia will call a change callback (if it exists) which takes the bindable property name followed by `Changed` added to the end. For example, `firstNameChanged(newVal, previousVal)` would fire every time the `firstName` bindable property is changed.

Expand Down Expand Up @@ -182,7 +182,7 @@ In some cases, you want to make an impact on the value that is binding. For such

```typescript
@bindable({
set: value => function(value), /* HERE */
set: value => someFunction(value), /* HERE */
// Or set: value => value,
mode: /* ... */
})
Expand All @@ -206,16 +206,10 @@ Suppose you have a `carousel` component in which you want to enable `navigator`

In version two, you can easily implement such a capability with the `set` feature.

To make things easier, first design a new type that accepts `true` and `false` as a string and a boolean.
```typescript
export type BooleanString = "true" | "false" | true | false /* boolean */;
```
Define your property like this:

```typescript
@bindable({ set: /* ? */, mode: BindingMode.toView }) public navigator: BooleanString = false;
@bindable({ set: /* ? */, mode: BindingMode.toView }) public navigator: boolean = false;
```

For `set` part, we need functionality to check the input. If the value is one of the following, we want to return `true`, otherwise, we return the `false` value.
Expand All @@ -235,17 +229,65 @@ export function truthyDetector(value: unknown) {
Now, we should set `truthyDetector` function as follows:

```typescript
@bindable({ set: truthyDetector, mode: BindingMode.toView }) public navigator: BooleanString = false;
@bindable({ set: truthyDetector, mode: BindingMode.toView }) public navigator: boolean = false;
```

Although, there is another way to write the functionality too:

```typescript
@bindable({ set: v => v === '' || v === true || v === "true", mode: BindingMode.toView }) public navigator: BooleanString = false;
@bindable({ set: v => v === '' || v === true || v === "true", mode: BindingMode.toView }) public navigator: boolean = false;
```

You can simply use any of the above four methods to enable/disable your feature. As you can see, `set` can be used to transform the values being bound into your bindable property and offer more predictable results when dealing with primitives like booleans and numbers.

## Bindable & getter/setter

By default, you'll find yourself work with binable and field most of the time, like the examples given above. But there' cases where
it makes sense to have bindable as a getter, or a pair of getter/setter to do more logic when get/set.

For example, a component card nav that allow parent component to query its active status.
With bindable on field, it would be written like this:

```ts
@customElement({ name: 'card-nav', template })
export class CardNav implements ICustomElementViewModel {
@bindable routes: RouteLink[] = [];

@bindable({ mode: BindingMode.fromView }) active?: string;

bound() {
this.setActive();
}

setActive() {
this.active = this.routes.find((y) => y.isActive)?.path;
}

handleClick(route: RouteLink) {
this.routes.forEach((x) => (x.isActive = x === route));
this.setActive();
}
}
```
Note that because `active` value needs to computed from other variables, we have to "actively" call `setActive`. It's not a big deal, but sometimes not desirable.

For cases like this, we can turn `active` into a getter, and decorate it with bindable, like the following:
```ts
@customElement({ name: 'card-nav', template })
export class CardNav implements ICustomElementViewModel {
@bindable routes: RouteLink[] = [];

@bindable({ mode: BindingMode.fromView }) get active() {
return this.routes.find((y) => y.isActive)?.path;
}

handleClick(route: RouteLink) {
this.routes.forEach((x) => (x.isActive = x === route));
}
}
```
Simpler, since the value of `active` is computed, and observed based on the properties/values accessed inside the getter.

## Bindable coercion

The bindable setter section shows how to adapt the value is bound to a `@bindable` property. One common usage of the setter is to coerce the values that are bound from the view. Consider the following example.
Expand Down
1 change: 0 additions & 1 deletion docs/user-docs/developer-guides/error-messages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ Dependency Injection errors can be found [here](0001-to-0015/).
| Error Code | Plugin name | Description |
| ---------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| AUR0901 | Dialog | This happens when an application is closed with some dialogs still open |
| AUR0902 | Dialog | This happens when `DialogController` injection is requested. It's a error prevention for v1->v2 migration of the dialog plugin |
| AUR0903 | Dialog | This happens when `IDialogService.open` is called without both `component` and `template` property |
| AUR0904 | Dialog | This happens when the default configuration of the dialog plugin is used, as there's no registration associated for key interfaces |

Expand Down
102 changes: 102 additions & 0 deletions docs/user-docs/developer-guides/ui-virtualization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

## Installation

{% hint style="info" %}
This plugin is in development, expect unstability.
{% endhint %}

Install via npm

```
npm install @aurelia/ui-virtualization
```

Load the plugin

```javascript
import { Aurelia } from 'aurelia';
import { DefaultVirtualizationConfiguration } from '@aurelia/ui-virtualization';

Aurelia
.register(DefaultVirtualizationConfiguration)
.app(...)
```

## Using the plugin

Simply bind an array to `virtual-repeat` like you would with the standard `repeat`. The repeated rows are expected to have equal height throughout the list, and one item per row.

### div
```html
<template>
<div virtual-repeat.for="item of items">
${$index} ${item}
</div>
</template>
```

### list
```html
<template>
<ul>
<li virtual-repeat.for="item of items">
${$index} ${item}
</li>
</ul>
</template>
```

### table
```html
<template>
<table>
<tr virtual-repeat.for="item of items">
<td>${$index}</td>
<td>${item}</td>
</tr>
</table>
</template>
```

```javascript
export class MyVirtualList {
items = ['Foo', 'Bar', 'Baz'];
}
```

With a surrounding fixed height container with overflow scroll. Note that `overflow: scroll` styling is inlined on the elemenet. It can also be applied from CSS.
An error will be thrown if no ancestor element with style `overflow: scroll` is found.

## Caveats

1. `<template/>` is not supported as root element of a virtual repeat template. This is due to the difficulty of technique employed: item height needs to be calculatable. With `<tempate/>`, there is no easy and performant way to acquire this value.
2. Similar to (1), other template controllers cannot be used in conjunction with `virtual-repeat`, unlike `repeat`. I.e: built-in template controllers: `with`, `if`, etc... cannot be used with `virtual-repeat`. This can be workaround'd by nesting other template controllers inside the repeated element, with `<template/>` element, for example:

```html
<template>
<h1>${message}</h1>
<div virtual-repeat.for="person of persons">
<template with.bind="person">
${Name}
</template>
</div>
</template>
```
3. Beware of CSS selector `:nth-child` and similar selectors. Virtualization requires appropriate removing and inserting visible items, based on scroll position. This means DOM elements order will not stay the same, thus creating unexpected `:nth-child` CSS selector behavior. To work around this, you can use contextual properties `$index`, `$odd`, `$even` etc... to determine an item position, and apply CSS classes/styles against it, like the following example:

```html
<template>
<div virtual-repeat.for="person of persons" class="${$odd ? 'odd' : 'even'}-row">
${person.name}
</div>
</template>
```
4. Similar to (3), virtualization requires appropriate removing and inserting visible items, so not all views will have their lifecycle invoked repeatedly. Rather, their binding contexts will be updated accordingly when the virtual repeat reuses the view and view model. To work around this, you can have your components work in a reactive way, which is natural in an Aurelia application. An example is to handle changes in change handler callback.

## Tobe implemented feature list

- [ ] ability to configure height & many aspects of the virtual repeat
- [ ] infinite scroll
- [ ] horizontal scrolling
- [ ] variable height
- [ ] scrolling direction
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ In some cases, you want to make an impact on the value that is binding. For such

```typescript
@bindable({
set: value => function(value), /* HERE */
set: value => someFunction(value), /* HERE */
// Or set: value => value,
mode: /* ... */
})
Expand Down Expand Up @@ -57,14 +57,13 @@ export function truthyDetector(value: unknown) {
Now, we should set `truthyDetector` function as following:

```typescript
@bindable({ set: truthyDetector, mode: BindingMode.toView }) public navigator: BooleanString = false;
@bindable({ set: truthyDetector, mode: BindingMode.toView }) public navigator: boolean = false;
```

Although, there is another way to write the functionality too

```typescript
@bindable({ set: v => v === '' || v === true || v === "true", mode: BindingMode.toView }) public navigator: BooleanString = false;
@bindable({ set: v => v === '' || v === true || v === "true", mode: BindingMode.toView }) public navigator: boolean = false;
```

You can simply use any of the above four methods to enable/disable your feature.
14 changes: 7 additions & 7 deletions examples/1kcomponents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
"postbuild:all": "rollup -c"
},
"dependencies": {
"@aurelia/kernel": "2.0.0-beta.5",
"@aurelia/metadata": "2.0.0-beta.5",
"@aurelia/platform": "2.0.0-beta.5",
"@aurelia/platform-browser": "2.0.0-beta.5",
"@aurelia/runtime": "2.0.0-beta.5",
"@aurelia/runtime-html": "2.0.0-beta.5",
"@aurelia/kernel": "2.0.0-beta.6",
"@aurelia/metadata": "2.0.0-beta.6",
"@aurelia/platform": "2.0.0-beta.6",
"@aurelia/platform-browser": "2.0.0-beta.6",
"@aurelia/runtime": "2.0.0-beta.6",
"@aurelia/runtime-html": "2.0.0-beta.6",
"d3-scale-chromatic": "^3.0.0",
"perf-monitor": "latest"
},
"devDependencies": {
"@aurelia/http-server": "2.0.0-beta.5",
"@aurelia/http-server": "2.0.0-beta.6",
"webpack": "^5.72.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2"
Expand Down

0 comments on commit 4b10aa7

Please sign in to comment.