Comments (8)
That is correct. The same information is provided on angular.dev.
https://angular.dev/guide/signals/model#customizing-model-inputs
Please use angular.dev for future reference as angular.io is not updated anymore:
from angular.
Does
export class Box {
position = model<Vector3|[number, number, number]>(new Vector3(0, 0, 0));
private vecPosition = computed(() => {
if (Array.isArray(position)) {
return new Vector3(position[0], position[1], position[2]);
} else {
return position;
}
}, equal: (a, b) => a.equals(b));
}
not address this use case?
In your first example, the positionChange
event is defined via toObservable
of the signal.
public readonly positionChange = outputFromObservable(toObservable(this.$position));
This is effectively doing (2) from my original message, and emitting positionChange
on any change of the position
input. This is unexpected from a 2-way binding - the change event should only be emitted for internal changes, not for changes originating from the input itself. This can have unwanted consequences around ExpressionChanged
or when a listener doesn't expect to see a change event from a binding change.
This actually highlights a benefit of model()
over direct implementation of the two-way binding contract - it's hard to get these subtle things right.
from angular.
The docs say model()
does not support input transformation, but it does not specify, whether its by design or simply not implemented at the moment: The why is missing, maybe that could be added in the docs.
https://angular.io/guide/model-inputs#customizing-model-inputs
from angular.
But if model would transform a incoming value and therefore adjusting the value of the model, it does update by 2 way binding the value of the parent?
Seems like a unwanted interaction going on here
from angular.
I haven't the details in mind but it is likely by design. In signal input document it says
Do not use transforms if they change the meaning of the input, or if they are impure.
This is to say that transform is mostly here to coerce the input to a type (name unkown
to string
, unknown
to boolean
). Since model
is dedicated to double binding and that the binding of output is typed, it don't make much sense to coerce a value since it have a defined emitted type.
We'll need a more concrete usecase from @e-oz to confirm that, but the feature request is probably linked to a misuse of the transform feature.
from angular.
model()
not supporting transform
is by design.
Model properties are designed for synchronization - keeping an outside copy of the data in sync with the value of the model. This is expressed via the two-way binding sugar [(model)]="outsideData"
. With this statement, outsideData
and the model should stay in sync via change detection (data -> model) and the modelChange
event (model -> data).
Because this synchronization is bi-directional, it'd be confusing to support a transformation during the data -> model operation. That would result in the value in the model being different than the value of the outside data. Either:
- We accept this discrepancy as how things work.
We don't think this option is a good fit, because it makes model
more difficult to reason about if the internal value can be different than the external value when its entire purpose is to keep them in sync.
This is also equivalent to declaring a computed
that applies the transform, which is straightforward to achieve already.
- We make
model
emit amodelChange
event after the transform changes the value.
This would keep the outside data in sync with the model, but has some major issues. For one, it would cause ExpressionChanged
errors if used with a non-signal two way binding. Even with signals, it could result in infinite looping change detection if the transform always produces a new value, which is easy to do accidentally.
Given that it's straightforward to work around this by using a computed
(which makes it obvious to anyone looking at the code that the transform is internal-only), we don't plan on adding any support for transform
to model
. However, we would be interested in collecting use cases when people feel like transform
is necessary, or that the additional boilerplate of a computed
is too great.
from angular.
Component <vos-box>
accepts position coordinates in 2 formats:
<vos-box [position]="[0, 1, - 1]"/>
<vos-box [position]="item.$position()" (positionChange)="item.$position.set($event)"/>
I would like to replace this:
@Component({
selector: 'vos-box',
})
export class Box {
private readonly $position = signal<Vector3>(new Vector3(0, 0, 0), { equal: (a, b) => a.equals(b)});
@Input() set position(position: Vector3 | [number, number, number]) {
if (Array.isArray(position)) {
this.$position.set(new Vector3(position[0], position[1], position[2]));
} else {
this.$position.set(position);
}
}
public readonly positionChange = outputFromObservable(toObservable(this.$position));
}
with this:
@Component({
selector: 'vos-box',
})
export class Box {
public readonly position = model<Vector3, [number, number, number] | Vector3>(new Vector3(0, 0, 0), {
transform: (position: Vector3 | [number, number, number]) => {
if (Array.isArray(position)) {
return new Vector3(position[0], position[1], position[2]);
} else {
return position;
}
},
equal: (a, b) => a.equals(b),
});
}
In this example, transform() is a pure function without side effects. It is not difficult to create other examples where transform() will also be pure.
I believe adding transform() here would simply make the API more consistent. While there are ways to misuse allowSignalWrites
, we still have it available.
from angular.
Agreed, it's too easy to cause infinite loops with transform()
in this scenario. The only workaround I can currently think of is to require equal
when transform
is set.
That would result in the value in the model being different than the value of the outside data.
Here, I respectfully disagree. The data that arrives at the input isn't always the direct representation of our model. It's merely a consequence of the fact that two-way binding in Angular serves as syntax sugar. There's no guarantee, nor should there be, that the (modelChange)
part will always be utilized or implemented, or that synchronization will occur at all. model()
should function without depending on this guarantee. Therefore, the only aspect that a component can refer to as the "value"
of a certain model is the value of the field defined by model()
, rather than relying on external data.
My initial rationale behind incorporating transform()
into model()
was to enforce this logic: "users may input any data into this field, but the model should consistently maintain accurate data. Unfiltered data or format disparities can potentially lead to issues." A straightforward example of this scenario is a time/date input field (where we have multiple formats for the same thing).
position = model<Vector3|[number, number, number]>(new Vector3(0, 0, 0));
Here output type is also Vector3|[number, number, number]
, not just Vector3
.
Nonetheless, I concur that the inclusion of transform()
raises the risk of infinite loops significantly. Even if we mandate users to specify equal
when declaring transform
, we cannot assure that a custom equal
will invariably prevent endless loops. While it may be straightforward to verify in the example I provided, in practical scenarios, this may not always be the case. I don't want to be responsible for all the possible CPU-warming pages, so I'm closing this issue 😉
I believe this particular use case is rare enough to justify dedicating one additional field instead of relying on transform()
😎
from angular.
Related Issues (20)
- Angular.dev doesn't cover Resolvers or how to fetch dynamic data
- Angular.dev show v0 instead of the latest version v17 HOT 6
- NullInjectorError in combination with standAlone is very unclear? HOT 3
- Router Outlet won't work if router link is in ngTemplate HOT 2
- NullInjectorError: No provider for Firestore2! HOT 1
- angular.dev: youtube embeds are being blocked on firefox HOT 1
- reading required signal input from effect causes error NG0950 when done inside structural directive creating an embedded view after promise resolves HOT 5
- withComponentInputBinding doesn't work as excepted with input signals HOT 1
- Angular 17 @for has memory leak HOT 2
- Reactive forms directives should be standalone HOT 1
- Rename Change detection "Default"
- `swPush.requestSubscription` never resolves on Arc browser (mac os) HOT 1
- AngularGPT
- Recursive template with @defer HOT 7
- The output-function page is not accessible https://angular.dev/guide/output-function
- developer preview features HOT 2
- toSignal causes the signal to not being lazy-evaluated HOT 6
- API for ngModule bootstrap for SSR? - `platformServer.bootstrapModule` vs `bootstrapApplication` of `@angular/platform-server`? HOT 2
- Renderer2 method that allows you to set signal inputs HOT 5
- Angular attributes produce HTML validation errors HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from angular.