I'm software engineer in Cambodia.
I want to custom Grid.js pagination with supanase, but I faced a problem.
I don't know the solution because it's not in the documentation.
I'm using Nuxt 3
Please tell me how to implement.
The Code is below:
onMounted(() => {
grid.updateConfig({
columns: [
{ name: 'Avatar', id: 'avatar' },
{ name: 'name', id: 'name' },
{ name: 'gender', id: 'gender' },
{ name: 'email', id: 'email' },
{ name: 'phone', id: 'phone' },
{ name: 'address', id: 'address' },
],
pagination: {
enabled: true,
limit: 5,
server: {
url: (prev, page, limit) => `${prev}&limit=${limit}&offset=${page * limit}`
},
summary: true,
},
server: {
keepalive: true,
data: async (opt) => {
console.log(opt)
const { data: customers, error, count } = await supabase
.from('customers')
.select('id, name, gender, email, phone, address, avatar', { count: 'exact' })
.is('deleted_at', null)
.order('created_at', { ascending: false })
return {
data: customers.map((customer) => [
customer.avatar,
customer.name,
customer.gender,
customer.email,
customer.phone,
customer.address,
]),
total: count,
}
},
},
width: '100%',
search: true,
pagination: true,
fixedHeader: true,
className: {
td: 'sr-td-class',
table: 'sr-table',
},
})
grid.render(table.value)
})
I found resolve:
GridJs configuration:
onMounted(() => {
grid.updateConfig({
columns: [
{ name: 'Avatar', id: 'avatar' },
{ name: 'name', id: 'name' },
{ name: 'gender', id: 'gender' },
{ name: 'email', id: 'email' },
{ name: 'phone', id: 'phone' },
{ name: 'address', id: 'address' },
],
pagination: {
enabled: true,
limit: 5,
server: {
url: (prev, page, limit) => `${prev}&limit=${limit}&offset=${page * limit}`
},
summary: true,
},
server: {
keepalive: true,
data: async (opt) => {
console.log(opt)
const { data: customers, error, count } = await supabase
.from('customers')
.select('id, name, gender, email, phone, address, avatar', { count: 'exact' })
.is('deleted_at', null)
.order('created_at', { ascending: false })
return {
data: customers.map((customer) => [
customer.avatar,
customer.name,
customer.gender,
customer.email,
customer.phone,
customer.address,
]),
total: count,
}
},
},
width: '100%',
fixedHeader: true,
className: {
td: 'sr-td-class',
table: 'sr-table',
},
})
grid.render(table.value)
})
Then create server/api directory
after create file customers.ts in server/api/ directory
This is code in customers.ts file
import { serverSupabaseUser, serverSupabaseClient } from '#supabase/server'
export default defineEventHandler(async (event) => {
const user = await serverSupabaseUser(event)
const client = serverSupabaseClient(event)
const query = useQuery(event)
const from = query.page ? parseInt(query.page) * parseInt(query.limit) : 0
const to = query.page ? from + parseInt(query.limit) : query.limit
if (!user) {
throw createError({ statusCode: 401, message: 'Unauthorized' })
}
const { data, error, count } = await client
.from('customers')
.select('id, name, gender, email, phone, address, avatar', {
count: 'exact',
})
.is('deleted_at', null)
.order('created_at', { ascending: false })
.range(from, to)
return { customers: data, count }
})
Related
my api service
const ordersApi = createApi({
reducerPath: 'ordersApi',
baseQuery: fetchBaseQuery({ baseUrl: ROOT_ENDPOINT }),
tagTypes: ['Orders', 'Order'],
endpoints: (builder) => ({
getOrder: builder.query<TOrder, string>({
query: (id) => `/orders/${id}`,
providesTags: (result) =>
result
? [
{ type: 'Order', id: result.id },
{ type: 'Order', id: 'ORDER' },
]
: [{ type: 'Order', id: 'ORDER' }],
transformResponse: ({ _id, ...order }: TOrderResponse) => order,
}),
// some other queries
...
createOrder: builder.mutation<TOrder, TCreateOrderDto>({
query: (body) => {
return {
url: '/orders/create',
method: 'POST',
body,
};
},
invalidatesTags: (result) =>
result
? [
{ type: 'Order', id: result.id },
{ type: 'Order', id: 'ORDER' },
]
: [
{ type: 'Order', id: 'LIST' },
],
}),
// some other mutations
...
}),
});
createOrder creates a new Order object or modifies existing (by either adding new product to productsInOrder array or increasing amount property of existing array element).
The order object looks like this
{
"status": "NEW",
"vendor": {
"defaultVendor": false,
"companyName": "Mahamba",
"contactPerson": "Bahamab",
"id": "62b95b8e5603c45a7950de22"
},
"productsInOrder": [
{
"quantityToOrder": 12,
"price": 12,
"orderLimit": 12,
"product": "62b95b9c5603c45a7950de26",
"amount": 4
}
],
"orderId": 51,
"total": 48,
"id": "62ba6d60c784c34497b72652"
}
but for some reason when a new order is created neither getOrders nor getOrder endpoints get refetched and I don't understand why..
getOrder's providesTags id is exactly the same as createOrders invalidatesTags id.
Is nested objects that get mutated the problem?
How do I make sure getOrder is invalidated when a new order is created?
I'm trying to integrate algolia search with sanity CMS using the sanity-algolia library (ref https://www.sanity.io/plugins/sanity-algolia)
But when i try to get the plain text from Portable Text rich text content using the pt::text function.
i get expected '}' following object body , and i dont really know where im missing a bracket.
(another note: since im hosting sanity by using sanity start, I am using nextjs (my frontend) to run the function instead using the /api routes in nextJS) So sanity has a webhook to this route.
details about the error:
details: {
description: "expected '}' following object body",
end: 174,
query: '* [(_id in $created || _id in $updated) && _type in $types] {\n' +
' _id,\n' +
' _type,\n' +
' _rev,\n' +
' _type == "post" => {\n' +
' title,\n' +
' "path": slug.current,\n' +
' "body": pt::text(body)\n' +
' }\n' +
'}',
start: 107,
type: 'queryParseError'
}
this is the serverless function im running:
export default async function handler(req, res) {
if (req.headers["content-type"] !== "application/json") {
res.status(400);
res.json({ message: "Bad request" });
return;
}
const algoliaIndex = algolia.initIndex("dev_kim_miles");
const sanityAlgolia = indexer(
{
post: {
index: algoliaIndex,
projection: `{
title,
"path": slug.current,
"body": pt::text(body)
}`,
},
},
(document) => {
console.log(document);
return document;
},
(document) => {
if (document.hasOwnProperty("isHidden")) {
return !document.isHidden;
}
return true;
}
);
return sanityAlgolia
.webhookSync(sanityClient, req.body)
.then(() => res.status(200).send("ok"));
}
and my post schema from sanity:
export default {
name: 'post',
title: 'Post',
type: 'document',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
},
{
name: 'author',
title: 'Author',
type: 'reference',
to: {type: 'author'},
},
{
name: 'mainImage',
title: 'Main image',
type: 'image',
options: {
hotspot: true,
},
},
{
name: 'categories',
title: 'Categories',
type: 'array',
of: [{type: 'reference', to: {type: 'category'}}],
},
{
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
},
{
name: 'body',
title: 'Body',
type: 'blockContent',
},
{
name: 'extra',
title: 'extra',
type: 'blockContent',
},
],
preview: {
select: {
title: 'title',
author: 'author.name',
media: 'mainImage',
},
prepare(selection) {
const {author} = selection
return Object.assign({}, selection, {
subtitle: author && `by ${author}`,
})
},
},
}
sanity api v1 doesnt work with functions, I was using
const sanityClient = client({
dataset: "production",
useCdn: true,
projectId: "project_id",
});
which defaults to v1, but in order to use function i added a apiVersion parameter, which made it use later api version:
const sanityClient = client({
dataset: "production",
useCdn: true,
projectId: "9dz8b3g1",
apiVersion: "2021-03-25",
});
As explained in the NextJs documentation: https://nextjs.org/docs/api-reference/next.config.js/headers , it is possible to configure the HTTP headers of a page using the code below, however my code is not working.
Next Example:
module.exports = {
async headers() {
return [
{
source: '/about',
headers: [
{
key: 'x-custom-header',
value: 'my custom header value',
},
{
key: 'x-another-custom-header',
value: 'my other custom header value',
},
],
},
]
},
}
My Code:
module.exports = {
async headers() {
return [
{
source: '/about',
headers: [
{
key: 'cache-control',
value: 'max-age=31536000',
},
],
},
]
},
}
You can see my code about grid.
Here, Window opens but there is not any value in my grid.
Could you please help?
My php returns JSON encode blow:
{"results":3,"disPath":"","success":"true","rows":[{"id":"1","faz":"Faz1"},{"id":"2","faz":"Faz2"},{"id":"3","faz":"Faz3"}]}
Code
Ext.define('MyDesktop.GridFazlar', {
extend: 'Ext.ux.desktop.Module',
requires: [
'Ext.data.ArrayStore',
'Ext.util.Format',
'Ext.grid.Panel',
'Ext.grid.RowNumberer'
],
id:'grid-fazlar',
init : function(){
this.launcher = {
text: 'Fazlar',
iconCls:'icon-grid'
};
},
createWindow : function(){
var desktop = this.app.getDesktop();
var win = desktop.getWindow('grid-fazlar');
if(!win){
var fazlarGridStore = new Ext.data.JsonStore({
root: 'rows',
autoLoad: true,
totalProperty: 'results',
remoteSort: true,
proxy: new Ext.data.HttpProxy({
url: 'phps/gridPhasesLoader.php',
actionMethods: {
create : 'POST',
read : 'POST',
update : 'POST',
destroy: 'POST'
},
}),
baseParams:{
depth: '2',
parentId: '2',
proccess: 'callGrid',
},
fields: [{
name :'id'
},{
name :'faz'
},
//{
]
});
win = desktop.createWindow({
id: 'grid-fazlar',
title:'Fazlar',
width:740,
height:480,
iconCls: 'icon-grid',
animCollapse:false,
constrainHeader:true,
layout: 'fit',
listeners:{
beforeshow: function(){
fazlarGridStore.proxy.extraParams = {
depth: '2',
parentId: '2',
proccess: 'callGrid',
};
}
},
items: [
{
border: false,
xtype: 'grid',
listeners: {
celldblclick : function() {
alert('Deneme');
},
},
contextMenu: new Ext.menu.Menu({
items:[
{
id:'grid-fazlar_menu_denemebutton',
text: 'DenemeButonu',
listeners: {
click: function(){
alert('sssss');
}
}
}
]
}),
store:fazlarGridStore,
columns: [
new Ext.grid.RowNumberer(),
{
text: "ID",
//flex: 1,
width: 70,
sortable: true,
dataIndex: 'id'
},{
text: "Faz",
//flex: 1,
width: 70,
sortable: true,
dataIndex: 'faz'
},
]
}
],
tbar:[{
text:'Add Something',
tooltip:'Add a new row',
iconCls:'add'
}, '-', {
text:'Options',
tooltip:'Modify options',
iconCls:'option'
},'-',{
text:'Remove Something',
tooltip:'Remove the selected item',
iconCls:'remove'
}]
});
}
return win;
},
statics: {
getDummyData: function () {
return [
['Faz1','06/06/2011','15/08/2010','19/12/2010'],
['Faz2','01/10/2010','15/11/2010','29/11/2011'],
['Faz3','16/11/2010','16/12/2010','14/02/2011'],
['Faz4','19/03/2011','20/03/2011','18/06/2011'],
['Faz5','21/03/2011','20/05/2011','18/08/2011'],
['Faz6','21/05/2011','05/07/2011','18/09/2011'],
['Faz7','06/07/2011','04/09/2011','06/12/2011'],
['Faz8','30/09/2011','30/10/2011','29/12/2011'],
];
}
}
});
Your Json has a custom root : 'rows'. You must configure your datareader to take this into account:
proxy: {
type: 'ajax',
url: 'phps/gridPhasesLoader.php',
actionMethods: {
create : 'POST',
read : 'POST',
update : 'POST',
destroy: 'POST'
},
reader: {
type: 'json',
root: 'rows'
}
},
I am new to ExtJs and I am using ExtJs4.
Now As shown in below image, There is one textfield named keywords, What I want to do is When I click on the button it will pass data of textfield to servlet and display resulted record in grid.
Now I have no idea how to do this. I am receiving JSON data response from servlet but don't know how to reload the store and refresh the grid.
Below is code for my store and grid.
Ext.define("Post", {
extend: 'Ext.data.Model',
proxy: {
type: 'ajax',
url: '/ezdi/searchServlet',
method: 'POST',
reader: {
type: 'json',
root: 'rows'
//,totalProperty: 'totalCount'
}
},
fields: [{
name: 'docid',
mapping: 'docid'
}, {
name: 'mrn',
mapping: 'mrn'
}, {
name: 'fname',
mapping: 'fname'
}]
});
var gridDataStore = Ext.create('Ext.data.Store', {
model: 'Post'
});
// Data store for grid end
Ext.define('Ezdi.Grid', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.ezdigrid',
initComponent: function() {
var config = {
store: gridDataStore,
columns: [{
header: "DocID",
width: 100,
sortable: true,
dataIndex: 'docid'
}, {
header: "MRN",
width: 100,
sortable: true,
dataIndex: 'mrn'
}, {
header: "FirstName",
width: 100,
sortable: true,
dataIndex: 'fname'
}],
viewConfig: {
forceFit: false,
autoLoad: false
},
loadMask: true
};
}
});
You could use:
{
xtype: 'button',
text: 'Search',
handler: function() {
store.clearFilter(); //clear previous search value
var searchValue = Ext.getCmp("textFieldId").getValue(); //get new value
store.load().filter('jsonGridFielName', searchValue); //load filtered data
}
}
And for for multiple textfield search:
//FILTERS
var searchValue1 = Ext.getCmp("textFieldId1").getValue(); //value1
var searchValue2 = Ext.getCmp("textFieldId2").getValue(); //value2
var noValue = "0000xxxx"; //no Value, for empty field, use value that you are sure it is not going to be searched!!!
var clear = store.clearFilter(); //shortcut
if (!searchValue1 && !searchValue2) {
clear;
store.load().filter("jsonGridFielName1", noValue);
} else if (searchValue1) {
clear;
store.load().filter('jsonGridFielName1', searchValue1);
//...else if(searchValue n...)...
} else {
clear;
store.load().filter('jsonGridFielName2', searchValue2);
}
ezdigrid.js
// Data store for grid start
Ext.define("Post", {
extend: 'Ext.data.Model',
proxy: {
type: 'ajax',
url: '/ezdi/searchServlet',
method: 'GET',
reader: {
type: 'json',
root: 'rows'
//,totalProperty: 'totalCount'
}
},
fields: [{
name: 'docid',
mapping: 'docid'
}, {
name: 'mrn',
mapping: 'mrn'
}, {
name: 'fname',
mapping: 'fname'
}]
});
var gridDataStore = Ext.create('Ext.data.Store', {
// pageSize: 10,
model: 'Post'
});
// Data store for grid end
Ext.define('Ezdi.Grid', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.ezdigrid',
initComponent: function() {
var config = {
store: gridDataStore,
columns: [{
//id:'ms',
header: "DocID",
width: 100,
sortable: true,
dataIndex: 'docid'
}, {
header: "MRN",
width: 100,
sortable: true,
dataIndex: 'mrn'
}, {
header: "FirstName",
width: 100,
sortable: true,
dataIndex: 'fname'
}],
viewConfig: {
forceFit: false,
autoLoad: false
},
loadMask: true
}; // eo config object
// apply config
Ext.apply(this, Ext.apply(this.initialConfig, config));
// call parent
Ezdi.Grid.superclass.initComponent.apply(this, arguments);
// load the store at the latest possible moment
this.on({
afterlayout: {
scope: this,
single: true,
fn: function() {
this.store.load({
params: {
start: 0,
limit: 30
}
});
}
}
});
} // eo function initComponent
});
demo.html
//handler for button click event
fbar: [{
xtype: 'button',
text: 'Search',
handler: function() {
var value = Ext.getCmp('_keyword').getValue(); //_keyword is textField
gridDataStore.load().filter('keywords', value);
}
}]
MyServlet
keyword = request.getParameter("keywords");
//code for quesry processing
Use extraParams in your model.
extraParams: {
keywords: 'your-value'
}
Put following code in your button click handler.
gridDataStore.proxy.extraParams.keywords = 'new value';
gridDataStore.load();