Framework: @transient does not appear to work for custom elements

Created on 15 Sep 2016  路  24Comments  路  Source: aurelia/framework

I'm submitting a bug report

  • Library Version:
    1.0.0

Please tell us about your environment:

  • Operating System:
    OSX 10.11.2
  • Node Version:
    0.4.0
  • NPM Version:
    3.8.9
  • JSPM OR Webpack AND Version
    JSPM 0.16.38
  • Browser:
    all
  • Language:
    ESNext

Current behavior:
@transient on a custom element doesn't cause it to be created/destroyed using if.bind

Expected/desired behavior:
@transient decorator should cause the custom element to be completely destroyed/recreated
https://gist.run/?id=b4c451b5a1940afa3fc321a434377e2e

  • What is the motivation / use case for changing the behavior?
    theif.bind example is the simplest I could think of to test the expected behavior of @transient as shown here in the Aurelia Hub
import {transient, inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';

@transient()
@inject(HttpClient)
export class CustomerDetail {
  constructor(http) {
    this.http = http;
  }
}

Most helpful comment

+1

It's very counterintuitive that custom element removed and re-added from the view with if.bind does not resets its state. Only show.bind should behave like this.

All 24 comments

This has nothing to do with the @transient decorator but how the if attribute works. Custom elements are transient by default and not so easy to make otherwise.

Okay, thanks. If you don't mind me asking, I have two follow up questions.

  1. If custom elements are transient by default, would you agree the documentation I referred to should be updated or removed since the @transient in that example isn't doing anything.
  2. Is there a way besides compose and router-view to make a custom element be destroyed and recreated when a bindable value changes?

Custom element lifetime is controlled by the View itself.

Using compose lets you take control as you desire.

@jpray About 1. - the example isn't using a custom element, so without @transient CustomerDetail will be registered as a singleton.

Thanks @EisenbergEffect and @StrahilKazlachev.

I was hoping to avoid compose but I'll chew on this for a while and work on some more gists. I think there might be a feature request coming soon to normalize the lifecycle methods between a regular viewModel/custom element/router-view/compose. It seems that any view/viewModel pair should be able to be developed on its own without having to be specifically written for use by router-view or compose. Will do some more research regarding this. Thanks.

+1

It's very counterintuitive that custom element removed and re-added from the view with if.bind does not resets its state. Only show.bind should behave like this.

Hi,

Sorry to revive this old issue; but is there any known workaround for this? I fully agree with what @fracz said and like @StrahilKazlachev, I'm trying to avoid using compose (having custom elements available makes the use of compose for the same thing kind of weird).

Thanks

ping @EisenbergEffect - Sorry to bother, but I'm trying to push Aurelia as a replacement on my current stack and being forced to use compose it's like a deal breaker, If I want the element to keep "living", I use show.bind.

Thanks.

Custom elements are designed to work just like normal dom elements, so they are always transient. You can use show.bind if you just want to show/hide something. The compose element is for dynamic composition, when you don't know what component is going to be rendered until runtime and you need to do that based on data. In that case, compose can render any model and you can have the model be singleton or transient, whatever makes sense.

I think the pain point is all about if.bind. It seems to behave so similarly to show.bind when being toggled multiple times. Would be nice to have a templating control that was able to blow away and recreate the DOM based on the template. Here an example:
https://gist.run/?id=7e65836736b6c28fa8d25d192d4d8833

The following workaround is the biggest framework related code smell in my Aurelia apps.

<template if.bind="someBoolean">
  <compose view-model="my-view-that-should-be-destroyed.js"></compose>
</template>

Internally, if caches the component so that if you toggle the values back and forth, it doesn't have to go through the work of creating and destroying over an over. However, we could extend the the if attribute itself so that you could control that behavior. For backwards compat reasons, we would leave the default behavior the same as today, but add a property to basically opt in to turning the caching off. Thoughts?

馃挴馃挴馃挴 Just have to come up with a good name. if-but-without-the-caching-part 馃槀

I've been wondering which of the following would make the most sense...

<div transient-if.bind="foo">
<div if.bind="foo" transient>
<div if.transient.bind="foo">

also, for the "else" view resource, would it inherit the behavior of the preceding "if", or would its behavior be defined separately. I think separate makes sense. Of the three options listed above I think

<div if.bind="foo" transient>
<div else transient>

feels the cleanest. Is there a precedent for having a custom attribute affect a view resource?

@EisenbergEffect Maybe something like this should be configurable at both application level in FrameworkConfiguration and per If via bindable property ?

@EisenbergEffect sounds great.

I like @bigopon idea, if possible, to make it something configurable at both application level and per instance.

@jpray As a separated attribute looks good :D.

I would live to contribute with a PR for this, but I'm kind of new to Aurelia, I've been using it only for a month, sorry!

We can't do this at the application level because if you are using a 3rd party component that internally uses an if binding, then changing it at the app level would effect the internal implementation of the component, possibly causing it to malfunction. We can do it on a per-case basis though. It can be done using some new templating features. So you could have:

Normal Behavior

if.bind="someValue"

No Cache

if="value.bind: someValue; cache: none"

To be honest, I'm not a big fan of that kind of syntax, I would even prefer a dictionary over a semicolon separated list :P.

I was thinking, isn't it possible to make it a static property of the element itself? because if the element can be cached or not doesn't sound like a template issue but a model/controller issue: It's on your model that you expect for something to happen.

Taking a look at the rest of the framework, it makes more sense to have in the element than having to remember how a life cycle is written in order to implement it the right way.

Couldn't the cache flag and ignore objects (not sure if it's a generic cache or just for elements) that implements the static property?

