-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component/attribute inheritance and precedence for primitives #5484
Comments
A bit of a tangent. From the beginning I've considered primitives a liability and a source of complexity and headaches. It makes markup inconsistent in a model where we have tags that map to entities and their attributes to components. I definitively see the appeal of primitives for beginners We could rename |
Primitives have a nice benefit in terms of DX and readability, which extend beyond just beginner use. With only <a-entity checkpoint geometry="primitive: cylinder; height: 1.5; radius: 0.2" material="color: green"></a-entity>
<a-entity checkpoint="final: true" geometry="primitive: cylinder; height: 1.5; radius: 0.2" material="color: purple" move-back-and-forth></a-entity> <a-checkpoint color="green"></a-checkpoint>
<a-checkpoint final="true" color="purple" move-back-and-forth></a-checkpoint> Of course there are multiple ways to achieve similar result. Mixins can be used or a component can be created that bootstraps the other components. Though, when working on a larger project, I find defining primitives to be a really nice way to make a sort of 'DSL' of tags. The usefulness quickly drops the more you do programmatically instead of declaratively, but I don't see this as an argument against it. It's akin to how Code complexity can be greatly reduced if the default values slot into the already existing inheritance behaviour. Having components without corresponding attribute is already the case for mixins, so dropping primitives won't help there. Only the attribute mappings are specific for primitives, but that is already handle in the primitive internally. |
In practice when writing HTML or instantiating entities with JS you don't deal with the component expansion. The
or in JS
The difference is pretty marginal if any at the expense of breaking the model consistency and introducing ambiguity (only |
That's one of the alternative approaches I mentioned, but it isn't without its drawbacks. If the component naively sets the other components, it won't properly handle mixins or conflicting component values on the entity. Exposing all properties of the underlying components in its schema is tedious and couples the bootstrapping component tighter to other components than needed. All in all it would be very tricky to correctly implement the lifecycle hooks ( This inheritance order is arguable a neat feature of primitives. The goal of this issue is to see if we can/should expand this behaviour beyond just initialization. That would allow users to add and remove attributes on primitives while retaining the base/default components and values of those primitives, which I think is generally more intuitive and expected behaviour.
While I do agree that having attributes map to either components or properties is less consistent and a potential source of confusion (/ learning curve), the problem of collisions could easily be addressed by having attribute mapping collisions be handled the same way as component name collisions, that is, by throwing an error. Since primitives are instances of
Is that really the case? Both AR-JS and 8thwall provide primitives. Virtually all sky and ocean/water implementations offer a primitive. And GUI libraries like aframe-gui and aframe-material-collection do as well for their UI elements. Primitives are ideal for those use-cases. Many users creating experiences instead of libraries will likely find no need to write their own primitives. But that doesn't mean they don't benefit from consuming primitives (built-in ones or third party). |
I think there's not really a difference recommending |
Description:
The inheritance and precedences of values for a primitive is currently as follows:
This is handled during entity and component initialization, but later updates can easily 'break' things. For example a
resetProperty
completely ignores mixins. Clobbering a multi-prop component wipes primitive defaults and mappings, yet retains mixins. There's a difference between initializing a primitive with a mixin and adding that mixin later. Some of these are fixed in #5474, but a lot of this logic for primitives lives outside of components. Effectively the precedence is:Especially the way mixins are handled causes unintuitive inconsistencies like the fact that a mixin at init time on a primitive can result in a different outcome compared to that mixin being added post initialization.
However, there are benefits to the current behaviour, despite its inconsistencies. Because it's all combined into the AttrValue of a component, it allows the user to "subtract" from it. Take the following example:
Which is also documented and also an answer on StackOverflow. So it's a given that users do depend on this behaviour.
At the same time users might be surprised why the following behaves inconsistently:
Only the box turns blue, while the image doesn't get "tinted" blue.
Personally I would be for enforcing the full inheritance at all times. This should avoid many inconsistent and unitive situations. But it won't allow any "subtracting", as in, the primitive can't be reduced beyond what it is by default. That would be a breaking change in behaviour, though.
The text was updated successfully, but these errors were encountered: