ngFor is a structural directive that renders a template for each item in a collection. The directive is placed on an element, which becomes the parent of the cloned templates. The ngForOf directive is generally used in the shorthand form *ngFor. In this form, the template to be rendered for each iteration is the content of an anchor element containing the directive.
Angular creates an < ng-template > element and applies the *ngFor directive onto it, where it becomes a property binding in square brackets, [ngFor]. The rest of the < div >, including its class attribute, is then moved inside the < ng-template >:

<ul *ngFor="let item of items; let i = index" data-index="i">
   <li>{{item}}</li>
</ul>

here is how your code will be converted by angular internally

<ng-template ngFor let-item [ngForOf]="items" let-i="index">
   <ul data-index="i">
       <li>{{item}}</li>
   </ul>
</ng-template>

In the above code, you can see that the data-index attribute having “i” that will always give you data-index=”i” because it is not the attribute of the ul element that’s why the value of i is not acceptable

If you want to store the value of i in the data-index attribute, then you need to make data-index as attribute

//for angular 2+
<ul>
   <li  *ngFor="let item of items; let i = index" [attr.data-index]="i"> 
       {{item}}
   </li>
</ul>
//for angularJS
<ul>
   <li *ngFor="#item of items; #i = index" [attr.data-index]="i">
       {{item}}
   </li>
</ul>

And if you add *ngFor on ul, your ul will also be part of the iteration, that’s why I added it on li.

References:
angular.io/guide
angular.io/api

Support On Demand!

Angular