Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Add repeat-range directive #10925

Open
Serabass opened this issue Feb 1, 2015 · 14 comments
Open

Add repeat-range directive #10925

Serabass opened this issue Feb 1, 2015 · 14 comments

Comments

@Serabass
Copy link

Serabass commented Feb 1, 2015

Programmers want this directive!
Google has many examples with ng-range, ng-repeat-range, but a lot of them working through compiling to e.g ng-repeat="n in [1,2,3,4,5,6]". This may create a large arrays in memory.

I want to see the ng-range directive as:
<div ng-range="5"> {{ n }} </div> - renders my element 5 times (n is a default alias)
<div ng-range="5 as x"> {{ x }} </div> - x is an new alias
<div ng-range="1 to 10"> {{ n }} </div>
<div ng-range="1 to 10 as x"> {{ x }} </div>
<div ng-range="10 step 2"> {{ n }} </div>
<div ng-range="10 step 2 as x"> {{ x }} </div>
<div ng-range="1 to 10 step 2"> {{ n }} </div>
<div ng-range="1 to 10 step 2 as x"> {{ x }} </div>

I wrote the RegExp for parse this string.
/^(.+?)(?:\s*to\s*(.+?))?(?:\s*step\s*(.+?))?(?:\s*as\s*(\w+))?$/.

Thanks!

@frfancha
Copy link

frfancha commented Feb 1, 2015

@Serabass ng-repeat="n in [1,2,3,4,5,6]". This may create a large arrays in memory
Hum. Array of integers are really very small in memory, so if really you make large integer arrays for ng-repeat, that means that the created DOM elements which take much much more room than a simple integer will explose your memory.

@Serabass
Copy link
Author

Serabass commented Feb 1, 2015

@frfancha all the same, this directive in official bundle does not prevent

@lgalfaso
Copy link
Contributor

lgalfaso commented Feb 1, 2015

@Serabass I think it should be possible to write a filter that does all this. It would be a filter that takes begin, end and step and would generate an array with the right elements. Variations of what you are proposing were discussed before at #8210 and #9310. In both cases, the call was that this was fine, but should not be part of the core.

I think that your proposal falls into the same category, it is very helpful, but it falls into something that should be part of a third party module.

@pkozlowski-opensource
Copy link
Member

@lgalfaso what about adding something on the expression level - that is - make $parse understand [x..y]and turning it into an array (I'm assuming that both x and y are constants here). But yeh, I agree with you that this one can be easily covered outside of the core...

@lgalfaso
Copy link
Contributor

lgalfaso commented Feb 2, 2015

@pkozlowski-opensource #8210 added just that with the exception that x and y can be expressions too, and that there was also a ... option that would create the array with the range [x,y). The issue is that it cannot be made flexible enough. Eg, we cannot add a step, as we would be limited by the begin and end properties. A filter does not have that limitation and can be made more flexible.

That said, I still think it would be best if this is not part of the core and a third party module.

@Serabass
Copy link
Author

Serabass commented Feb 2, 2015

I found the function from Google and modify it for understanding the step and alias with examples from first topic:
`
angular.module('myApp')
.directive('ngRepeatRange', ['$compile', function ($compile) {
return {
replace: true,
link: function (scope, element, attrs) {

            // returns an array with the range of numbers
            // you can use _.range instead if you use underscore
            function range(from, to, step) {
                var result = [];

                for (var i = from; i <= to; i += step) {
                    result.push(i);
                }

                return result;
            }

            var rgx = /^(.+?)(?:\s*to\s*(.+?))?(?:\s*step\s*(.+?))?(?:\s*as\s*(\w+))?$/;

            var data = rgx.exec(attrs.ngRepeatRange.trim());

            // prepare range options
            var from;
            var to;
            var step = scope.$eval(data[3]) || 1;
            var alias = data[4] || 'n';

            if (data[2]) {
                from = scope.$eval(data[1]);
                to = scope.$eval(data[2]);
            } else {
                from = 0;
                to = scope.$eval(data[1]);
            }

            // get range of numbers, convert to the string and add ng-repeat
            var rangeString = range(from, to, step).join(',');

            angular.element(element).attr('ng-repeat', alias + ' in [' + rangeString + ']');
            angular.element(element).removeAttr('ng-repeat-range');

            $compile(element)(scope);
        }
    };
}]);

`

@lgalfaso
Copy link
Contributor

lgalfaso commented Feb 2, 2015

It would be a lot easier if done with a filter

@syndicatedshannon
Copy link

Not saying this should be built-in, but here's the problem I'm currently having that easy access to such a feature would solve.

It's not just a single in-memory array that is the cost. Because the examples the OP was referring to compile to ng-repeat, the array actually appears many times in the DOM, with resource consumption growing exponentially. More repeats make the attributes larger, and also generates more of those larger attributes. Just a few nestings of this demonstrates vastly diminished scalability/performance.

I agree that a filter avoids this issue, however it adds the complexity of managing index uniqueness. I'm currently using: http://stackoverflow.com/questions/11873570/angularjs-for-loop-with-numbers-ranges#14932395

@lgalfaso
Copy link
Contributor

@syndicatedshannon using a filter solves many odd cases, and this is why it works best. Eg, say that there is a filter named range that given and end (and optional begin) returns an array that is [begin..end], then doing <div ng-repeat="foo in 5 | range">{{foo}}</div> would solve the following issues:

  • Are the begin, end, step, etc parameters expressions or constants? Do you do not care, $parse and ngRepeat will do the right thing
  • Do you want to initialize the array and then just use it. Again, just ng-init="foo = 10 | range:5"
  • Do you want to iterate over chars/token/something else. If the range filter does not support it, create your own filter (that is a lot easier than to write a directive)
  • No memory leaks. It took a lot of time to be sure that ngRepeat does not leak (and in fact it looks like the implementation at Add repeat-range directive #10925 (comment) does not handle some cases so it can leak)

@syndicatedshannon
Copy link

Yeah, I'm not arguing with you. I also see "trackBy: $index" isn't required if the filter returns unique values. A filter is working fine for me.

Of course, semantically it still looks really awkward to me. I wouldn't easily have thought of it on my own.

edit: explaining that last comment... I think that since, as you mentioned, a filter solves many unusual cases, it's perfectly represented by the "alter/embellish" syntax the pipe notation offers. However, representing a highly normal/structured grid by embellishing an empty set just doesn't feel natural.

@graingert
Copy link
Contributor

any update on this?

@gkalpak
Copy link
Member

gkalpak commented Sep 7, 2016

It's in the Ice Box, so probably never going to be added to core.

@graingert
Copy link
Contributor

@gkalpak ice box?

@syndicatedshannon
Copy link

syndicatedshannon commented Sep 7, 2016

https://github.com/angular/angular.js/milestone/38 : "Ice Box" - Milestone for valid issues and bugs that we have no aspirations on fixing any time soon because they are not affecting primary use-cases and are not aligned with our goals.

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

No branches or pull requests

8 participants