I think it feels more "natural" to write it on the element as:

_"I'm marking the element transient/noCacheable/whatever because I expect it to be reseted every time is attached"_

than

_"I need to remember that this element doesn't reset it state on the attached but on the constructor, so I have to disable cache if I want to implement it with an if"_.

IMO :P

The problem is, what if the custom element is nested deeply in a tree of elements? In that case, now the if behavior has to do a deep node walk of all views in order to determine what behavior it should use to ensure the proper functioning of the element. That would kill performance and make the entire framework unusable, so we can't do that.

The simples option is to enable controlling this on the if itself. The syntax show above is the standard syntax support by Aurelia for multi-option custom attributes, since day one. So, that's the way we would need to do it, for consistency and full support. This syntax was designed to align with existing HTML standards, such as the way that the style attribute works.

  • Cache: Damn it, I assumed the cache worked on a different way, every instance as a different record, but if it works that way, yeah, it would be a performance kill. But you have to admit that it will be kind of weird as you'll have to be aware of the element internal functionality in order to use it on an if.

  • Syntax: I'm going to blame Angular and JSX :P... you are right, semicolon separated list is the (html) way to go.

One last thing (because I'm that kind of annoying person), why none and not false for the cache option? Are you thinking on more scenarios than just "save it" and "don't save it"?

Btw, thanks so much for taking the time to reply to all my comments even when I ask for functionalities without knowing how the framework internally works. This talks a lot about the people behind it :)

Good question about none. I had false first, then changed my post because, without having typed bindables, you would need to do cache.bind: false to get a Boolean value, whereas none would be interpreted as a string automatically without using bind. I did wonder whether there could be other options, but maybe that's not realistic. We are getting close on the typed bindable feature, so we could wait until we had that and then implement this.

Sounds good 馃憤 (560 is the PR for typed bindables right?)

Yes, that's part of it. It's a tricky little feature, so it might take some time to get it to where we want it. In order to track the if cache control, @homer0 can you open a new issue on the templating-resources repo to track that enhancement?

Done aurelia/templating-resources#320

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dasch88 picture dasch88  路  4Comments

ConductedClever picture ConductedClever  路  5Comments

piet-v picture piet-v  路  3Comments

RichiCoder1 picture RichiCoder1  路  4Comments

pvencill picture pvencill  路  6Comments