Skip to content
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

Feature request: Conditionally add/remove attributes from the markup #76

Closed
Aaike opened this issue May 15, 2015 · 32 comments
Closed

Feature request: Conditionally add/remove attributes from the markup #76

Aaike opened this issue May 15, 2015 · 32 comments

Comments

@Aaike
Copy link
Contributor

Aaike commented May 15, 2015

Would be great if we can toggle html attributes like this :

<button disabled.if="isFirstPage"></button>

or even

<button disabled.if="page<=0"></button>
@EisenbergEffect
Copy link
Contributor

use disabled.bind

@Aaike
Copy link
Contributor Author

Aaike commented May 15, 2015

Sorry i actually meant for the attribute to be added/removed from the dom if isFirstPage is true so it can be styled with button:disabled

@Aaike
Copy link
Contributor Author

Aaike commented May 15, 2015

ok after checking again it works fine on form controls and buttons.
but not on links and other tags.
so <a disabled.bind="isLastPage"> does not work where i would then style it as a[disabled]{...}

@plwalters
Copy link
Contributor

@Aaike I'm not 100% certain that the disabled attribute is valid on other non-form controls. Same thing for the anchor tag. In these cases I think the best approach is to use a class for disabling to ensure compatibility. Thoughts?

@Aaike
Copy link
Contributor Author

Aaike commented May 15, 2015

yes that is what i am doing now, i was just exploring the various ways it can be done, there might be other uses cases for general attribute toggling. can't really think of any good use cases right now though :)

@timbell
Copy link

timbell commented Jan 25, 2016

Just wanted to check whether there has been any change here for arbitrary attributes? I'm working with bootstrap and I'd like to conditionally add data-toggle=xyz attributes. Currently I have data-toggle= in the negative case but it would be neater without the attribute at all.

@EisenbergEffect
Copy link
Contributor

You could always do this by creating a custom attribute that uses the element api to respond to bindings and add remove data- attributes.

Can you provide some more details around your scenario? Are you saying that when the value is null or undefined you want the attribute remove altogether for example?

@timbell
Copy link

timbell commented Jan 25, 2016

@EisenbergEffect - yes, be nice if it was built-in though :-)

Yes, that was what I was hoping for. eg data-toggle.bind="hasChildren ? 'dropdown' : null)" would give you either data-toggle=dropdown or no data-toggle attribute at all. Currently you get data-toggle=null so I changed by bind expression to ...? 'dropdown' : ''" just to reduce the size a bit. You could imagine some libraries where the presence of an empty attribute of this type might cause problems.

In angular 1 there was an ng-attr directive that did this, I think.

@timbell
Copy link

timbell commented Jan 25, 2016

Or if your data- attribute had the same semantics as the disabled attribute - active if present.

@philiphendry
Copy link

I have a question here regarding HTML boolean attributes. In my particular case I have a <select> element on which I optional wish to have the multiple attribute - and it would appear according to the HTML spec that I must add or remove this attribute. @EisenbergEffect, are you suggesting that I should create a custom attribute, bind it to a boolean then inside this attribute I could inject and manipulate the element to add/remove the require attribute. For example:

   <select toggle-multiple.bind="isMultiple">
   ...
   </select>

Given the HTML spec has boolean attributes it feels like this should be support by the framework or at the very least mentioned in the cheat sheet. Thoughts?

@EisenbergEffect
Copy link
Contributor

Using a custom attribute will certainly work. That's a definite way to always get the exact behavior you want.

@jdanyow What do you think of this issue though? It has come up repeatedly. Should we hard-code a list of the known boolean attributes in the binding engine (with an api to add to the list, of course) and then treat these differently by adding/removing the attr?

@alexisargyris
Copy link

It is interesting to note that when aurelia calls 'attached' (where one might add the attribute 'multiple' depending on the state of some custom attribute), it may be already too late, as the select element seems to have already picked up the first item of its bound list of options, which is something you don't want in the case of a multiple select. So, support for boolean attributes seems really useful.

@akircher
Copy link

Can we do interpolation inside html syntax such as <input ${ required && 'required'} ${ disabled && 'disabled'}>?

