I want to send my own operator in odata request and not use the aurelia slickgrid inbuilt "eq" operator.
This is my column definition
{
id: 'LockoutEndDateUtc', name: 'Status', field: 'LockoutEndDateUtc', minWidth: 85, maxWidth: 95,
type: FieldType.boolean,
sortable: true,
formatter: Formatters.multiple,
params: { formatters: [this.StatusFormatter, Formatters.checkmark] },
filterable: true,
filter: {
collection: [
{ value: 'le ' + (() => {const dt = new Date(); return dt.toISOString().split('.')[0] + "Z";})(), label: 'True' },
{ value: 'gt ' + (() => {const dt = new Date(); return dt.toISOString().split('.')[0] + "Z";})(), label: 'False' }
], //['', 'True', 'False'],
model: Filters.singleSelect,//multipleSelect//singleSelect,
}
}
This is the UI
This is how the request filter looks like..
$filter=(LockoutEndDateUtc%20eq%20le%202022-06-28T12%3A59%3A25Z)
If i remove %20eq from the above request, everything else works. So my question is how to i remove %20eq. Or how do i send my own gt, le in the request.
You can't really do that on a boolean filter (you could however do it on a date filter with operator) and I don't think I've added any ways to provide a custom filter search the way you want to do it, but since you're using OData, you have a bit more control and you could change the query string yourself. To be clear, it's not at all recommended to change the OData query string, it's a last solution trick and at your own risk, but for your use case it might be the only way to achieve what you want.
prepareGrid() {
this.gridOptions = {
// ...
backendServiceApi: {
service: new GridOdataService(),
process: (query) => this.getCustomerApiCall(query),
} as OdataServiceApi
};
}
}
getCustomerApiCall(query: string) {
let finalQuery = query;
// in your case, find the boolean value from the column and modify query
// your logic to modify the query string
// untested code, but it would probably look similar
if (query.includes('LockoutEndDateUtc%20eq%20true')) {
// calculate new date and replace boolean with new date
finalQuery = query.replace('LockoutEndDateUtc%20eq%20true', 'LockoutEndDateUtc%20le%202022-06-28T12%3A59%3A25Z');
}
return finalQuery;
}
Another possible solution but requires a bit more work.
If I'd be using a regular grid, without backend service and without access to the query string, I would probably add an external drop down outside of the grid and also add the date column and then control filters in the grid by using dynamic filtering. You can see a demo at Example 23, the principle is that you keep the column's real nature (date in your case) and filter it, if you want something like a "below today's date" then add an external way of filtering dynamically (a button or a drop down) and control the filter dynamically as shown below (from Example 23)
Related
I am having multiple nested where conditions and want to generate them without too much code duplication with typeORM.
The SQL where condition should be something like this:
WHERE "Table"."id" = $1
AND
"Table"."notAvailable" IS NULL
AND
(
"Table"."date" > $2
OR
(
"Table"."date" = $2
AND
"Table"."myId" > $3
)
)
AND
(
"Table"."created" = $2
OR
"Table"."updated" = $4
)
AND
(
"Table"."text" ilike '%search%'
OR
"Table"."name" ilike '%search%'
)
But with the FindConditions it seems not to be possible to make them nested and so I have to use all possible combinations of AND in an FindConditions array. And it isn't possible to split it to .where() and .andWhere() cause andWhere can't use an Object Literal.
Is there another possibility to achieve this query with typeORM without using Raw SQL?
When using the queryBuilder I would recommend using Brackets
as stated in the Typeorm doc: https://typeorm.io/#/select-query-builder/adding-where-expression
You could do something like:
createQueryBuilder("user")
.where("user.registered = :registered", { registered: true })
.andWhere(new Brackets(qb => {
qb.where("user.firstName = :firstName", { firstName: "Timber" })
.orWhere("user.lastName = :lastName", { lastName: "Saw" })
}))
that will result with:
SELECT ...
FROM users user
WHERE user.registered = true
AND (user.firstName = 'Timber' OR user.lastName = 'Saw')
I think you are mixing 2 ways of retrieving entities from TypeORM, find from the repository and the query builder. The FindConditions are used in the find function. The andWhere function is use by the query builder. When building more complex queries it is generally better/easier to use the query builder.
Query builder
When using the query build you got much more freedom to make sure the query is what you need it to be. With the where you are free to add any SQL as you please:
const desiredEntity = await connection
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.andWhere("user.date > :date OR (user.date = :date AND user.myId = :myId)",
{
date: specificCreatedAtDate,
myId: mysteryId,
})
.getOne();
Note that depending on your used database the actual SQL that you use here needs to be compatible. With that could also come a possible draw back of using this method. You will tie your project to a specific database. Make sure to read up about the aliases for tables you can set if you are using relations this would be handy.
Repository
You already saw that this is much less comfortable. This is because the find function or more specific the findOptions are using objects to build the where clause. This makes is harder to implement a proper interface to implement nested AND and OR clauses side by side. There for (I assume) they have chosen to split AND and OR clauses. This makes the interface much more declarative and means the you have to pull your OR clauses to the top:
const desiredEntity = await repository.find({
where: [{
id: id,
notAvailable: Not(IsNull()),
date: MoreThan(date)
},{
id: id,
notAvailable: Not(IsNull()),
date: date
myId: myId
}]
})
I cannot imagin looking a the size of the desired query that this code would be very performant.
Alternatively you could use the Raw find helper. This would require you to rewrite your clause per field, since you will only get access to the one alias at a time. You could guess the column names or aliases but this would be very poor practice and very unstable since you cannot directly control this easily.
if you want to nest andWhere statements if a condition is meet here is an example:
async getTasks(filterDto: GetTasksFilterDto, user: User): Promise<Task[]> {
const { status, search } = filterDto;
/* create a query using the query builder */
// task is what refer to the Task entity
const query = this.createQueryBuilder('task');
// only get the tasks that belong to the user
query.where('task.userId = :userId', { userId: user.id });
/* if status is defined then add a where clause to the query */
if (status) {
// :<variable-name> is a placeholder for the second object key value pair
query.andWhere('task.status = :status', { status });
}
/* if search is defined then add a where clause to the query */
if (search) {
query.andWhere(
/*
LIKE: find a similar match (doesn't have to be exact)
- https://www.w3schools.com/sql/sql_like.asp
Lower is a sql method
- https://www.w3schools.com/sql/func_sqlserver_lower.asp
* bug: search by pass where userId; fix: () whole addWhere statement
because andWhere stiches the where class together, add () to make andWhere with or and like into a single where statement
*/
'(LOWER(task.title) LIKE LOWER(:search) OR LOWER(task.description) LIKE LOWER(:search))',
// :search is like a param variable, and the search object is the key value pair. Both have to match
{ search: `%${search}%` },
);
}
/* execute the query
- getMany means that you are expecting an array of results
*/
let tasks;
try {
tasks = await query.getMany();
} catch (error) {
this.logger.error(
`Failed to get tasks for user "${
user.username
}", Filters: ${JSON.stringify(filterDto)}`,
error.stack,
);
throw new InternalServerErrorException();
}
return tasks;
}
I have a list of
{
date: specificCreatedAtDate,
userId: mysteryId
}
My solution is
.andWhere(
new Brackets((qb) => {
qb.where(
'userTable.date = :date0 AND userTable.type = :userId0',
{
date0: dates[0].date,
userId0: dates[0].type,
}
);
for (let i = 1; i < dates.length; i++) {
qb.orWhere(
`userTable.date = :date${i} AND userTable.userId = :userId${i}`,
{
[`date${i}`]: dates[i].date,
[`userId${i}`]: dates[i].userId,
}
);
}
})
)
That will produce something similar
const userEntity = await repository.find({
where: [{
userId: id0,
date: date0
},{
id: id1,
userId: date1
}
....
]
})
I am trying to fully localize the alloyui scheduler in French.
Following this article: How can I get a localized version of a YUI 3 or AlloyUI component? the job is almost done.
However I am still missing tips for two things:
- I need the time format in the left column to be changed from 1-12am/pm to 1-24
- I don't succeed to localize the "All day" term in the left top corner (or at least a way to hide it).
Any help will be welcome
To change to a 24 hour clock, you need to set the isoTime attribute to true for each SchedulerView subclass that you are using.
To internationalize the strings, you need to set the strings attribute of Scheduler, SchedulerDayView SchedulerWeekView, SchedulerMonthView, SchedulerAgendaView, and SchedulerEventRecorder as well as setting YUI's lang attribute to the locale of your choice. For example, I've used Google Translate* to internationalize the Scheduler below for Spanish users:
YUI({lang: 'es-ES'}).use('aui-scheduler', function (Y) {
var es_ES_strings_allDay = { allDay: 'todo el dia' };
new Y.Scheduler({
render: true,
// https://alloyui.com/api/classes/A.Scheduler.html#attr_strings
// https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-base.js#L606-L622
strings: {
agenda: 'agenda',
day: 'día',
month: 'mes',
today: 'hoy',
week: 'semana',
year: 'año'
},
views: [
// https://alloyui.com/api/classes/A.SchedulerDayView.html#attr_strings
// https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-view-day.js#L363-L373
new Y.SchedulerDayView({
isoTime: true,
strings: es_ES_strings_allDay
}),
// https://alloyui.com/api/classes/A.SchedulerWeekView.html#attr_strings
// SchedulerWeekView extends SchedulerDayView: https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-view-week.js#L19
new Y.SchedulerWeekView({
isoTime: true,
strings: es_ES_strings_allDay
}),
// https://alloyui.com/api/classes/A.SchedulerMonthView.html#attr_strings
// https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-view-week.js#L19
new Y.SchedulerMonthView({
isoTime: true,
strings: {
showMore: 'mostrar {0} más',
close: 'cerrar'
}
}),
// https://alloyui.com/api/classes/A.SchedulerAgendaView.html#attr_strings
// https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-view-week.js#L19
new Y.SchedulerAgendaView({
isoTime: true,
strings: {
noEvents: 'No hay eventos futuros'
}
})
],
// https://alloyui.com/api/classes/A.SchedulerEventRecorder.html#attr_strings
// https://github.com/liferay/alloy-ui/blob/3.0.3-deprecated.65/src/aui-scheduler/js/aui-scheduler-view-week.js#L19
eventRecorder: new Y.SchedulerEventRecorder({
strings: {
'delete': 'borrar',
'description-hint': 'descripción insinuación',
cancel: 'cancelar',
description: 'descripción',
edit: 'editar',
save: 'salvar',
when: 'cuando'
}
})
});
});
* I don't recommend using Google Translate to internationalize a production application since there are many nuances to internationalization that a machine translation will miss.
I'm using jquery data tables and I'm assigning an array of values to the initialization of the data table. The table basically looks like this.
based on an an radio button I would like to limit the items that are display in the table and the items that are searched in the table.
For my example it would be based on the "Chart column". I want to limit the table to only show the items that are based on chart "D" or Chart "S". Here is how I'm initializing the table.
if (!$.fn.DataTable.isDataTable( '#fundLookUptbl' ) ) {
fundTable = $('#fundLookUptbl').DataTable( {
data: funds,
columns: [
{ "mData": "chart" },
{ "mData": "fund" },
{ "mData": "orgDefault" },
{ "mData": "progDefault" }
]
} );
var filteredData = fundTable
.columns( [0, 1] )
.data()
.eq( 0 )
.filter( function ( value, index ) {
return value = 'D' ? true : false;
} );
}
This is obviously not working, and the filterData variable is a lousy attempt on trying to make it work. I'm having a hard time understanding the API's. So the question is , How can initialize the table to only show the items that are based on a given chart. I know that I can remove the items of the array but i don't want to do that since I would simple like to be able to switch between chart "D" and "S" but still continue to search through the other columns.
I believe that filtering the column would solve your problem.
table.column(0).search('Bruno').draw()
So you could just filter the column when the radio button selection change
Here is a fiddle example
I´m not sure to be understanding what you want to do but here are some options:
One way is selecting by default value example "s". You can use a dropdown is easier to handled .Then select with jQuery the dafault value "s" on that dropdown and add a function
$("#DropdownId").change(function () {
var chart=$("#DropdownId").val();
});
$.ajax({
url: "url")",//url to reload page with new value
type: "POST",
data: {chart:chart},
success: function (data) {
}
});
});
on this way the filter is on backend. If you want to do something on the row depending of a column value you shoud to add something like this
"fnRowCallback": function (nRow, mData, iDisplayIndex, iDisplayIndexFull) {
if (mData["chart"] =="s") {
return nRow;
}
},
Datatables: custom function inside of fnRowCallback.
Good luck
fundTable.order( [0, 'asc'] );
Try that or look at this particular page for reference:
https://datatables.net/reference/api/order%28%29
Basically orders in pair of columnIndex in either asc(ending) or desc(ending) order.
I am working with a dgrid where I want to find a search term in my grid on two columns.
For instance, I want to see if the scientific name and commonName columns contain the string "Aca" (I want my search to be case insensitive)
My Grid definition:
var CustomGrid = declare([Grid, Pagination ]);
var gridStore = new Memory({ idProperty: 'tsn', data: null });
gridStore.queryEngine = rql.query;
grid = new CustomGrid({
store: gridStore,
columns:
[
{ field: "tsn", label: "TSN #"},
{ field: "scientificName", label: "Scientific Name"},
{ field: "commonName", label: "Common Name",},
],
autoHeight: 'true',
firstLastArrows: 'true',
pageSizeOptions: [50, 100],
}, id);
With the built in query language (I think simple query language), I was able to find the term in one column or the other, but I couldn't do a complex search that would return results for both columns.
grid.set("query", { scientificName : new RegExp(speciesKeyword, "i") });
grid.refresh()
I started reading and I think RQL can solve this problem, however, I am struggling with the syntax.
I have been looking at these pages:
http://rql-engine.eu01.aws.af.cm/
https://github.com/kriszyp/rql
And I am able to understand basic queries, however the "contains" syntax eludes me.
For instance if I had this simple data set and wanted to find the entries with scientific and common names that contain the string "Aca" I would think my contains query would look like this:
contains(scientificName,string:aca)
However, this results in no matches.
[
{
"tsn": 1,
"scientificName": "Acalypha ostryifolia",
"commonName": "Rough-pod Copperleaf",
},
{
"tsn": 2,
"scientificName": "Aegalius acadicus",
"commonName": "Northern Saw-whet Owl",
},
{
"tsn": 3,
"scientificName": "Portulaca pilosa",
"commonName": "2012-02-01",
},
{
"tsn": 4,
"scientificName": "Accipiter striatus",
"commonName": "Kiss-me-quick",
},
{
"tsn": 5,
"scientificName": "Acorus americanus",
"commonName": "American Sweetflag",
}
]
Can someone guide me in how to formulate the correct syntax? Thank you.
From what I'm briefly reading, it appears that:
contains was replaced by any and all
these are meant for array comparisons, not string comparisons
I'm not sure offhand whether RegExps can just be handed to other operations e.g. eq.
With dojo/store/Memory, you can also pass a query function which will allow you to do whatever you want, so if you wanted to compare for a match in one field or the other you could do something like this:
grid.set('query', function (item) {
var scientificRx = new RegExp(speciesKeyword, 'i');
var commonRx = new RegExp(...);
return scientificRx.test(item.scientificName) || commonRx.test(item.commonName);
});
Of course, if you want to filter only items that match both, you can do that with simple object syntax:
grid.set('query', {
scientificName: scientificRx,
commonName: commonRx
});
I want to have client side paging.
But for some reason, I only seem to get back the first page? Even though I know I have two pages worth of data (IE... I step through my code, and I definitely have two...)... What is more baffling is that my links to navigate through the pages never seem to be correct... For instance I would expect the following screen to say 1 of 2...
Also I would expect the bottom right hand side to say View 1-15 of 21?
My feeling is that I am doing something wrong in my data layer to give this pager it's info.
So It only returns the first page.
public static string JsonifyEnc(IEnumerable<TemplateModel> model, int popId, int page, int rows) {
TemplateModel variable = model.ToArray()[0];
ArrayList al = new ArrayList();
//foreach (PatientACOModel patMod in variable.Template) {
int i = 1;
int rowstart = (page * rows + 1) - rows;
int rowend = page * rows;
//Here is where I create the rows... nothing special here
var griddata = new {
total = variable.Template.Count % rows > 0 ? (variable.Template.Count / rows) + 1 : (variable.Template.Count / rows),
page = page,
records = al.Count,
rows = al.ToArray()
};
When I quick wath the total variable it says two?
This would be the first part of my json string that is returned...
{"total":2,"page":1,"records":15,"rows":
So it's there. Also, this is how I am building up my jqGrid...
$(document).ready(function () {
jQuery("#frTable").jqGrid ({
cmTemplate: { sortable: false },
caption: '#TempData["POPNAME"]' + ' Population',
datatype: 'json',
mtype: 'GET',
url: '/Encounters/GetAjaxPagedGridData/', //'Url.Action("GetAjaxPagedGridData", "Encounters", new { popId = TempData["POPULATIONID"] })',//
postData: { popId: '#TempData["POPULATIONID"]'},
pager: '#pager',
jsonReader: {repeatitems: false},
loadonce: true,
height: 'auto',
gridview: true,
viewrecords: true,
rowNum: 15,
shrinkToFit: false,
autowidth: true,
If you use loadonce: true on the client side you should change the server code so that it ignores page and rows options and returns all data. You should just sort the data corresponds to sidx and sord parameter (see sortname and sortorder in jqGrid). You don't need to fill total, page and records parts in the response.
If you use loadonce: true jqGrid load the data and save it in internal data and _index parameters. After that jqGrid change datatype option of jqGrid to "local". So all later sorting, filtering (searching) and paging of data will be done locally.