I am very new to angular and, I am not sure how to control the behavior of my filters.
In the app, I have two different single-select drop down controls that filter the results of my data set and fill a table. However, even though these filters work, the results are dependent of both controls and if both are not being used , the empty set is returned. So, my question is: How can I use these filters optionally? So, the app returns every result when the filters are not used or returns the filtered results by one of the controls or both?
Thank you
Here is the code:
AngularJS
The filters for each control. They look very similar:
.filter('byField', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
for (var i in this.options) {
if ((options[i].value === value.fieldId &&
options[i].name === "Field" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
}, items);
return items.out;
};
})
.filter('byClass', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
for (var i in this.options) {
if ((options[i].value === value.documentClass &&
options[i].name === "Class" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
}, items);
return items.out;
};
})
HTML
This is what I am doing to populate the rows of the table:
<tr ng-repeat="result in results | byField:outputFields | byClass:outputClasses">
<td>{{result.documentId}}</td>
...
</tr>
Dorado7.1 in all event listeners provides a view implicit variable pointing to the current event host's view, the variable can completely replace the use of this scenario.
Well, as I imagined the answer was more related to set theory than to angular.
I just made an union between the empty set and every result, and it worked.
.filter('byField', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
if (options.length) {
for (var i in this.options) {
if ((options[i].value === value.fieldId &&
options[i].name === "Field" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
} else {
this.out = results.slice();
}
}, items);
return items.out;
};
})
Related
I have simple function to set active state for nav based on url:
angular.forEach(this.$filter('filter')(this.fullNav, {
'link': this.$location.$$path
}, true), (item) => {
item.active = true;
});
Now I want to add legacy URLs to be also highlighted. Instead of link I have links (if there are more then one, but I can make all links parameters to be arrays.
To have it work with links and link paramaters I made change to this:
angular.forEach(this.fullNav, (item) => {
if(item.links) {
if(item.links.includes(this.$location.$$path)) {
item.active = true;
}
} else {
if(item.link === this.$location.$$path) {
item.active = true;
}
}
});
How can I write the second function in $filter form? Or at least without if else statement (by removing link property and having only links property).
you can try following code which is similar to the code you have added.
angular.forEach(this.$filter('filter')(this.fullNav, (item) => {
return Array.isArray(item.links) && item.links.includes(this.$location.$$path);
}, true), (item) => {
item.active = true;
});
I am using the ag-grid for angular1, (and loving it), and I want my users to be able to reorgenize columns, change sortings, and everything, and that it will stay after a refresh.
It should not be very hard, except that the columns are circular (contains pointers to themselves), and thus I cannot parse them.
Code:
var columnDefsKey = "columnDefs["+$rootScope.page+"]";
var savedColumns = localStorage.getItem(columnDefsKey);
function saveColumnsState() {
var currentCol = vm.gridOptions.columnApi.getAllColumns();
if (!angular.equals(currentCol, savedColumns))
try {
localStorage.setItem(columnDefsKey, JSON.stringify(currentCol));
} catch (ex) {
log(ex);
log(currentCol);
}
}
And:
onColumnEverythingChanged: saveColumnsState,
onColumnVisible: saveColumnsState,
onColumnPinned: saveColumnsState,
onColumnResized: saveColumnsState,
onColumnRowGroupChanged: saveColumnsState,
onColumnValueChanged: saveColumnsState,
onColumnMoved: saveColumnsState,
onColumnGroupOpened: saveColumnsState,
It fails on the "try" every time:
TypeError: Converting circular structure to JSON(…) [Column, Column, Column, Column, Column, Column, Column, Column, Column, Column]
How can I do that? (save columns for later use)
If I manage to do that, I will be able to create several views without coding.
you can get the better understanding of the issue from below link
Chrome sendrequest error: TypeError: Converting circular structure to JSON
Also check below reference
https://github.com/isaacs/json-stringify-safe
The way to achieve this was to build my own column model, that I can save and parse again, and in which to save only necessary properties.
This method is XSS vulnerable, as I am evaluating functions, but it is a working solution.
columnsApi: {
key: null,
grid: null,
newColumnModel: {
headerName: "",
width: 200,
valueGetter: "",
filter: 'text',
aggFunc: 'none',
filterParams: {apply: true}
},
setKey: function (key) {
this.key = key;
},
setGrid: function (grid) {
this.grid = grid;
},
format: function (columns) {
var format = [];
angular.forEach(columns, function (col) {
var colDef = {
width: col.actualWidth,
pinned: col.pinned,
hide: !col.visible
};
format.push(angular.extend(col.colDef, colDef));
});
return format;
},
getIDs: function (columns) {
var ids = [];
angular.forEach(columns, function (col) {
ids.push(col.colId);
});
return ids;
},
stringify: function (columns) {
return JSON.stringify(columns, function (key, value) {
if (typeof value === "function")
return "/Function(" + value.toString() + ")/";
return value;
});
},
parse: function (string) {
return JSON.parse(string, function (key, value) {
if (typeof value === "string" &&
value.startsWith("/Function(") &&
value.endsWith(")/")) {
value = value.substring(10, value.length - 2);
return eval("(" + value + ")");
}
return value;
});
},
add: function (column) {
if (this.grid === null) {
console.error("Assertion error: grid must not be null");
return;
}
if(column.aggFunc == 'none')
column.aggFunc = undefined;
var groups = this.get().groups;
var newColumns = this.format(getGridColumns(this.grid));
newColumns.push(column);
this.grid.api.setColumnDefs(newColumns);
this.setGroups(groups);
},
save: function () {
var self = this;
if (this.key === null) {
console.error("Assertion error: key must not be null");
return;
}
if (this.grid === null) {
console.error("Assertion error: grid must not be null");
return;
}
var savedOptions = {
columns: self.format(getGridColumns(self.grid)),
groups: self.getIDs(self.grid.columnApi.getRowGroupColumns()),
sorting: self.grid.api.getSortModel(),
filter: self.grid.api.getFilterModel()
};
localStorage.setItem(this.key, this.stringify(savedOptions));
},
// Get function uses "eval" - XSS vulnerable.
get: function () {
if (this.key === null) {
console.error("Assertion error: key must not be null");
return;
}
var options = localStorage.getItem(this.key);
if (options)
options = this.parse(options);
return options;
},
remove: function (field) {
if (this.grid === null) {
console.error("Assertion error: grid must not be null");
return;
}
var newColumns = this.format(getGridColumns(this.grid));
angular.forEach(newColumns, function (col, key) {
if (col.field == field)
newColumns.splice(key, 1);
});
this.grid.api.setColumnDefs(newColumns);
},
setGroups: function (groups) {
var self = this;
angular.forEach(groups, function (id) {
angular.forEach(getGridColumns(self.grid), function (col) {
if (col.colId == id)
self.grid.columnApi.addRowGroupColumn(col);
});
});
}
}
This solution was written for Ag-Grid 5 I believe, and thus I am not sure if it still holds.
I have the array of objects. when I require to filter the object by single vaue i am doing like this:
$scope.filteredByPhase = $filter('filter')($scope.allApps, {Phase:"All"});
$scope.allAppsBatch = $scope.filteredByPhase;
But as a option, I would like to filter the objects by 2 'Phase` values by "All" or "Home" in this case how to filter?
I tried like this:
$scope.filteredByPhase = $filter('filter')($scope.allApps, {Phase:("All" || "Home")});
$scope.allAppsBatch = $scope.filteredByPhase;
But not works.. any one guide me please?
In AngularJS, you can use a function as an expression in the filter. In the function you can validate the condition and return Boolean value. All the falsy items are filtered out of the result. So you can do
$scope.filteredByPhase = $filter('filter')($scope.allApps, function (app) {
if (app.Phase == "All" || app.Phase == "Home") {
return true;
}
return false;
});
Read More : AngularJS Filter Documentation
Use $filter passing an anonymous comparison function.
$scope.filteredItems = $filter('filter')($scope.items, function (item) {
return (item.Phase == "all") ? true : false;
});
Keep in mind that you may use Array.filter as well:
$scope.items = [{
Phase: "home"
}, {
Phase: "all"
}, {
Phase: "all"
}, {
Phase: "home"
}];
console.log($scope.items);
$scope.filteredItems = $scope.items.filter(function (item) {
return (item.Phase == "all") ? true : false;
})
console.log($scope.filteredItems)
You may also trigger multiple filtering actions using chaining:
$scope.fi = $scope.i.filter(func1).filter(func2);
I have items which should have multiple (e.g. categories). Now I want to filter my items to these categories.
I think the task is not possible with the filter-directive without using a custom filter, right?
I came up with a solution, but it looks dirty and wrong to me:
$scope.filterList = function (item) {
var found = false;
var allFalse = true;
angular.forEach(item.attributes, function (value, key) {
if ($scope.activeAttributes[value.name] === true) {
found = true;
}
});
angular.forEach($scope.activeAttributes, function (value, key) {
if (value === true) {
allFalse = false;
}
});
$log.log("length: " + Object.keys($scope.activeAttributes).length);
if (found === true || Object.keys($scope.activeAttributes).length === 0 || allFalse === true) {
return true;
}
};
Demo JSFiddle of my code
I thought with Angular, that the code should be simple and most of the work should be done by Angular. What if I need to filter more attributes?
The ui-grid example on the official website ( http://ui-grid.info/docs/#/tutorial/209_grouping ) presents a grouping feature, which looks like this:
I would like to have the Grouping menu item, but not have the Aggregate ones (count, sum, max, min, avg) in the column menu and I couldn't find a way around removing them.
A solution I've tried was overriding the uiGridGroupingService, by providing a decorator for the groupingColumnBuilder, but the service is not resolved at all and I can't help but wonder if there is a simpler way of achieving this.
Is anyone aware of any solution for this problem?
It's set to true by default so you need to specify it in your columnDefs
groupingShowAggregationMenu: false
There is a suppress aggregation option! Set groupingShowAggregationMenu to false.
The decorator approach is probably the best approach in this case. There are no config option to remove this from the column menu.
PS: The decorator is only shown to remove the aggregate items.
Here is a working plnkr with the decorator approach.
http://plnkr.co/edit/nzBeqbmEVUwmZF0qgyd6?p=preview
app.config(function($provide){
$provide.decorator('uiGridGroupingService', function ($delegate,i18nService,gridUtil) {
$delegate.groupingColumnBuilder = function (colDef, col, gridOptions) {
if (colDef.enableGrouping === false){
return;
}
if ( typeof(col.grouping) === 'undefined' && typeof(colDef.grouping) !== 'undefined') {
col.grouping = angular.copy(colDef.grouping);
} else if (typeof(col.grouping) === 'undefined'){
col.grouping = {};
}
if (typeof(col.grouping) !== 'undefined' && typeof(col.grouping.groupPriority) !== undefined && col.grouping.groupPriority >= 0){
col.suppressRemoveSort = true;
}
col.groupingSuppressAggregationText = colDef.groupingSuppressAggregationText === true;
var groupColumn = {
name: 'ui.grid.grouping.group',
title: i18nService.get().grouping.group,
icon: 'ui-grid-icon-indent-right',
shown: function () {
return typeof(this.context.col.grouping) === 'undefined' ||
typeof(this.context.col.grouping.groupPriority) === 'undefined' ||
this.context.col.grouping.groupPriority < 0;
},
action: function () {
service.groupColumn( this.context.col.grid, this.context.col );
}
};
var ungroupColumn = {
name: 'ui.grid.grouping.ungroup',
title: i18nService.get().grouping.ungroup,
icon: 'ui-grid-icon-indent-left',
shown: function () {
return typeof(this.context.col.grouping) !== 'undefined' &&
typeof(this.context.col.grouping.groupPriority) !== 'undefined' &&
this.context.col.grouping.groupPriority >= 0;
},
action: function () {
service.ungroupColumn( this.context.col.grid, this.context.col );
}
};
if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.group')) {
col.menuItems.push(groupColumn);
}
if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.ungroup')) {
col.menuItems.push(ungroupColumn);
}
}
return $delegate;
})
});