I wanted to update my model using $filter or some other smart way without having to do multiple for each.
So basically I have my model similar to below:
$scope.projects = [
{
tasks: [
{
name: 'task name',
visible: true,
starred: true
}
],
createdAt: 'something'
},
{
tasks: [
{
name: 'second task name',
visible: true,
starred: false
}
],
createdAt: 'something'
}
]
What I wanted to do is by using $filter or some other way like underscore and so on, to update the content of the variable. So for instance, when I click a button, I'd like to set visible = true only to tasks that are starred.
Anyone have a suggestion on how to achieve that? Is it possible or I would have to do a couple of loops?
Something like:
$filter('filter')($scope.projects, {{starred = true}}).tasks.visible = true
UPDATE
With the help from #jbrown I was able to achieve what I wanted.
Just in case someone needs similar approach, the final solution was as written below:
_.forEach($scope.projectsModel.projects, function(proj){
_.forEach(_.filter(proj.tasks, {starred: true}), function(task){
task.visible = true;
});
});
Using underscore you can use a combination of filter and find to get the results you are looking for.
$scope.filteredProjects = _.filter($scope.projects, function(proj) {
return _.find(proj.tasks, {
visible: true, starred: true });
});
Related
Im new at this sencha thingy and im trying to experiment a bit with it. I've been making some very simple tests and now i've reached the point where i want to pass the data from one file to another. To make it easier to understand im trying to get the data from the following text box to make a simple filter tool.
the textfield has been created in the following piece of code in the file Filters.js
Ext.define('Prj01Filtro.view.main.Filters', {
extend:'Ext.form.Panel',
xtype: 'Filters',
alias: 'form.formFilter',
requires: [
'Prj01Filtros.view.main.FilterController'
],
controller: 'controllerFilter',
items:[
{
margin: '0 0 10 0',
buttons: [
{
xtype: 'textfield',
fieldLabel: 'Filter',
name: 'textFieldSearch'
}, {
name: 'buttonSearch',
text: 'Buscar',
listeners: {
click: 'onClickFilter'
}
}, {
name: 'buttonRemoveFilter',
text: 'X',
listeners: {
click: 'onClickRemove'
}
}
]
}
]
The code of the buttons have been located in a file named FilterController.js
Ext.define('Prj01Filtros.view.main.FilterController',{
extend: 'Ext.app.ViewController',
alias: 'controller.controllerFilter',
onClickFilter: function() {
//Code to apply the filter
},
onClickRemove: function() {
//code to remove the filter
}
})
Finally the code of the table is located in a file named List.js
Ext.define('Prj01Filtros.view.main.List', {
extend: 'Ext.grid.Panel',
xtype: 'mainlist',
plugins: 'gridfilters',
requires: [
'Prj01Filtros.store.Personnel'
],
title: 'Personnel',
store: {
type: 'personnel'
},
columns: [
{ text: 'Name', dataIndex: 'name', align: 'left', filter: {
type: 'string'
}},
{ text: 'Email', dataIndex: 'email', flex: 1, align: 'left', filter: {
type: 'string'
}},
{ text: 'Phone', dataIndex: 'phone', flex: 1, align: 'left', filter: {
type: 'string'
}},
],
listeners: {
select: 'onItemSelected'
},
});
So my goal is to make a function in FilterController.js that can be called from Filters.js which sets the value for the filters applied in the columns of List.js but im kind of stuck on how to set the value property for the filter from the function that i have created. If someone knows how to do it i would appreciate the help. Thanks!
I've tried many things but since im new to sencha im pretty much sure that they were incorrect anyways.
I suggest to study View Models & Data Binding and ViewModel Internals sections of Ext JS documentation. These are among the most powerful features of Ext JS so it good to develop a deep understanding.
For you current question, you need to access the store behind your List view to manage the filters, not the list itself. Once you get the store, you can set / clear the filters with setFilters and clearFilter methods on the store:
store.setFilter([{
property: 'email',
value: '<search term here>',
operator: 'like'
}]);
store.clearFilter();
To easily access the store, define the store in the view model of a view that is above both List and Filters view. This is the important part because this way you can take advantage of inheriting the view model from the container parent. Let's say you have a panel which contains both List and Filters. Define the store in the view model:
stores: {
personnel: {
type: 'personnel'
}
}
Use bind config when assigning the store in your List:
Ext.define('Prj01Filtros.view.main.List', {
...
bind: {
store: '{personnel}'
}
...
});
After all this, you can access the store from the Filters view controller's methods:
onClickFilter: function() {
this.getViewModel().getStore('personnel').setFilters(...);
}
You need the get the value the user entered into the field. You can access it from the controller with querying the textfield component, but you can also do it with the view model.
I am trying to change the value of an item in array, based on matching other items in the array. The array might contain details of the section (non-unique), a unique ID, and a value I wish to change (in this case a 'selected' flag). The goal is to be able to have multiple items which can have their selected flag. Within any single section, only one item could be 'selected' but multiple sections could have an individual item 'selected'. Conceptually, I think this could be thought of in the same way as having multiple groups of radio buttons.
The ultimate aim is to be able to use state to remember the selections made in a component that is created using props. I'm keen to understand not simply copy. I'll get my head around state mutations next, but better to solve this problem first.
So, take an array like:
menuItems: [
{
section: 'National',
id: 'First item',
selected: false
},
{
section: 'National',
id: 'Second item',
selected: false
},
{
section: 'National',
id: 'Third item',
selected: true
},
{
section: 'Local',
id: 'Fourth item',
selected: false
},
{
section: 'Local',
id: 'Fifth item',
selected: false
},
{
section: 'Local',
id: 'Sixth item',
selected: true
}
]
And some search strings like:
searchSection: 'National',
searchId: 'First item'
How would I create a function that could change the selected flag of the item with id: First item to true, the others (second, third item) to false, and don't change anything in the 'Local' section?
I have tried to get my head around using forEach loops to no avail, even though this feels the right approach. Using findIndex for the section seems destined to fail as there are multiple items to be found.
First SO question - so pre-emptive apologies if problems with the way I have asked. I'm using Vue3. All advice appreciated.
Loop through the items testing for the proper section. With the section, if there is an id match, set selected to true, otherwise set selected to false:
methods: {
flag(searchSection, searchId) {
this.menuItems.forEach(item => {
if (item.section === searchSection) {
item.selected = item.id === searchId;
}
});
}
}
Call the function:
this.flag('National', 'First item');
I have a simple UI grid with these options:
$scope.transactionGrid = {
enableSorting : true,
enableColumnResize : true,
enableScrollbars : true,
enablePaginationControls : false,
minRowsToShow : 6,
onRegisterApi : function(gridApi) {
$scope.gridEventsApi = gridApi;
}
};
I want to hide rows which have a specific value, deleted: "y".
$scope.transactionGrid.data = [
{ Name: "First", deleted: "y" },
{ Name: "Second", deleted: "y" },
{ Name: "Third", deleted: "n" },
{ Name: "Fourth", deleted: "n" }
];
Without actually changing the data, can it be filtered out from rows?
One way is to adjust the row-repeater-template to check for some row-specific value and make the row show/hide that way.
I created a Plunkr showcasing a possible solution.
First you need to create your row-value-checker-function:
appScopeProvider: {
showRow: function(row) {
return row.deleted !== 'y';
}
},
Then you adjust their template by adding that check to their row-repeater
$templateCache.put('ui-grid/uiGridViewport',
...
ng-if=\"grid.appScope.showRow(row.entity)\"
...
}
I know you specifically said "without actually changing the data", but assigning a filtered dataset to the grid would not change the dataset, just the data for the grid. Also it might be a relevant and valid solution for other cases like this.
I forked CMR's Plunk to demonstrate this: http://plnkr.co/edit/NntwWb?p=preview
The key part is just adding a filter when assigning the dataset:
$scope.gridOptions = {
data: $scope.myData.filter(function(row) {
return row.deleted !== "y";
})
};
You can hide it by creating cell templates and hide it based on row value for every field:
$scope.transactionGrid = {
enableSorting : true,
enableColumnResize : true,
enableScrollbars : true,
enablePaginationControls : false,
minRowsToShow : 6,
onRegisterApi : function(gridApi) {
$scope.gridEventsApi = gridApi;
}
// Column template definitions:
columnDefs: [
{
name: 'Name',
displayName: 'Name',
cellTemplate : '<div ng-if="row.entity.deleted">{{row.entity.Name}}</div>'
}
]
};
I made a Plunk to demonstrate a viable technique to solve this: https://plnkr.co/edit/XQRC45vaiZCySZYkEGrz?p=preview
I've got this class:
Ext.define('Customer_Portal_UI.FormParameters', {
constructor: function (c) {
Ext.each(this.ParticipantFormParameters, function (field) {
if (field.toFieldSet != null) {
var fieldSetName = c.targetFieldSetPrefix + field.toFieldSet;
field.toFieldSet = fieldSetName;
}
});
},
ParticipantFormParameters: [
{ name: 'service_executive_account_Number', visible: false },
{ name: 'firstname', visible: true, displayText: 'Firstname', toFieldSet: 'GeneralInfoFS', allowBlank: false },
{ name: 'lastname', visible: true, displayText: 'Lastname', toFieldSet: 'GeneralInfoFS', allowBlank: false },
{ name: 'gendercode', visible: true, hasCheckbox: true, displayText: 'Gender', toFieldSet: 'GeneralInfoFS', xtype: 'combo', allowBlank: false },
]
});
If I do this once:
var formParams = Ext.create(Customer_Portal_UI.FormParameters, { targetFieldSetPrefix: 'add' });
console.log(formParams.ParticipantFormParameters[0].toFieldSet);
I will get addGeneralInfoFS.
But if I create this object many times, I'll get addGeneralInfoFS, addaddGeneralInfoFS, addaddaddGeneralInfoFS
What is going on here ? I tried delete formParams but when I initialize an object if the same type again, same problem.
Is it ExtJS or a javascript problem ? I find it weird I need to put effort into simply destroying objects...
EDIT: Ah! I found this in the documentation:
When placing items in the prototype of the prototype, they will be
shared across all instances of the class. Unless you specifically want
to do this, you should only put "primitive" types inside the prototype
definition.
The question now is how to I declare members without them being "in the prototype" ? That's nice, but where does ParticipantFormParameters go exactly then ? I don't want it in the config cause it's dirty, I'll have to drag this huge (the real code has much more items in the array) block of javascript markup everytime I initialize the object ?
I've refactored things in my application with this approach and I'm completely paralized until this works...I cannot unrefactor...
Thank you.
Why do you modify the property ? Use two different variables. This could be the solution :
(...)
Ext.each(this.ParticipantFormParameters, function (field) {
if (field.toFieldSet != null) {
var fieldSetName = c.targetFieldSetPrefix + field.toFieldSetRaw;
field.toFieldSet = fieldSetName;
}
});
},
ParticipantFormParameters: [
{ name: 'service_executive_account_Number', visible: false },
{ name: 'firstname', visible: true, displayText: 'Firstname', toFieldSetRaw: 'GeneralInfoFS', allowBlank: false },
Simple question here, and I'm really surprised I cannot find an answer to it anywhere.
I have the following product model:
Ext.define('Product', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'manufacturer', type: 'auto'}, // i.e. nested object literal
],
});
As can be seen, a product has a nested object inside it that contains details about a manufacturer (id, name, description, etc.).
I display products in an Ext.grid.Panel, like so:
Ext.create('Ext.grid.Panel', {
title: 'Products',
...
remoteSort: true,
columns: [
{text: 'Id', dataIndex: 'id', sortable: true},
{text: 'Name', dataIndex: 'name', sortable: true},
{text: 'Manufacturer', dataIndex: 'manufacturer', sortable: true, renderer: function(manufacturer) {
return manufacturer.name; // render manufacturer name
}
},
],
});
As you can see, all three columns are configured to be sortable, and I am using remote sorting. This works for the first two columns, but the third column (manufacturer) is giving me trouble.
For instance, when a user sorts the grid by product name, Ext sends the following JSON to the server:
{ sort: [{ property: 'name', direction: 'ASC' }] }
This is fine, because the server knows to simply sort by each product's name property.
But when a user sorts the grid by manufacturer, the JSON sent is:
{ sort: [{ property: 'manufacturer', direction: 'ASC' }] }
Since the manufacturer is an object, this does not give the server any idea which property of the manufacturer it should sort by! Is it the manufacturer's id? Or is it the manufacturer's name? Or something else?
For my grid, since I render the manufacturer's name, I'd like to sort it by name. But I see no way to tell the server this. And I certainly don't want to make my server just sort by the manufacturer's name all the time.
If the JSON was sent like this it would be ideal:
{ sort: [{ property: 'manufacturer.name', direction: 'ASC' }] }
Sadly, Ext does not seem to do that (?). So, what's the solution?
Okay, I asked on the Sencha Forums and got a response. It appears you can override getSortParam() in the column config. Example:
columns: [
...
{
header: 'Manufacturer',
dataIndex: 'manufacturer',
sortable: true,
renderer: function(manufacturer) {
return manufacturer.name; // render manufacturer name
},
getSortParam: function() {
return 'manufacturer.name';
},
}
...
]
This will send the ideal JSON I described in my OP. Now I just need to make my server parse this properly! :)