Eric Eric - 4 months ago 46
AngularJS Question

Isolating events in Angular 1.5 Component Instances

I've created a component which is being used to add "data grid" functionality to HTML tables. The headers are clickable to allow sorting of the data (ascending/descending) on that column. So far it's working great unless I have two instances of the component on the same page. When I click a header in one table, it affects both tables.

Is there a way I'm missing to isolate the component's events to only affect that instance?

Component:

angular.module('app')
.component('datagrid', {
templateUrl:'components/datagrids/datagrids.component.html',
controller:DatagridController,
})


Controller (Work in progress, I know It's a bit of a mess at the moment!):

function DatagridController($filter, datagridService){
var ctrl = this;
ctrl.today = new Date();
ctrl.sortBy = null;
ctrl.fields = [];
ctrl.data = [];
ctrl.update = function(){
var service = datagridService;
console.log(datagridService);
var updatedFields = [];
console.log(datagridService.fields);
for(var i = 0; i < datagridService.fields.length; i++){
var fieldName = datagridService.fields[i];
var fieldDirection = (ctrl.fields.length === 0) ? 'ascending' : ctrl.fields[i].direction;
updatedFields.push({name:fieldName, direction:fieldDirection});
}
ctrl.fields = updatedFields;
console.log(ctrl.fields)
if (ctrl.sortBy == null){ ctrl.sortBy = $filter('toCamelCase')(ctrl.fields[0].name); }
ctrl.data = datagridService.data.sort(ctrl.sortData(ctrl.sortBy));
ctrl.today = new Date();
};
ctrl.sortData = function(field, reverse, primer){
console.log(field + ' | ' + reverse)
var key = primer ?
function(x) {return primer(x[field])} :
function(x) {return x[field]};
reverse = !reverse ? 1 : -1;
ctrl.sortBy = field;
return function (a, b) {
return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
}
};
ctrl.toggleSortDirection = function(index){
console.log(index);
var field = ctrl.fields[index];
console.log(field);
var fieldName = field.name;
console.log(fieldName);
var direction = ctrl.fields[index].direction;
console.log(direction);
var reverse = (direction == 'ascending') ? true : false;
console.log(reverse);
var direction = (direction === 'ascending') ? 'descending' : 'ascending';
console.log(direction);
for(var i = 0; i < ctrl.fields.length; i++){
ctrl.fields[i].direction = 'ascending';
}
ctrl.fields[index].direction = direction;
ctrl.data.sort(ctrl.sortData($filter('toCamelCase')(fieldName), reverse));
};
ctrl.validDatetime = function(dt){
//this should probably be a service
console.log(dt);
var rx = /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/;
if(dt.match(rx)){ console.log(dt); }
return (dt.match(rx)) ? true : false;
};
ctrl.$onInit = ctrl.update();
}
DatagridController.$inject = ['$filter', 'datagridService'];


Template:

<table ng-if="$ctrl.data.length > 0" class="datagrid">
<caption ng-if="$ctrl.caption">{{ $ctrl.caption }}</caption>
<colgroup ng-if="$ctrl.colgroup.length > 0">
<col ng-repeat="col in $ctrl.colgroup">
</colgroup>
<thead ng-if="$ctrl.hasHeader = true">
<tr>
<th ng-repeat="field in $ctrl.fields" ng-click="$ctrl.toggleSortDirection($index)" data-sortable="true">{{ field.name }}<div ng-class="field.direction"></div></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in $ctrl.data">
<td ng-repeat="field in $ctrl.fields">
<span ng-if="!$ctrl.validDatetime(record[(field.name|toCamelCase)])"><a>{{ record[(field.name|toCamelCase)] }}</a></span>
<span ng-if="$ctrl.validDatetime(record[(field.name|toCamelCase)])"><a>{{ record[(field.name|toCamelCase)] | date: 'dd-MMM-yyyy' }}</a></span>
</td>
</tr>
</tbody>
<tfoot ng-if="$ctrl.hasFooter = true">
<td colspan="{{ $ctrl.fields.length }}">Last Updated: {{ $ctrl.today | date: 'dd-MMM-yyyy' }}</td>
</tfoot>
</table>


Component Tag:

<datagrid></datagrid>

Answer

Components are isolated by default, which means there is its own $ctr for every instance.

Thing is that data is shared through service. For example you do datagridService.data.sort in first instance => it changes data in service => it gets reflected in all instances of your component (there is one data object in memory, that you are trying to access).

One fix might be, to make copies of data for every component instance.

ctrl.data = Object.assign([], datagridService.data);

Dont do any manipulation directly on datagridService.data, but use ctrl.data instead

Comments