My code on Angular 2 runs 10-15 times slower than similar on AngularJS.
What am I doing wrong and how to fix it?
There is an array of the following view (there are 1000 elements)
http://pastebin.com/wZ82vVEb
data = [{col:25, y:96, color:"#ddfd51"}, {col:19, y:20, color:"#a05304"}, ...];
For each value of col
, an svg
element is displayed (there are reasons not to draw everything on one svg), containing rectangles with color coordinate y
. Each of the elements can be selected. For this there is a set of checkboxes that are bound to the fields isSelected
.
In the examples, the columns are turned into rows for ease of display. Accordingly, for each col
svg
element 1 * 100 is displayed with rectangles with x
coordinate equal to y
. All rectangles are distributed in two groups g
. The first one is for unselected items, the second is for selected items. Separation into two groups is required due to the fact that svg does not support z-index
, and the coordinates of the elements may coincide, therefore it is necessary to ensure that the selected elements go in the markup after the unselected ones.
This is how it looks on AngularJS
https://jsfiddle.net/1taex13L/5/
angular.module('app', []) .controller('MainController', function ($scope) { var data = $scope.data = window.data; // See pastebin script }) .controller('ChartController', function ($scope, $filter) { $scope.columns = []; for (var q=0; q<32; ++q) { $scope.columns.push($scope.data.filter(function (item) { return item.col === q; })); } $scope.falseTrue = [false, true]; }) .controller('SelectionController', function ($scope) { }) .directive('bindAttrs', function () { return function (scope, element, attrs) { scope.$watch(attrs.bindAttrs, function (val) { element.attr(val); }) } }) .filter('truthy', function () { return function (items, prop, val) { return items.filter(function (item) { return !!item[prop] === val; }) } })
html { margin: 1em; } figure { margin: 0 0 1em 0; border: 1px dotted; } svg { display: block; width: 100%; } g { opacity: .1; } g.selected { opacity: 1; } input { margin: 0; vertical-align: middle; } label { display: inline-block; line-height: 1em; width: 1em; }
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script> <script src="//pastebin.com/raw/wZ82vVEb"></script> <main ng-app="app" ng-controller="MainController"> <figure ng-controller="ChartController"> <svg ng-repeat="c in columns" viewBox="0 0 100 1"> <g ng-repeat="sel in falseTrue" ng-class="{selected:sel}"> <rect ng-repeat="r in c | truthy : 'isSelected' : sel" bind-attrs="{x:ry, fill:r.color}" y="0" height="100%" width="1" /> </g> </svg> </figure> <div ng-controller="SelectionController"> <label ng-repeat="item in data"><input type="checkbox" ng-model="item.isSelected"></label> </div> </main>
And so - on Angular 2
http://plnkr.co/edit/IxbVrvpECWwpcCCDmIjK?p=preview
Removed what still did not work, it turned out like this:
http://plnkr.co/edit/grj61gIizyCRmY8QpqEF?p=preview
plus the question, but what did I subscribe to ?
import {Component, View, Input, Output, EventEmitter, OnChanges} from '@angular/core' @Component({ selector: 'chart-component', template: ` <figure> <svg *ngFor="let c of columns" viewBox="0 0 100 1"> <g *ngFor="let s of unselSel" [ngClass]="{selected:s==='sel'}"> <rect *ngFor="let r of c[s]" [attr.x]="ry" [attr.fill]="r.color" y="0" height="100%" width="1" /> </g> </svg> </figure> ` }) export class ChartComponent implements OnChanges { @Input() data : any[]; columns : any[]; unselSel = ['unsel', 'sel']; ngOnChanges() { this.columns = []; for (let q=0; q<32; ++q) { this.columns.push({sel:[], unsel:[]}) } for (let item of this.data) { this.columns[item.col][item.isSelected ? 'sel' : 'unsel'].push(item); } } } @Component({ selector: 'selection-component', template: ` <label *ngFor="let item of data"><input type="checkbox" [(ngModel)]="item.isSelected"></label> ` }) export class SelectionComponent { @Input() data; } @Component({ selector: 'my-app', template: ` <chart-component [data]="data"></chart-component> <selection-component [data]="data" (change)="update()"></selection-component> `, directives: [ChartComponent, SelectionComponent] }) export class AppComponent { data = window.data; // See pastebin update() { this.data = [].concat(this.data); // Clone array } }
Performance measurements in chrome show that clicking on the checkbox in the first angulyar is processed less than 10 ms and in most cases does not result in long-frames (and if it does, it is not longer than 33 ms). But the change of the checkbox in the second version consistently causes a long frame with a duration of about 150 ms. How to optimize the code?
onChanges
method does for youonChanges
it is called for every click, but it runs in a cycle, even in two - Grundythis.data = [].concat(this.data); // Clone array
this.data = [].concat(this.data); // Clone array
. Anyway, can you fix it? - Qwertiy ♦