My use case is creating disabled menu items using Material Design Lite. This doesn't work for the their css selector .mdl-menu__item[disabled]

  <li
          disabled.bind="disabled"
          class="mdl-menu__item">
          Export CSV
   </li>

Update for those interested my workaround for now is

<li
          show.bind="!disabled"
          class="mdl-menu__item">
          Export CSV
</li>
<li
          show.bind="disabled"
          disabled
          class="mdl-menu__item">
          Export CSV
</li>

@EisenbergEffect
Copy link
Contributor

No that isn't supported. Our syntax is DOM-based. If it isn't valid html, we don't support it.

@robinchesterman
Copy link

I would like to do a similar thing. disabled.bind is fine, but for accessibility I'd like to add aria-disabled="true" if the condition is met.

@EisenbergEffect
Copy link
Contributor

That's a particularly insteresting case. I wonder if we could do that automatically. @jdanyow Any thoughts on the aria aspect?

@plwalters
Copy link
Contributor

Could there be an aria plugin that somehow augments the behavior for things like disabled? That way it's an opt-in and doesn't cause any confusion if not expected.

@robinchesterman
Copy link

I suppose I can just use aria-disabled="${disabled}"

@Baudin999
Copy link

Baudin999 commented May 22, 2016

I am also looking for a similar thing. For example, the placeholder attribute in materialize renders a placeholder when the attribute is on the element, it does not check if the attribute has an actual value. Similar situations are of course valid HTML but syntactical nonsense.

I am torn between wanting a feature like this (to add or remove an attribute depending on the existence of a parameter) and sending change request to the libraries involved to check the value of the attributes instead of the existence of the attribute. Apart from this almost silly example I think the use-case might be valid though.

Maybe it would be possible to do this in a non-declarative way? So only through the view-model?

export class VM {
    @attribute-binding placeholder;
}

It is such a "non-html" case that I think the view model might be the best place to solve this.

@timfish
Copy link
Contributor

timfish commented Oct 22, 2016

I came across this issue with a pure CSS tooltip. I didn't want the attribute adding at all if the string was undefined or null otherwise the pseudo elements get added.

Quite easily implemented with a custom attribute, so posting for reference:

import { inject, customAttribute, DOM } from 'aurelia-framework';

@customAttribute('tooltip')
@inject(DOM.Element)
export class Tooltip {
  constructor(private element: Element) { }

  valueChanged(newValue: string, oldValue: string) {
    if (newValue == undefined || newValue == null) {
      this.element.removeAttribute('data-tooltip');
    } else {
      this.element.setAttribute('data-tooltip', newValue);
    }
  }
}

@obedm503
Copy link

since this issue isn't fully resolved without workarounds, the issue should be reopened

@AlbertoMonteiro
Copy link

I have a custom attribute, and I want to add it if some condition is true, for now I am using tag with if.bind:

<template if.bind="!item.carregando">
    <i class="fa fa-right-dir lista-empresas_expandir-pastas" click.trigger="expandir(item)"></i>
</template>
<template if.bind="item.carregando">
    <i class="fa fa-arrows-cw animate-spin"></i>
</template>

another case

<template if.bind="exibindoEmpresas">
    <ul slim-scroll="customheight.one-time: 340" class="lista-empresas todo-list small-list">
        <!-- other markup -->
    </ul>
</template>
<template if.bind="!exibindoEmpresas">
    <ul class="lista-pastas">
        <!-- other markup -->
    </ul>
</template>

@rluba
Copy link

rluba commented Jul 6, 2018

@EisenbergEffect Here’s another use-case that’s difficult to implement without native support in Aurelia: I want to use Aurelia’s route-href attribute to link to a state, but only if a certain condition is met. For example: Link to an "edit" state, but only if the item is not read-only.

But there’s no built-in way to conditionally add route-href="…" to an element. It’s easy to write a custom attribute that adds normal HTML attributes to an element, it’s much more complex for custom attributes (or Aurelia’s attributes). Duplicating the whole element is not an option since it either could contain a lot of markup or explodes combinatorially if you have more than one conditional attribute.

