How to properly extend the IF attribute

Are there any examples of how to properly extend an if attribute. I would like to create a custom-attribute that extends the IF attribute, so that my custom attribute can control the if.condition property.

For example I’d like to have this:

<div has-claim="some:claim">
    Some Secure DIV
</div>

and have the IF logic applied to this element assuming I have “some:claim”

Here’s what I have so far:

import {bindingMode} from ‘aurelia-binding’;
import {bindable, BoundViewFactory, customAttribute, ViewSlot} from ‘aurelia-templating’;
import {DOM} from ‘aurelia-pal’;
import {inject} from ‘aurelia-framework’;
import {If} from “aurelia-templating-resources”;
import {Store} from “aurelia-store”;

@customAttribute(‘has-claim’, bindingMode.oneTime)
@inject(DOM.Element, BoundViewFactory, ViewSlot, Store)
export class HasClaimCustomAttribute extends If {
@bindable({primaryProperty: true}) claim;

constructor(element, boundViewFactory, viewSlot, store) {
    super(boundViewFactory, viewSlot);
    this.element = element;
    this.store = store;
}

static inject() {
    return [DOM.Element, BoundViewFactory, ViewSlot, Store];
}

bind(bindingContext, overrideContext) {
    super.bind(bindingContext, overrideContext);
    this.subscription = this.store.state.subscribe(
        (state) => this.state = state
    );
}

unbind() {
    this.subscription.unsubscribe();
}

}

which gives me the following error:

ERROR [app-router] Error: Error invoking HasClaimCustomAttribute. Check the inner error for details.

Inner Error:
Message: Cannot read property ‘setValue’ of undefined
Inner Error Stack:
TypeError: Cannot read property ‘setValue’ of undefined
at HasClaimCustomAttribute.descriptor.set [as cache] (webpack-internal:///hij8:3998:30)
at HasClaimCustomAttribute.IfCore (webpack-internal:///aurelia-templating-resources:241:20)
at HasClaimCustomAttribute.If [as constructor] (webpack-internal:///aurelia-templating-resources:307:47)
at new HasClaimCustomAttribute (webpack-internal:///common/resources/custom-attributes/has-claim:73:58)
at Object.invokeWithDynamicDependencies (webpack-internal:///3U8n:459:18)
at InvocationHandler.invoke (webpack-internal:///3U8n:434:166)
at Container.invoke (webpack-internal:///3U8n:705:23)
at ProviderResolver.get (webpack-internal:///hij8:2340:72)
at Container.get (webpack-internal:///3U8n:644:21)
at Container.elementContainerGet [as get] (webpack-internal:///hij8:2395:15)
at HtmlBehaviorResource.create (webpack-internal:///hij8:4413:56)
at applyInstructions (webpack-internal:///hij8:2505:31)
at ViewFactory.create (webpack-internal:///hij8:2726:7)
at BoundViewFactory.create (webpack-internal:///hij8:2606:33)
at IfCore._show (webpack-internal:///aurelia-templating-resources:272:42)
at If.bind (webpack-internal:///aurelia-templating-resources:314:18)
End Inner Error Stack

Any help is appreciated.

1 Like

Maybe @Sayan751can help give a realworld example on this, as hes done it

This code is from the aurelia cheat sheet, does that help you? It’s a naive if implementation.

import {BoundViewFactory, ViewSlot, customAttribute, templateController, autoinject} from 'aurelia-framework';

@customAttribute('naive-if')
@templateController
@autoinject
export class NaiveIf {
	constructor(private viewFactory: BoundViewFactory, private viewSlot: ViewSlot) { }

	valueChanged(newValue: any): void {
	  if (newValue) {
		let view = this.viewFactory.create();
		this.viewSlot.add(view);
	  } else {
		this.viewSlot.removeAll();
	  }
	}
}
1 Like

@elitastic I take it back!.. It does work… ty…

I needed to add the @templateController so that my BoundVewFactory had a viewFactory on it.

@bigopon: So is there not a straight forward way to simply extend the IF attribute? If not in the legacy, how about for vNext? I would think that I should be able to just extend it and move on?

1 Like

Is there a show version of this?

1 Like

This should be much simpler (untested code!)

import {customAttribute, autoinject} from 'aurelia-framework';

@customAttribute('naive-show')
@autoinject
export class NaiveShow {
	constructor(private element: Element) { }

	valueChanged(newValue: any): void {
		this.element.style.display = newValue ? 'block' : 'none';
	}
}
1 Like

First, thank-you for all your help! I’ve managed to get things working as a work-around for extending the native controls. @bigopon, I hope this is looked at with the vNext, as all your internal controls for Aurelia should be easily extendable (the Typescript way)

So for the IF version, is there something that needs to be done to keep the bindings between setup and teardown? I.e. I set some bindings, the IF pulls them off the dom, it then puts them back on the dom, but I’ve lost information/functionality.

1 Like

It should be relatively easy, as you can see from the source code of if. Did yoy try mimicking the built in if ? It seems you went similar routes, so I guess you were confused by which methods should be extended and which not?