I have a mover directive with the following html for the list
<select class="select-list" multiple
ng-model="unassigned"
name="unAssignedList"
data-no-dirty-check
ng-options="unassignedItem.descrip for unassignedItem in unassignedItems | orderBy:'descrip' | filter: filterCriteria"></select>
So, when I use this directive I can specify my filter-criteria like this
<data-sm:duallist-directive ng-required="false" keep-pristine="true"
unassigned-items-title="'#String.Format(Labels.availableX, Labels.items)'"
unassigned-items="currentItemGroup.unassignedItems"
assigned-items-title="'#String.Format(Labels.assignedX, Labels.items)'"
assigned-items="currentItemGroup.assignedItems"
sortable="false"
filter-criteria="{categoryId:selectedCategoryId}"
selected-item="currentItemGroup.selectedItem">
</data-sm:duallist-directive>
The problem is with the MoveAllLeft (or MoveAllRight) buttons. They have the following code:
$scope.moveRightAll = function() {
var unassignedItems = $scope.unassignedItems.slice(0);
var smItems = $scope.unassignedItems.slice(0);
angular.forEach(smItems, function (value, key) {
$scope.assignedItems.push(value);
removeItem(unassignedItems, value);
});
$scope.unassignedItems = unassignedItems;
if (!$scope.keepPristine)
$scope.form.$setDirty();
$scope.assigned = null;
};
The problem is that it works against original unfiltered array. Say, if I have 642 items in total and I filtered them by category to only, say, 5, I only want to move these 5 items when I press my button, not all 642 which I don't even see on the screen.
How can I modify my code to get only items which are filtered? Also, I don't have to enter filter-criteria, so it should work correctly when nothing is entered in the filter-criteria.
I solved the problem - it turned out to be very easy. I added the following code at the top
var filteredData ;
if ($scope.filterCriteria)
filteredData = $filter('filter')($scope.unassignedItems, $scope.filterCriteria);
else
filteredData = $scope.unassignedItems;
var unassignedItems = filteredData.slice(0);
var smItems = filteredData.slice(0);
and now only filtered items are moved.
Related
I am working on an Earth Engine application where I have 2 widgets of type ui.Select that are dependent on each other (bidirectionally dependent). That is, when I make a change in one of them, it changes the list in the other and vice versa.
The behavor is correct, except that when I want to reselect the option that was before the change, it does not work. Due to the items generator script that populate the widgets is allways sure that the previous selection is in the new (modified) items list.
Here are the two widgets implementations
var select_date_init = ui.Select({
items: Object.keys(dates_init),
onChange:function(key) {
var selected_end = select_date_end.getValue();
var selected_init_end_date = dates_init[key][1];
var new_list = Object.keys(get_dates_list(selected_init_end_date,''));
select_date_end.items().reset(new_list);
//select_date_end.items = new_list
if(selected_end !== null){
select_date_end.setValue(selected_end,false)
}else{
select_date_end.setValue(null,false)
}
},
placeholder: 'Seleccionar semestre 1...'
});
var select_date_end = ui.Select({
items: Object.keys(dates_end),
onChange: function(key) {
var selected_init = select_date_init.getValue();
var selected_end_init_date = dates_init[key][0];
var new_list = Object.keys(get_dates_list(system_date_init,selected_end_init_date));
select_date_init.items().reset(new_list);
//select_date_init.items = new_list;
if(selected_init !== null){
select_date_init.setValue(selected_init,false)
}else{
select_date_init.setValue(null,false)
}
},
placeholder: 'Seleccionar semestre 2...'
});
The problem seems to be on the setValue() part of the script. The items of the two widgets are correctly changed, but it allways show the 'Seleccionar semestre 1...' or 'Seleccionar semestre 2...' default text after the change action.
The action I need is:
First step:
Select from 1 -> this changes the list on 2 (originally empty so no pre-selection needed)
Second step:
Select from 2 -> this changes the list on 1 (but keep the selected option in 1)
If the user wants:
Change selection from 1 (or 2) -> update the other list -> keep pre-selected options
Here is the link to the full working script https://code.earthengine.google.com/b251b24ffbc37775e4e5eede8dcbd11f
The onChange function of your button #2 is resetting your button #1 every time. You only need to uncomment lines 209-216 of your code, so the onChange function of your variable var select_date_end looks like this:
var select_date_end = ui.Select({
items: Object.keys(dates_end),
onChange: function(key) {
var selected_init = select_date_init.getValue();
var selected_end_init_date = dates_init[key][0];
var new_list = Object.keys(get_dates_list(system_date_init,selected_end_init_date));
},
placeholder: 'Seleccionar semestre 2...'
});
See code: https://code.earthengine.google.com/9e2d4f13e9bfa57e1df3673a6d82684e
https://stackblitz.com/edit/timeline-angular-7-wbff3f
The above stackblitz will demo a few pipe filters. If you click on locations of "North, South or East", it will populate the *ngFor loop - these filters can be selected on/off and use a "multifilter pipe". By default I want all these filter buttons "active" or "on" to show the complete populated list to begin with, allowing you to then click the filters off. Otherwise, the list if blank on first load!
.html file
<button [class.active]="entry.isLocationActive" (click)="toggle(entry.location); entry.isLocationActive = !entry.isLocationActive" class="btn btn-primary" type="button" *ngFor="let entry of timeLine | filterUnique">{{entry.location}}</button>
<my-timeline-entry *ngFor="let entry of timeLine | filter:filteredYear:'year'| multifilter:filteredLocations:'location' " timeEntryHeader={{entry.year}} timeEntryContent={{entry.detail}} timeEntryPlace={{entry.place}} timeEntryLocation={{entry.location}}></my-timeline-entry>
.ts file
filteredLocations: string[] = [];
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter((i) => i !== location);
} else {
this.filteredLocations.push(location);
}
}
I've played around with pre-populating the filteredLocations object with all the values, so it starts off with them all therefore active.
I managed to get the 'active' class to be active on first load but that did not carry through to the pipe filter. I'm sure its not a big change just can't see it, any help would be appreciated greatly!
filteredLocations = this.timeLine.map(a => a.location);
Adding this to the .ts file and replacing the empty filteredLocations: string[] = [];
I wasn't far off initially in the end. Hope it helps others.
I have a Master-Detail ag-grid. One column has checkboxes, (checkboxSelection: true). The details grid have a custom status panel with a button. When the user clicks the button in any specific Detail grid, I don't know how to get the SelectedRows from just that one specific detail grid.
The problem is they might leave multiple details displayed/open, and then looping over each Detail Grid will include results from all open grids. I'm trying to isolate to just the grid where the user clicked the button.
I tried looping through all displayed/open detail grids to get the Detail grid ID. But I don't see any info in this that shows me which one they clicked the button in.
I tried in the button component to see if, in the params, there is anything referencing the detailgrid ID that the button is in, but I did not see anything there either.
This is the button component:
function ClickableStatusBarComponent() {}
ClickableStatusBarComponent.prototype.init = function(params)
{
this.params = params;
this.eGui = document.createElement('div');
this.eGui.className = 'ag-name-value';
this.eButton = document.createElement('button');
this.buttonListener = this.onButtonClicked.bind(this);
this.eButton.addEventListener("click", this.buttonListener);
this.eButton.innerHTML = 'Cancel Selected Records <em class="fas fa-check" aria-hidden="true"></em>';
console.log(this.params);
this.eGui.appendChild(this.eButton);
};
ClickableStatusBarComponent.prototype.getGui = function()
{
return this.eGui;
};
ClickableStatusBarComponent.prototype.destroy = function()
{
this.eButton.removeEventListener("click", this.buttonListener);
};
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows();
};
Here is the code to loop through and find all open detail grids:
function getSelectedRows()
{
this.gridOptions.api.forEachDetailGridInfo(function(detailGridApi) {
console.log(detailGridApi.id);
});
I was able to work this out, so thought I'd post my answer in case others have the same issue. I'm not sure I took the best approach, but it's seemingly working as I need.
First, I also tried using a custom detail cell renderer, as per the documentation, but ultimately had the same issue. I was able to retrieve the DetailGridID in the detail onGridReady function--but couldn't figure out how to use that variable elsewhere.
So I went back to the code posted above, and when the button was clicked, I do a jquery .closest to find the nearest div with a row-id attribute (which represents the the DetailgridID), then I use that specific ID to get the rows selected in just that detail grid.
Updated button click code:
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows(this);
};
Updated getSelectedRow function:
function getSelectedRows(clickedBtn)
{
var detailGridID = $(clickedBtn.eButton).closest('div[row-id]').attr('row-id');
var detailGridInfo = gridOptions.api.getDetailGridInfo(detailGridID);
const selectedNodes = detailGridInfo.api.getSelectedNodes()
const selectedData = selectedNodes.map( function(node) { return node.data })
const selectedDataStringPresentation = selectedData.map( function(node) {return node.UniqueID}).join(', ')
console.log(selectedDataStringPresentation);
}
I am trying to build an SAPUI5 application using TreeTable and I'm facing some problems to use its methods.
In my app, I have a button which triggers this method.
onChangeViewContext: function(oEvent) {
.........
.........
var aViewContext = oContext.oModel.getProperty(sPath + "/ViewContext");
var aDataModel = oContext.oModel.getProperty("/ApplicationCollection/" + sAppId + "/DataModel");
var oStructure = this._createParentChildStructure(aDataModel);
var oTreeModel = this.getView().getModel("treeModel");
oTreeModel.setData(oStructure);
this._oViewDetailLine = oSource.getParent().getParent().getParent();
this._oViewDetailLine.setVisible(false);
this.byId("idSelectElementsPanel").setVisible(true);
this._setSelectedItems(aViewContext, oTree);
}
What I'm trying to do here is only bind the rows with my treeModel, get tree table object and send it to my _setSelectedItems method which below.
_setSelectedItems: function(aViewContext, oTree) {
oTree.clearSelection();
var sElementName;
var aSelectedIndices = [];
var aElements = [];
var aRows = oTree.getRows();
aRows.forEach(function(row) {
if (row._oNodeState !== undefined) {
aElements.push(row.getCells()[0].getText());
}
});
I need to get rows array here because I will use it for setting selected items of tree table. The problem is when "onChangeViewContext" triggered, oTable.getRows() returns an empty array. But when I click cancel button (which just hides my tree table, nothing more) and then trigger "onChangeViewContext" function again, I can get the rows array completely.
Even on the first call when I try to get table's model, I can get the treeModel and its data correctly.
I've tried to refresh bindings, aggregations etc. But no luck.
By the way, I'm using row binding in my xml view like this :
<t:TreeTable id="idSelectElementsTree" rows="{path: 'treeModel>/'}" selectionMode="MultiToggle" enableSelectAll="false"
rowSelectionChange="onSelectElement">
I'm really drowning here so any any help would be appreciated.
Edit : rest of the setSelectedIndexes function :
aViewContext.forEach(function(name) {
sElementName = name;
if (aElements.indexOf(sElementName) !== -1) {
aSelectedIndices.push(aElements.indexOf(sElementName));
}
});
aSelectedIndices.forEach(function(idx) {
if (oTree.getRows()[idx]._bHasChildren) {
oTree.expand(idx);
}
oTree.addSelectionInterval(idx, idx);
});
What could help here is to add an event rowsUpdated="onRowsUpdated" to the table in the XML view. This event is triggered after the table has been loaded and will hence provide you with the data via;
this.getView().byId("sTableId").getRows();
The difference to your approach is that the event would not be triggered by the press of a button but automatically, as the table is rendered. You can then also use this function to trigger another one as per your use case.
list.js Question:
How do you create a div so when you click it, it shows the last page of the pagination, after I have done a filter.
I have a list
var List = new List('list', {
valueNames: ['name'],
page: 5,
plugins: [ListPagination({})]
});
And say this list has 20 pages.
After I apply a filter:
List.filter(function(item) {
if (item.values().category.toLowerCase().indexOf('wordtofilter') > -1) {
return true;
} else {
return false;
}
});
It now has 5 pages. I want to have a button when I click it will take me to the last page.
Currently I can get to the last page of an unfiltered list using this:
$('.go-to-last-page').on('click', function(){
List.show(List.size(), 5);
});
But If i filter my list, and click it, it will attempt to take me to page 20, instead of 5. How do I make it so it takes me to the last page of the filtered list? (page 5)
I had this same problem a few years after your question, so I'm sharing my solution in case it might help someone else:
First I create the first and last page buttons:
<nav>
<button id="btn-first">FIRST</button>
<ul class="pagination"></ul>
<button id="btn-last">LAST</button>
</nav>
Secondly, in js I assign the data attributes that the paging buttons generated by list.js have by default.
const LIST_PAGINATION = 10;
var btn_first = document.getElementById('btn-first');
var btn_last = document.getElementById('btn-last');
btn_first.addEventListener("click",function(e){
btn_first.dataset.i = 1;
btn_first.dataset.page = LIST_PAGINATION;
},false);
btn_last.addEventListener("click",function(e){
let total = list.matchingItems.length; // list.js object in my case I called it "list"
let page = Math.ceil(total / LIST_PAGINATION);
btn_last.dataset.i = page;
btn_last.dataset.page = LIST_PAGINATION;
},false);