2

I'm trying to create a nested treeview.

@Component({
    selector: "myItem",
    template: `
        <li *ngIf="!Array.isArray(data)"> {{data.text}} </li>
        <ul *ngIf="Array.isArray(data)">
            <myItem *ngFor="let x of data" [(data)] = "x.data"> </myItem>
        </ul>
    `
})
export class MyItemComponent {
    static root = true;
    data: any;

    constructor() {
        if (MyItemComponent.root) {
            this.data = [
                { text: "foo" },
                [{ text: "bar" }, { text: "foo" }]
            ];
            MyItemComponent.root = false;
        }
    }
}

The error is

Can't bind to 'data' since it isn't a known native property ("ta.text}} ][data] = "x.data"> "): MyItemComponent

EDIT: how can I implement a delete button? The template should look like:

        <li *ngIf="!Array.isArray(data)"> {{data.text}} <button (click)="clicked()"> delete me</button> </li>
        <ul *ngIf="Array.isArray(data)"> <button (click)="clicked()"> delete me</button>
            <myItem *ngFor="let x of data" [(data)] = "x.data"> </myItem>
        </ul>

What would be the click function?

aristotll
  • 8,694
  • 6
  • 33
  • 53
Zen
  • 5,065
  • 8
  • 29
  • 49
  • 3
    https://plnkr.co/edit/6uYQ25LTaydBNlACCTra?p=preview – yurzui May 15 '16 at 08:21
  • I think there is a flaw in the logic, `data` is an array in both cases (outer array, inner array) so `!isArray(data)` will always be false and the `li`will fail to render every time. – Thorsten Westheider May 15 '16 at 20:19

2 Answers2

2

Adding custom attributes to Components

I think this will solve your issue better: to be able to bind to custom attributes in the newer versions of Angular2 you must use [attr.custom] syntax. See this issue and this one for more information.

Look at @yurzui's comment - it links you to a working Plunker

If you want to add custom inputs to a Directive
If you'd like to input data into a directive, this can be done with Angular2's Input. E.g.

 export class MyItemComponent {
  //...
  @Input('attribute') attribute: any;
  //...
 }

This will ensure that in your Component Angular will know where to look to get the data.

Thanks to @Günter Zöchbauer for clarification :)

  • I still get that error and another warning: `"#" inside of expressions is deprecated. Use "let" instead!`. – Zen May 15 '16 at 07:45
  • @Zen all good, you're right there - I just checked - I'm using an older version of Angular2. Anyway, did you try adding `@Input`? – Aᴄʜᴇʀᴏɴғᴀɪʟ May 15 '16 at 08:05
  • @Zen did that solve your issue? Just seeing if I can help you work it out :) – Aᴄʜᴇʀᴏɴғᴀɪʟ May 15 '16 at 08:12
  • No, still `Can't bind to 'data' since it isn't a known native property `. – Zen May 15 '16 at 08:14
  • @Zen okay I'll start a new project and try to replicate your problem – Aᴄʜᴇʀᴏɴғᴀɪʟ May 15 '16 at 08:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/111969/discussion-between-c-and-zen). – Aᴄʜᴇʀᴏɴғᴀɪʟ May 15 '16 at 08:15
  • @zen just updated the answer, and I wrote it in chat too: use `[(attr.data)]` rather than `[(data)]` and it should bind without any errors :) – Aᴄʜᴇʀᴏɴғᴀɪʟ May 15 '16 at 08:35
  • 1
    `[(attr.property)]` won't really work and it binds to an attribute not a property. First an attribute doesn't emit events, therefore `()` with `attr` doesn't make sense and `[attr.somename]` is to create an attribute on the element with the name `somename` (Angular also reads attributes into `@Input()` properties therefore the value assignment will work but not the event binding). See also http://stackoverflow.com/questions/6003819/properties-and-attributes-in-html. – Günter Zöchbauer May 15 '16 at 18:32
  • 1
    `'data'` in `@Input('data') data: any;` is redundant because its the same name as the property name and it should be avoided (from the Angular2 style guide) naming inputs different than the class properties where they are applied because thats quite confusing. – Günter Zöchbauer May 15 '16 at 18:32
2

You can't use Array... in binding expressions or any other global reference like (window, Object, enums or other type names, ...). Angular can only refer to properties or methods of its components class or template variables.

You can create a function in your component and call this function instead

 <li *ngIf="!isArray(data)"> {{data.text}} </li>
 export class MyComponent {
   isArray(arr) { return Array.isArray(arr); }

   ...
 }

As mentioned <myItem> needs to have an @Input() data for value binding ([...]) to work and an @Output() dataChange = new EventEmitter() for event binding ((...)) to work and the output name has to be the same as the input name only with a Change suffix for the shorthand [(...)] "two-way-binding" to work.

x is already an item from the data array and this item doesn't have a data property (the 1st item has a text property and the 2nd is an array that contains two objects, therefore also no data property).

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567