In Angular1, this was trivially solved with ng-attr-*, i.e., I could use ng-attr-route-href="{{editable ? 'route: edit; params.bind: …' : undefined}}". ng-attr sets the attribute to the result of the expression and removes it if the expression evaluates to undefined – and this happens before custom attributes are evaluated and bound, so it even works for things like route-href.

@EisenbergEffect
Copy link
Contributor

@rluba What would be the meaning of the link if there was no route-href? Would you want to remove the entire link instead?

@timfish
Copy link
Contributor

timfish commented Jul 6, 2018

For a similar situations we bind to disabled although I don't think this is supported by default in older browsers.

@rluba
Copy link

rluba commented Jul 9, 2018

What would be the meaning of the link if there was no route-href? Would you want to remove the entire link instead?

@EisenbergEffect In this case I’d prefer to remove the whole <a> tag and just keep its children, but I don’t mind it staying there without a href.

But in the general case I just want to remove the attribute. (For example if I want to conditionally show a tooltip, etc.)

For a similar situations we bind to disabled…

That’s not appropriate here. The element can contain plenty of children that might be active, I just want to remove a particular link (or tooltip or …).

I worked around this particular issue by creating a custom copy of route-href that takes a condition parameter, but that’s an unnecessary copy that needs to be maintained.

I’d strongly prefer a solution to conditionally set attributes that also works for custom attributes, i.e., an equivalent to AngularJS’s ng-attr-*.

@EisenbergEffect
Copy link
Contributor

@jdanyow Could we do this with a binding behavior? We've got the attr binding behavior today that does this for built-in attributes. Could we expand that to work for custom attributes as well, in a general way?

For reference to those on this thread:

@rluba
Copy link

rluba commented Aug 5, 2018

@EisenbergEffect That would be great, but I’m not sure if it’s feasible. The binding behavior would have to remove and unbind it’s own attribute, thereby unbinding itself with no way to re-bind when the condition becomes true again.
If you get this to work via a binding behavior, I expect it’ll contain lots of messy hacks.

@EisenbergEffect
Copy link
Contributor

So, are you saying you want the whole lifecycle of the attribute to act as if it's bound/attached when it's true and detached/unbound when its false? That would be a change in behavior to attributes in general, as apposed to an attribute whose value is just changed from true to false but where the actual attribute in HTML is added/removed in association with that. With the custom attribute system we can add/remove the HTML attributes while keeping the Aurelia attribute working. At runtime, from Aurelia's perspective, the HTML attribute is mostly just a marker and doesn't affect the runtime once the template is parsed. Does that make sense?

@rluba
Copy link

rluba commented Sep 3, 2018

Yes, it does.

I understand that there’s nothing comparable in Aurelia at the moment. I understand that just toggling HTML attributes is rather trivial for Aurelia and adding/removing whole Aurelia attributes seems to be close to impossible. I suggested it because other frameworks offer that capability (see ng-attr-* in AngularJS) and it is useful in some circumstances.

But I’ve created workarounds for my use-cases in the meantime and they work fine. One required me to "copy & own" route-href, which I had preferred to avoid, but it’s not a deal-breaker for me.

@EisenbergEffect
Copy link
Contributor

@rluba I realize it's not ideal to have to copy and own something like that, but I do highly appreciate your willingness to go that route. Aurelia being open source, and since it's likely impossible that we can handle every possible real-scenario, we really wanted to ensure that the framework was extensible enough for you to be able to do things like that. For vNext we're hoping to not only correct and add some behaviors but take extensibility up to a new level. We'd love for you to get involved in helping us define the vNext router. There's some discussion going on for that over here: aurelia/aurelia#67

Additionally, if you are interested, we'd love to have you guest post on the official Aurelia blog, sharing with the community about your needs and how you customized things to solve your problems.

The invitation is always open to anyone who has interesting stories and valuable experience to share with the community.

@rluba
Copy link

rluba commented Sep 4, 2018

I’ll have a look at the discussion for vNext.

Thank you for the invitation, @EisenbergEffect. I appreciate it and will think about what I could share that might be useful for others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests