Zingchart not showing crosshair plot-area guide-labels for series with only one point - zingchart

I have a multiple series (time) line chart. I define crosshair-x plot-area guide-labels on the individual series. If a series has more than one data element (point) the plot-area guide-labels for all points in that series display fine. If a series has only one point that specific series fails to show the plot-area guide labels. I am using the latest Chrome browser (63.0.3239.132)
I have included the config below for the errant chart, but have also set up 2 examples on codepen.
Example with guide-labels not showing for the 1-point series
https://codepen.io/iklotzko/pen/QQqEmp
Example with guide-labels showing for both 2-point series
https://codepen.io/iklotzko/pen/bLoeqp
The only difference between the above examples is S2 in the errant example has just 1 point.
The config for the errant example
Series 1 (S1) has 2 points, Series 2 (S2) has 1 point. The guide labels for S2 fail to show in the plot area.
var myConfig =
{
"type":"line",
"legend":{
"layout":"1x5",
"shadow":false,
"adjustLayout":true,
"margin-bottom":"17px",
"margin-top":"0",
"padding":"0",
"align":"center",
"background-color":"none",
"vertical-align":"bottom",
"border-top":"0 none",
"border-right":"0 none",
"border-bottom":"0 none",
"border-left":"0 none"
},
"labels":{
"visible":false
},
"marker":{
"border-width":0,
"border-color":"none",
"shadow":false
},
"plot":{
"line-width":1
},
"title":{
"adjustLayout":true,
"text":"S2-2pts, S2-pt",
"font-size":12,
"margin-top":2,
"padding":0,
"height":"10px",
"bold":true,
"border":"0 none",
"font-color":"#333",
"background-color":"white"
},
"subtitle":{
"text":"subtitle",
"color":"#444",
"font-style":"italic",
"font-size":12,
"margin":0,
"offset-y":-17,
"padding":0,
"height":"18px",
"bold":false
},
"plotarea":{
"margin":"dynamic 50 dynamic dynamic",
"adjust-layout":true
},
"scaleX":{
"items-overlap":true,
"values":[1516942800000,1519362000000,1522382400000],
"transform":{
"type":"date",
"all":"%d-%M-%Y",
"item":{
"visible":false
}
},
"item":{
"angle":-45,
"font-size":10
}
},
"scaleY":{
"guide":{
"lineStyle":"solid"
},
"format":"$%v",
"thousands-separator":",",
"label":{
"text":"Amount"
}
},
"tooltip":{
"visible":false
},
"crosshairX":{
"lineColor":"#000",
"plotLabel":{
"exact":false,
"multiple":true,
"font-color":"white",
"border-radius":"5px",
"text":"%t: $%v<br />%data-custom (%k)",
"thousands-separator":",",
"decimals":2
},
"scaleLabel":{
"borderRadius":2,
"format":"$%v",
"thousands-separator":",",
"decimals":2
},
"marker":{
"size":5,
"type":"circle",
"backgroundColor":"transparent",
"border-color":"#888",
"border-width":"6px",
"border-alpha":0.4
}
},
"series":[
{
"values":[[1517893200000,19480], [1522382400000, 30000]],
"data-custom":["first-point", "second-point"],
"text":"S1",
"marker":{
"type":"star5",
"background-color":"blue",
"border-width":0,
"border-color":"none",
"size":6,
"shadow":false
},
"legend-marker":{
"type":"star5",
"backgroundColor":"blue",
"shadow":false,
"show-line":true,
"size":4,
"border-width":0,
"border-color":"none"
},
"guide-label":{
"background-color":"blue",
"border-color":"blue"
},
"lineColor":"blue"
},
{
"values":[],
"data-custom":[],
"text":"Empty Series",
"marker":{
"type":"diamond",
"background-color":"black",
"border-width":0,
"border-color":"none",
"size":6,
"shadow":false
},
"legend-marker":{
"type":"diamond",
"backgroundColor":"black",
"shadow":false,
"show-line":true,
"size":4,
"border-width":0,
"border-color":"none"
},
"guide-label":{
"background-color":"black",
"border-color":"black"
},
"lineColor":"black"
},
{
"values":[],
"data-custom":[],
"text":"Empty-Series",
"marker":{
"type":"square",
"background-color":"brown",
"border-width":0,
"border-color":"none",
"size":6,
"shadow":false
},
"legend-marker":{
"type":"square",
"backgroundColor":"brown",
"shadow":false,
"show-line":true,
"size":4,
"border-width":0,
"border-color":"none"
},
"guide-label":{
"background-color":"brown",
"border-color":"brown"
},
"lineColor":"brown"
},
{
"values":[],
"data-custom":[],
"text":"Empty-Series",
"marker":{
"type":"triangle",
"background-color":"green",
"border-width":0,
"border-color":"none",
"size":6,
"shadow":false
},
"legend-marker":{
"type":"triangle",
"backgroundColor":"green",
"shadow":false,
"show-line":true,
"size":4,
"border-width":0,
"border-color":"none"
},
"guide-label":{
"background-color":"green",
"border-color":"green"
},
"lineColor":"green"
},
{
"values":[[1517893200000,0]],
"data-custom":["first-point"],
"text":"S2",
"marker":{
"type":"circle",
"background-color":"red",
"border-width":0,
"border-color":"none",
"size":6,
"shadow":false
},
"legend-marker":{
"type":"circle",
"backgroundColor":"red",
"shadow":false,
"show-line":true,
"size":4,
"border-width":0,
"border-color":"none"
},
"guide-label":{
"background-color":"red",
"border-color":"red"
},
"lineColor":"red"
}
],
"id":"myChart"
};

I emailed ZingChart and they confirmed this is a bug in 2.6.3, they fixed the bug and will make it available in their next release, presumably 2.6.4 or 2.7.0

Related

Replace img tags from headless cms with Next Image component

I am working on a headless wordpress marketing/commerce app with Nextjs (hosted on vercel) and would like to parse incoming content bodies to conditionally replace img tags therein using the Next/Image Image component. If anyone has any ideas on how to go about hot-swapping Image for img so that the images are rendered as WebP and not png that would be immensely appreciated.
Here is my Page Template Component used to render 9 top-level pages currently
import css from './page-template.module.css';
import cn from 'classnames';
import { Fallback, AtomicImage } from '../UI';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import {
PageIdType,
useParentPageQuery,
ParentPageQuery
} from '#/graphql/graphql';
import parser, { DOMNode, HTMLReactParserOptions } from 'html-react-parser';
import { Element, Node } from 'domhandler/lib/node';
import React from 'react';
import { ApolloError as ApolloErrorProps } from '#apollo/client';
export interface PageTemplateProps {
className?: string;
topLevelPage: ParentPageQuery['page'];
}
const dynamicProps = {
loading: () => <Fallback />
};
const ApolloError = dynamic<{ error: ApolloErrorProps }>(
async () => import('../UI/ApolloError'),
dynamicProps
);
const PageTemplate = ({ className, topLevelPage }: PageTemplateProps) => {
const router = useRouter();
const paths = router.query ? (router.query.paths as string) : '/';
console.log(JSON.stringify(paths, null, 2));
const { data, loading, error } = useParentPageQuery({
variables: {
idType: PageIdType.URI,
id: encodeURIComponent(paths)
},
notifyOnNetworkStatusChange: true
});
topLevelPage = data != null && data.page != null ? data.page : undefined;
const options: HTMLReactParserOptions = {
replace: (domNode: DOMNode) => {
if (
domNode instanceof Node && domNode.type
? domNode.type.replace('img', 'Image')
: domNode.type
) {
return domNode;
}
return domNode.cloneNode();
}
};
const value = parser(`${topLevelPage?.content}`, options);
const ele = value;
const htmlToReact = (x: string) =>
parser(x, {
replace: (domNode: DOMNode) => {
if (domNode instanceof Element && domNode.attribs) {
return domNode.attribs;
} else {
return domNode.cloneNode();
}
}
});
console.log(
htmlToReact(
`${
topLevelPage?.content?.includes(`img`)
? topLevelPage.content.replace('img', 'Image')
: topLevelPage?.content
}`
)
);
// const x = createElement(
// topLevelPage?.content ? topLevelPage.content : `${topLevelPage?.content}`
// )
// const GenesisElement = React.createElement(topLevelPage!.content as string);
return (
<>
{error ? (
<ApolloError error={error} />
) : loading && !error ? (
<Fallback />
) : (
<>
{topLevelPage &&
topLevelPage.content &&
topLevelPage.title &&
topLevelPage.featuredImage ? (
<>
<div
className={cn(
'overflow-hidden select-none font-sans',
className
)}>
<div className='relative max-w-8xl mx-auto px-4 sm:px-6 lg:px-8'>
<div className='hidden lg:block bg-blue-900 absolute top-0 bottom-0 left-3/4 w-screen'></div>
<div className='mx-auto text-base max-w-7xl lg:grid lg:grid-cols-5 lg:gap-8 lg:max-w-none'>
<div className='lg:col-span-3 '>
<h2 className='text-5xl font-sans text-blue-800 font-semibold tracking-tighter uppercase'>
<div
className={cn(
css.title,
'capitalize font-sans transform-translate transform-gpu mt-5 ml-5'
)}>
{parser(topLevelPage.title).toString()}
</div>
</h2>
<div className=' text-justify justify-items-stretch justify-around text-base leading-normal font-medium tracking-tight sm:text-lg duration-300 transition-colors'>
<div className={cn(css.p)}>
{htmlToReact(
`${
topLevelPage?.content?.includes(`img`)
? topLevelPage.content.replace('img', 'Image')
: topLevelPage?.content
}`
)}
</div>
</div>
</div>
<div className='mt-8 lg:grid lg:col-span-2 lg:gap-8'>
<div className='relative lg:row-start-1 lg:col-start-2'>
<svg
className='hidden lg:block absolute top-0 right-0 -mt-20 -mr-20'
width='404'
height='384'
fill='none'
viewBox='0 0 404 384'
aria-hidden='true'>
<defs>
<pattern
id='de316486-4a29-4312-bdfc-fbce2132a2c1'
x='0'
y='0'
width='20'
height='20'
patternUnits='userSpaceOnUse'>
<rect
x='0'
y='0'
width='4'
height='4'
className='text-gray-50'
fill='rgba(249, 250, 251,1)'
/>
</pattern>
</defs>
<rect
width='404'
height='384'
fill='#rgba(249, 250, 251,1)'
/>
</svg>
<div className='relative text-base mx-auto max-w-prose lg:max-w-none'>
<figure>
<div className='aspect-w-12 aspect-h-7 lg:aspect-none bg-gray-900 bg-opacity-75 rounded-full p-4'>
<AtomicImage
className='rounded-lg shadow-lg object-cover object-center'
width={500}
aria-orientation='vertical'
height={500}
src={
topLevelPage.featuredImage.node?.sourceUrl
? topLevelPage.featuredImage.node.sourceUrl
: 'https://secure.gravatar.com/avatar/2715d942950e7c4c4672dcb984c52e84?s=96&d=mm&r=pg'
}
/>
</div>
<figcaption className='flex text-sm text-gray-100'>
<svg
className='flex-none w-6 h-6 text-blue-700'
stroke='#000000'
strokeWidth='1'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 20 20'
fill=''
aria-hidden='true'>
<path
fillRule='evenodd'
d='M4 5a2 2 0 00-2 2v8a2 2 0 002 2h12a2 2 0 002-2V7a2 2 0 00-2-2h-1.586a1 1 0 01-.707-.293l-1.121-1.121A2 2 0 0011.172 3H8.828a2 2 0 00-1.414.586L6.293 4.707A1 1 0 015.586 5H4zm6 9a3 3 0 100-6 3 3 0 000 6z'
clipRule='evenodd'
fill='text-blue-700'
/>
</svg>
<span className='ml-2 text-gray-600 font-medium'>
{topLevelPage.title}
</span>
</figcaption>
</figure>
</div>
</div>
</div>
</div>
</div>
</div>
</>
) : (
<></>
)}
</>
)}
</>
);
};
export default PageTemplate;
I have tried using
const htmlToReact = (x: string) =>
parser(x, {
replace: (domNode: DOMNode) => {
if (domNode instanceof Element && domNode.attribs) {
return domNode.attribs;
} else {
return domNode.cloneNode();
}
}
});
console.log(
htmlToReact(
`${
topLevelPage?.content?.includes(`img`)
? topLevelPage?.content?.replace('img', 'Image')
: topLevelPage?.content
}`
)
);
Which, when console logged, has the following shape:
[
'\n',
{
'$$typeof': Symbol(react.element),
type: 'div',
key: '1',
ref: null,
props: {
className: 'wp-block-genesis-blocks-gb-columns gpb-altitude-section-text-image-columns-dark gb-layout-columns-2 gb-2-col-equal has-eso-white-background-color gb-columns-center alignfull',
style: [Object],
children: [Object]
},
_owner: null,
_store: {}
},
'\n'
]
Expanding the above:
[
"\n",
{
"type": "div",
"key": "1",
"ref": null,
"props": {
"className": "wp-block-genesis-blocks-gb-columns gpb-altitude-section-text-image-columns-dark gb-layout-columns-2 gb-2-col-equal has-eso-white-background-color gb-columns-center alignfull",
"style": {
"paddingTop": "3em",
"paddingRight": "1em",
"paddingBottom": "3em",
"paddingLeft": "1em"
},
"children": {
"type": "div",
"key": null,
"ref": null,
"props": {
"className": "gb-layout-column-wrap gb-block-layout-column-gap-2 gb-is-responsive-column",
"style": {
"maxWidth": "1200px"
},
"children": [
"\n",
{
"type": "div",
"key": "1",
"ref": null,
"props": {
"className": "wp-block-genesis-blocks-gb-column gb-block-layout-column gb-is-vertically-aligned-top",
"children": {
"type": "div",
"key": null,
"ref": null,
"props": {
"className": "gb-block-layout-column-inner",
"children": [
"\n",
{
"type": "h2",
"key": "1",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"style": {
"fontSize": "28px"
},
"children": "Start with Spot."
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "p",
"key": "3",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"children": "Next Gen Dermatology meets Coast-to-Coast Accessibility — Spot. AI-Powered, HIPAA-Compliant; Guaranteed to meet your Skincare needs."
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "h2",
"key": "5",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"style": {
"fontSize": "28px"
},
"children": "Create a Case."
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "p",
"key": "7",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"children": "Creating a new case on Spot is as easy as 1, 2, 3. You’re walked through a set of skin-related questions — similar to ones your doctor would ask you in person."
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "h2",
"key": "9",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"style": {
"fontSize": "28px"
},
"children": "Conquer the Unexpected"
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "p",
"key": "11",
"ref": null,
"props": {
"className": "has-eso-black-color has-text-color",
"children": "Don’t put looking and feeling your best on hold for weeks if not months to get in with a specialist; conquer skin conditions as they arise from the convenience of your smart phone or computer with Spot. "
},
"_owner": null,
"_store": {}
},
"\n"
]
},
"_owner": null,
"_store": {}
}
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "div",
"key": "3",
"ref": null,
"props": {
"className": "wp-block-genesis-blocks-gb-column gb-block-layout-column gb-is-vertically-aligned-center",
"children": {
"type": "div",
"key": null,
"ref": null,
"props": {
"className": "gb-block-layout-column-inner",
"children": [
"\n",
{
"type": "figure",
"key": "1",
"ref": null,
"props": {
"className": "wp-block-image size-full is-style-default",
"children": {
"type": "img",
"key": null,
"ref": null,
"props": {
"loading": "lazy",
"width": "980",
"height": "980",
"src": "https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=980%2C980&ssl=1",
"alt": "",
"className": "wp-image-71",
"srcSet": "https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?w=981&ssl=1 981w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=300%2C300&ssl=1 300w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=150%2C150&ssl=1 150w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=768%2C768&ssl=1 768w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=600%2C600&ssl=1 600w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=324%2C324&ssl=1 324w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=416%2C416&ssl=1 416w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=100%2C100&ssl=1 100w",
"sizes": "(max-width: 980px) 100vw, 980px",
"data-recalc-dims": "1",
"children": null
},
"_owner": null,
"_store": {}
}
},
"_owner": null,
"_store": {}
},
"\n\n\n\n",
{
"type": "p",
"key": "3",
"ref": null,
"props": {
"children": null
},
"_owner": null,
"_store": {}
},
"\n"
]
},
"_owner": null,
"_store": {}
}
},
"_owner": null,
"_store": {}
},
"\n"
]
},
"_owner": null,
"_store": {}
}
},
"_owner": null,
"_store": {}
},
"\n"
]
Which ultimately contains:
<img loading="lazy" width="980" height="980" src="https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=980%2C980&ssl=1" alt="" class="wp-image-71" srcset="https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?w=981&ssl=1 981w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=300%2C300&ssl=1 300w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=150%2C150&ssl=1 150w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=768%2C768&ssl=1 768w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=600%2C600&ssl=1 600w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=324%2C324&ssl=1 324w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=416%2C416&ssl=1 416w, https://i1.wp.com/cortinaai.wpengine.com/wp-content/uploads/2021/07/Spot.png?resize=100%2C100&ssl=1 100w" sizes="(max-width: 980px) 100vw, 980px" data-recalc-dims="1" />
Here is my top level catch-all parent pages file, [...paths].tsx
import { pathDeduplication } from '#/lib/path-deduplication';
import { AppLayout } from '#/components/Layout';
import { initializeApollo, addApolloState } from '#/apollo/apollo';
import { useRouter } from 'next/router';
import { Fallback } from '#/components/UI';
import { ParsedUrlQuery } from '#/types/pages';
import { PageTemplate } from '#/components/PageTemplate';
import {
GetStaticPathsContext,
GetStaticPathsResult,
GetStaticPropsContext,
GetStaticPropsResult,
InferGetStaticPropsType
} from 'next';
import {
ParentPageDocument,
ParentPagesDocument,
ParentPageQuery,
ParentPagesQuery,
ParentPageQueryVariables,
ParentPagesQueryVariables,
PageIdType,
DynamicNavDocument,
DynamicNavQuery,
DynamicNavQueryVariables,
MenuNodeIdTypeEnum,
OrderEnum,
PostObjectsConnectionOrderbyEnum
} from '#/graphql/graphql';
export type CatchAllTopLevelPagesProps = {
nav: DynamicNavQuery;
pageData: ParentPageQuery;
paths: string;
};
const apolloClient = initializeApollo();
export const getStaticPaths = async ({
locales
}: GetStaticPathsContext): Promise<GetStaticPathsResult<ParsedUrlQuery>> => {
const { data: parentPages } = await apolloClient.query<
ParentPagesQuery,
ParentPagesQueryVariables
>({
query: ParentPagesDocument,
notifyOnNetworkStatusChange: true,
variables: {
first: 30,
parentIn: [null],
field: PostObjectsConnectionOrderbyEnum.SLUG,
order: OrderEnum.DESC
}
});
return {
paths: locales
? locales.reduce<string[]>((paths, locale) => {
parentPages.pages?.edges?.forEach(page => {
if (page?.node?.slug && !pathDeduplication.includes(page.node.slug))
paths.push(`/${locale}/${page.node.slug}`);
});
return paths;
}, [])
: parentPages?.pages?.edges
? parentPages.pages.edges.map(page => `/${page?.node?.slug}`)
: ['/'],
fallback: false
};
};
export const getStaticProps = async (
ctx: GetStaticPropsContext<ParsedUrlQuery>
): Promise<GetStaticPropsResult<CatchAllTopLevelPagesProps>> => {
const paths = ctx.params ? (ctx.params.paths as string) : '/';
const { data: pageData } = await apolloClient.query<
ParentPageQuery,
ParentPageQueryVariables
>({
query: ParentPageDocument,
notifyOnNetworkStatusChange: true,
variables: {
id: paths[0],
idType: PageIdType.URI
}
});
const { data: nav } = await apolloClient.query<
DynamicNavQuery,
DynamicNavQueryVariables
>({
query: DynamicNavDocument,
variables: {
idHead: 'Header',
idTypeHead: MenuNodeIdTypeEnum.NAME,
idFoot: 'Footer',
idTypeFoot: MenuNodeIdTypeEnum.NAME
},
notifyOnNetworkStatusChange: true
});
return addApolloState(apolloClient, {
props: {
nav,
pageData,
paths
},
revalidate: 60
});
};
export default function PathsCatchAllPage<T extends typeof getStaticProps>({
nav,
pageData,
paths
}: InferGetStaticPropsType<T>) {
console.log(JSON.stringify(paths, null, 2));
const router = useRouter();
paths = router.query ? (router.query.paths as string) : '';
return (
<>
{router.isFallback ? (
<>
<Fallback />
</>
) : pageData ? (
<AppLayout header={nav.Header} footer={nav.Footer}>
{pageData && pageData.page ? (
<PageTemplate topLevelPage={pageData.page} />
) : (
<div>{'loading...'}</div>
)}
</AppLayout>
) : (
<>{'Something went wrong...'}</>
)}
</>
);
}
The image renders as a png instead of webp (intended result) for everything I've attempted so far
link to vercel app

Can't call a nested key in Mongoose and Express (undefined)

I have a Mongoose Schema that display results. I can display the result array but not what's inside.
{
"_id": {
"$oid": "5cfa15e123d2f414da760635"
},
"objectID": 21078,
"cars_getroute": "volvo-123-gt-coach-1967-1968",
"gm_url": "https://www.url.com",
"results": [
{
"marque": "Volvo",
"model": "Amazon 123 GT",
"model_year": "1967",
"price_str": "£4 982",
"price_int": 4982,
"price_currency": "£",
"sold": true,
"auction_house": "Anglia Car Auctions",
"auction_country": "Grande-Bretagne",
"auction_date": "25 août 2018",
"auction_datetime": "2018-08-25",
"auction_url": null,
"image_urls": null,
"price_int_eu": 5523
},
{
"marque": "Volvo",
"model": "Amazon 123 GT",
"model_year": "1968",
"price_str": "CHF9 000",
"price_int": 9000,
"price_currency": "CHF",
"sold": true,
"auction_house": "Oldtimer Galerie",
"auction_country": "Suisse",
"auction_date": "29 avril 2017",
"auction_datetime": "2017-04-29",
"auction_url": null,
"image_urls": "https://www.url.com/img/auctions/car/thumb/17-4-11-f8a85a227775570cdeb80ba0a437cc40.jpg",
"price_int_eu": 8309
},
{
"marque": "Volvo",
"model": "Amazon 123 GT",
"model_year": "1968",
"price_str": "Estimate £6 000 - £7 000 (unsold)",
"price_int": null,
"price_currency": null,
"sold": false,
"auction_house": "Herefordshire Vintage Auctions",
"auction_country": "Grande-Bretagne",
"auction_date": "20 octobre 2016",
"auction_datetime": "2016-10-20",
"auction_url": "http://www.url.com/Motor_details.php?pid=188&motor=188_01.jpg",
"image_urls": "https://www.url.com/img/auctions/car/2016-10-4a5a09eeb5c685e053e421c45656dcf7.jpg",
"price_int_eu": null
}
]
}
And the model:
const DemocarauctionSchema = new Schema({
objectID: {
type: Number
},
cars_getroute: {
type: String
},
gm_url: {
type: String
},
"results": { type: [{
marque: {
type: String
},
model: {
type: String
},
model_year: {
type: String
},
price_str: {
type: String
},
prince_int: {
type: Number
},
price_currency: {
type: String
},
sold: {
type: Boolean
},
auction_house: {
type: String
},
auction_country: {
type: String
},
auction_date: {
type: String
},
auction_datetime: {
type: String
},
auction_url: {
type: String
},
image_urls: {
type: String
},
price_int_eu: {
type: Number
},
}]}
},
{
collection: 'democarficheauction'
});
And when I call console.log(democarauctions.results.marque), nothing appears.
Is it a problem with my Schema ? Because There are multiple "marque" keys in the "results" array ?
It's working well with gm_url, objectId and cars_getroute.
EDIT: here is my Express route:
router.get('/demo/:cars_getroute', (req, res, next) => {
Promise.all([Democar.findOne({ cars_getroute: req.params.cars_getroute }), Democarauction.findOne({ cars_getroute: req.params.cars_getroute })])
.then(result => {
const [democars, democarauctions] = result;
console.log(democarauctions.results.marque)
res.render('demo/fichecar-demo', {
marque: democars.marque,
modele: democars.modele,
sous_modele: democars.sous_modele,
sous_modele2: democars.sous_modele2,
type: democars.type,
precision: democars.precision,
years_interval: democars.years_interval,
cote_actual: democars.cote_actual.toLocaleString('fr-FR'),
img_url: democars.img_url,
cote_1989_eu_excp: democars.cote.cote_1989.cote_1989_eu.cote_1989_excp,
cote_1989_eu_concours: democars.cote.cote_1989.cote_1989_eu.cote_1989_concours,
cote_1989_eu_base: democars.cote.cote_1989.cote_1989_eu.cote_1989_base,
cote_1989_eu_be: democars.cote.cote_1989.cote_1989_eu.cote_1989_be,
cote_1989_eu_me: democars.cote.cote_1989.cote_1989_eu.cote_1989_me,
cote_1989_eu_ar: democars.cote.cote_1989.cote_1989_eu.cote_1989_ar,
cote_1989_eu_epa: democars.cote.cote_1989.cote_1989_eu.cote_1989_epa,
cote_2004_excp: democars.cote.cote_2004.cote_2004_excp,
cote_2004_concours: democars.cote.cote_2004.cote_2004_concours,
cote_2004_base: democars.cote.cote_2004.cote_2004_base,
cote_2004_be: democars.cote.cote_2004.cote_2004_be,
cote_2004_me: democars.cote.cote_2004.cote_2004_me,
cote_2004_ar: democars.cote.cote_2004.cote_2004_ar,
cote_2004_epa: democars.cote.cote_2004.cote_2004_epa,
cote_2014_excp: democars.cote.cote_2014.cote_2014_excp,
cote_2014_concours: democars.cote.cote_2014.cote_2014_concours,
cote_2014_base: democars.cote.cote_2014.cote_2014_base,
cote_2014_be: democars.cote.cote_2014.cote_2014_be,
cote_2014_me: democars.cote.cote_2014.cote_2014_me,
cote_2014_ar: democars.cote.cote_2014.cote_2014_ar,
cote_2014_epa: democars.cote.cote_2014.cote_2014_epa,
cote_2017_excp: democars.cote.cote_2017.cote_2017_excp,
cote_2017_concours: democars.cote.cote_2017.cote_2017_concours,
cote_2017_base: democars.cote.cote_2017.cote_2017_base,
cote_2017_be: democars.cote.cote_2017.cote_2017_be,
cote_2017_me: democars.cote.cote_2017.cote_2017_me,
cote_2017_ar: democars.cote.cote_2017.cote_2017_ar,
cote_2017_epa: democars.cote.cote_2017.cote_2017_epa,
cote_2019_excp: democars.cote.cote_2019.cote_2019_excp,
cote_2019_concours: democars.cote.cote_2019.cote_2019_concours,
cote_2019_base: democars.cote.cote_2019.cote_2019_base,
cote_2019_be: democars.cote.cote_2019.cote_2019_be,
cote_2019_me: democars.cote.cote_2019.cote_2019_me,
cote_2019_ar: democars.cote.cote_2019.cote_2019_ar,
cote_2019_epa: democars.cote.cote_2019.cote_2019_epa,
cote_actual_chart: democars.cote_actual,
cote_actual_excp: democars.cote.cote_2019.cote_2019_excp.toLocaleString('fr-FR'),
cote_actual_concours: democars.cote.cote_2019.cote_2019_concours.toLocaleString('fr-FR'),
cote_actual_base: democars.cote.cote_2019.cote_2019_base.toLocaleString('fr-FR'),
cote_actual_be: democars.cote.cote_2019.cote_2019_be.toLocaleString('fr-FR'),
cote_actual_me: democars.cote.cote_2019.cote_2019_me.toLocaleString('fr-FR'),
cote_actual_ar: democars.cote.cote_2019.cote_2019_ar.toLocaleString('fr-FR'),
cote_actual_epa: democars.cote.cote_2019.cote_2019_epa.toLocaleString('fr-FR'),
perfo_1989: democars.performance.perfo1989,
perfo_2004: democars.performance.perfo2004,
perfo_2014: democars.performance.perfo2014,
perfo_2017: democars.performance.perfo2017,
results: democarauctions.results,
marqueauction: democarauctions.results.marque
}
});
})
.catch(err => {
// handle error.
console.log(err);
})
});
Here is my HTML template:
<div class="results__container--content">
{{#each results}}
<div class="results__container--box">
<h1>{{marqueauction}}</h1>
</div>
{{else}}
<div class="results__container--box">
<p>Aucun résultat d'enchères n'est disponible pour ce modèle.</p>
</div>
{{/each}}
</div>
results is an array, you should loop over the array to get the value of marque
let marqueauction = [];
for(let i =0; i < democarauctions.results.length; i++) {
marqueauction.push(democarauctions.results[i].marque)
}
res.render('demo/fichecar-demo', {
marqueauction: marqueauction
});
You can now render the marque in your HTML by looping over the marqueauction.

getting profile information using passport-linkedin-oauth2

I am using passport-linkedin-oauth to connect to linkedin from node.js. I am getting results in the format :
{ provider: 'linkedin',
id: 'd8UCzVLeQ4',
displayName: 'Sunanda Saha',
name: { familyName: 'Saha', givenName: 'Sunanda' },
emails: [ { value: 'sunanda.saha90#gmail.com' } ],
photos:
[ { value: 'https://media.licdn.com/dms/image/C4D03AQFrIK2nFzHrCA/profile-displayphoto-shrink_100_100/0?e=1535587200&v=beta&t=uSLIk3r-gZ8yxdKK_X3g8M1a4usPXmkLbQbyhvhwu0w' } ],
_raw: '{\n "apiStandardProfileRequest": {\n "headers": {\n "_total": 1,\n "values": [{\n "name": "x-li-auth-token",\n "value": "name:Hhbi"\n }]\n },\n "url": "https://api.linkedin.com/v1/people/d8UCzVLeQ4"\n },\n "distance": 0,\n "emailAddress": "sunanda.saha90#gmail.com",\n "firstName": "Sunanda",\n "formattedName": "Sunanda Saha",\n "headline": "Student at National Institute of Technology Durgapur",\n "id": "d8UCzVLeQ4",\n "industry": "Computer Hardware",\n "lastName": "Saha",\n "location": {\n "country": {"code": "in"},\n "name": "Durgapur Area, India"\n },\n "numConnections": 43,\n "numConnectionsCapped": false,\n "pictureUrl": "https://media.licdn.com/dms/image/C4D03AQFrIK2nFzHrCA/profile-displayphoto-shrink_100_100/0?e=1535587200&v=beta&t=uSLIk3r-gZ8yxdKK_X3g8M1a4usPXmkLbQbyhvhwu0w",\n "pictureUrls": {\n "_total": 1,\n "values": ["https://media.licdn.com/dms/image/C4D00AQHRGUNS3tX9nA/profile-originalphoto-shrink_900_1200/0?e=1530086400&v=beta&t=OpBcFgQZf3T6itjXiRUruXYwBmJ0E-Lth3Vk-HwSMT8"]\n },\n "positions": {"_total": 0},\n "publicProfileUrl": "https://www.linkedin.com/in/sunanda-saha-b02098144",\n "relationToViewer": {"distance": 0},\n "siteStandardProfileRequest": {"url": "https://www.linkedin.com/profile/view?id=AAoAACLlVTIBohmGBTmRaxEcHqMt7a-RpvQakIE&authType=name&authToken=Hhbi&trk=api*a5259465*s5577785*"}\n}',
_json:
{ apiStandardProfileRequest:
{ headers: [Object],
url: 'https://api.linkedin.com/v1/people/d8UCzVLeQ4' },
distance: 0,
emailAddress: 'sunanda.saha90#gmail.com',
firstName: 'Sunanda',
formattedName: 'Sunanda Saha',
headline: 'Student at National Institute of Technology Durgapur',
id: 'd8UCzVLeQ4',
industry: 'Computer Hardware',
lastName: 'Saha',
location: { country: [Object], name: 'Durgapur Area, India' },
numConnections: 43,
numConnectionsCapped: false,
pictureUrl: 'https://media.licdn.com/dms/image/C4D03AQFrIK2nFzHrCA/profile-displayphoto-shrink_100_100/0?e=1535587200&v=beta&t=uSLIk3r-gZ8yxdKK_X3g8M1a4usPXmkLbQbyhvhwu0w',
pictureUrls: { _total: 1, values: [Array] },
positions: { _total: 0 },
publicProfileUrl: 'https://www.linkedin.com/in/sunanda-saha-b02098144',
relationToViewer: { distance: 0 },
siteStandardProfileRequest:
{ url: 'https://www.linkedin.com/profile/view?id=AAoAACLlVTIBohmGBTmRaxEcHqMt7a-RpvQakIE&authType=name&authToken=Hhbi&trk=api*a5259465*s5577785*' } } }
To get the information I am using
newUser.linkedin.name = profile.displayName;
newUser.linkedin.email = profile.emailAddress;
newUser.linkedin.location = profile.location;
I am getting the name but not email and location. How to get the other two. And also the gender.
According to the json you provided it should give you your required info in the following formate.
newUser.linkedin.name = jsonYouGave.formattedName;
newUser.linkedin.email = jsonYouGave.emailAddress;
newUser.linkedin.location = jsonYouGave.location.name;
jsonYouGave is the json which you updated in your answer.

Mongoose/MongoDb getting error geoNear is not a function

This is my controller file locations.js
var mongoose = require('mongoose');
var Loc = mongoose.model('location');
module.exports.locationsListByDistance = function(req, res) {
var lng = parseFloat(req.query.lng);
var lat = parseFloat(req.query.lat);
var point = {
type: "Point",
coordinates: [lng, lat]
};
var geoOptions = {
spherical: true,
maxDistance: 1000
};
Loc.geoNear(point, geoOptions, function (err, results, stats) {
console.log(results);
});
};
My model file locations.js
var mongoose = require('mongoose');
var reviewSchema = new mongoose.Schema({
author: String,
rating: {
type: Number,
required: true,
min: 0,
max: 5
},
reviewText: String,
createdOn: {
type: Date,
"default": Date.now
}
});
var openingTimeSchema = new mongoose.Schema({
days: {
type: String,
required: true
},
opening: String,
closing: String,
closed: {
type: Boolean,
required: true
}
});
var locationSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
address: String,
rating: {
type: Number,
"default": 0,
min: 0,
max: 5
},
facilities: [String],
// Always store coordinates longitude, latitude order.
coords: {
type: [Number],
index: '2dsphere'
},
openingTimes: [openingTimeSchema],
reviews: [reviewSchema]
});
mongoose.model('location', locationSchema, 'locations');
Whenever I run http://localhost:3000/api/locations?lng=-0.9690884&lat=51.455041 I get error geoNear is not a function
TypeError: Loc.geoNear is not a function
at module.exports.locationsListByDistance (/home/shackers/Projects/mean/loc8r/app_api/controllers/locations.js:51:7)
at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5)
at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5)
at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12)
at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:174:3)
at router (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:47:12)
at Layer.handle [as handle_request] (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:317:13)
at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:335:12)
at next (/home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:275:10)
at /home/shackers/Projects/mean/loc8r/node_modules/express/lib/router/index.js:635:15
This are versions of dependencies i am using:
node : 8.9.3 npm : 5.5.1 express : 4.15.5 mongoose : 5.0.0
mongoDb : 3.6.1
router.get('/', () => {
Loc.aggregate([
{
$geoNear: {
near: 'Point',
distanceField: "dist.calculated",
maxDistance: 100000,
spherical: true
}
}
]).then(function(err, results, next){
res.send();
}).catch(next);
});
Ref:- https://docs.mongodb.com/manual/reference/command/geoNear/
This error is happening because .geoNear used to be supported, but is no longer supported as of Mongoose 5, which uses the Node MongoDB v3 driver.
The issue is documented in the Migrating to Mongoose 5 document, which in turn links to the MongoDB 3 drive release notes which provides this statement about recommend replacements:
The functionality of the geoNear command is duplicated elsewhere in the language, in the $near/$nearSphere query operators on unsharded collections, and in the $geoNear aggregation stage on all collections.
Effectively, the official docs are endorsing kind of use of $geoNear documented in other answers.
I'm having the exact same problem and I've abandoned the approach I was using before (which looks like you were having too). The following is an alternative that does not throw an error and should give you the same result you were after using Loc.geoNear:
Loc.aggregate(
[
{
'$geoNear': {
'near': point,
'spherical': true,
'distanceField': 'dist',
'maxDistance': 1000
}
}
],
function(err, results) {
// do what you want with the results here
}
)
Apparently I'm in the same book (Getting Mean, Manning) and running into roughly the same issues. This seems to work for me:
var mongoose = require('mongoose');
var Loc = mongoose.model('Location');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
var theEarth = (function() {
console.log('theEarth');
var earthRadius = 6371; // km, miles is 3959
var getDistanceFromRads = function(rads) {
return parseFloat(rads * earthRadius);
};
var getRadsFromDistance = function(distance) {
return parseFloat(distance / earthRadius);
};
return {
getDistanceFromRads: getDistanceFromRads,
getRadsFromDistance: getRadsFromDistance
};
})();
/* GET list of locations */
module.exports.locationsListByDistance = function(req, res) {
console.log('locationsListByDistance:');
var lng = parseFloat(req.query.lng);
var lat = parseFloat(req.query.lat);
var maxDistance = parseFloat(req.query.maxDistance);
var point = {
type: "Point",
coordinates: [lng, lat]
};
console.log('point: ' + point)
var geoOptions = {
spherical: true,
maxDistance: theEarth.getRadsFromDistance(maxDistance),
num: 10
};
console.log('geoOptions: ' + geoOptions);
if ((!lng && lng!==0) || (!lat && lat!==0) || ! maxDistance) {
console.log('locationsListByDistance missing params');
sendJSONresponse(res, 404, {
"message": "lng, lat and maxDistance query parameters are all required"
});
return;
} else {
console.log('locationsListByDistance running...');
Loc.aggregate(
[{
'$geoNear': {
'near': point,
'spherical': true,
'distanceField': 'dist.calculated',
'maxDistance': maxDistance
}
}],
function(err, results) {
if (err) {
sendJSONresponse(res, 404, err);
} else {
locations = buildLocationList(req, res, results);
sendJSONresponse(res, 200, locations);
}
}
)
};
};
var buildLocationList = function(req, res, results) {
console.log('buildLocationList:');
var locations = [];
results.forEach(function(doc) {
locations.push({
distance: doc.dist.calculated,
name: doc.name,
address: doc.address,
rating: doc.rating,
facilities: doc.facilities,
_id: doc._id
});
});
return locations;
};
returns a result list similar to such:
[
{
"distance": 0,
"name": "Rathaus",
"address": "Markt",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e503e"
},
{
"distance": 61.77676881925853,
"name": "Haus Löwenstein",
"address": "",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e5045"
},
{
"distance": 63.03445976427102,
"name": "Goldener Schwan",
"address": "Markt 37",
"rating": 0,
"facilities": [
"restaurant"
],
"_id": "5a9366517775811a449e5052"
},
{
"distance": 66.60375653163021,
"name": "Klein Printenbäckerei",
"address": "Krämerstraße 12",
"rating": 0,
"facilities": [
"supermarket"
],
"_id": "5a9366517775811a449e504d"
},
{
"distance": 74.91278395082011,
"name": "Couven-Museum",
"address": "Hühnermarkt 17",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e5042"
},
{
"distance": 132.2939512054143,
"name": "Cathedral Treasury",
"address": "Johannes-Paul-II.-Straße",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e503d"
},
{
"distance": 152.11867357742042,
"name": "Aachen Cathedral",
"address": "Domhof 1",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e503c"
},
{
"distance": 155.92015153163268,
"name": "International Newspaper Museum",
"address": "Pontstraße 13",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e5040"
},
{
"distance": 175.0857109968383,
"name": "Nobis Printen",
"address": "Münsterplatz 3",
"rating": 0,
"facilities": [
"supermarket"
],
"_id": "5a9366517775811a449e504c"
},
{
"distance": 179.32348875834543,
"name": "Grashaus",
"address": "Fischmarkt",
"rating": 0,
"facilities": [
"museum"
],
"_id": "5a9366517775811a449e5044"
},
{
"distance": 189.8675948747873,
"name": "Maranello",
"address": "Pontstraße 23",
"rating": 0,
"facilities": [
"restaurant"
],
"_id": "5a9366517775811a449e5057"
},
{
"distance": 198.2239741555585,
"name": "Carlos I",
"address": "Rennbahn 1",
"rating": 0,
"facilities": [
"restaurant"
],
"_id": "5a9366517775811a449e5055"
}
]
Not sure how accurate it is - got a list of addresses loaded and not 100% sure what's close to what in the random mess... but it returns a list and I'll test correctness somehow at some point.
I found the solution. Just downgrade mongoose and install version 4.9.1. Latest release of mongoose does not support Loc.geoNear
npm remove mongoose
npm install mongoose#4.9.1
More straightforward IMO, than the previous two answers in the Grider's course is:
index(req, res, next) {
const { lng, lat } = req.query;
Driver.find({
'geometry.coordinates': {
$nearSphere: {
$geometry: {
type: 'Point',
coordinates:[parseFloat(lng), parseFloat(lat)]
},
$maxDistance: 200000,
},
}
})
.then(drivers => res.send(drivers))
.catch(next);
}
This is in the spirit of the original definition he gives and uses the new functions which do the same thing as the old geoNear, except they've split out the spherical and non-spherical versions now. You'll need:
beforeEach((done) => {
const { drivers } = mongoose.connection.collections;
drivers.drop()
.then(() => drivers.createIndex({ 'geometry.coordinates': '2dsphere' }))
.then(() => done())
.catch(() => done());
};
In the test helper as mentioned before.
I think you're looking for this, please correct me if there is mistake there.
module.exports.locationsListBydistance = function (req, res) {
var lng = parseFloat(req.query.lng);
var lat = parseFloat(req.query.lat);
Loc.aggregate(
[{
$geoNear: {
'near': {'type':'Point', 'coordinates':[lng, lat]},
'spherical': true,
'maxdistance': theEarth.getRadsFromDistance(20),
'num':10,
'distanceField': 'dist'
}
}
], function(err, results) {
var locations = [];
console.log(results);
results.forEach(function (doc) {
locations.push({
distance: theEarth.getDistanceFromRads(doc.dist),
name: doc.name,
address: doc.address,
facilities: doc.facilities,
rating: doc.rating,
_id: doc._id
});
});
sendJsonResponse(res, 200, locations);
});
};
router.get('/',function(req,res,next){
Loc.aggregate([
{
$geoNear: {
near: {type:'Point', coordinates:[parseFloat(req.query.lng), parseFloat(req.query.lat)]},
distanceField: "dist.calculated",
maxDistance: 1000,
spherical: true
}
}
]).then(function(Locs){
res.send(Locs)
})
})
Model.geoNear() has been removed because the MongoDB driver no longer supports it
Model.geoNear() has been removed because the MongoDB driver no longer supports it, so you should use the aggregation method for example :
for mongoose v4.2:
my code was like this :
Modal.geoNear(
{type: 'Point', coordinates: [parseFloat(req.query.lng), parseFloat(req.query.lat)]},
{maxDistance: 100000, spherical: true}
)
.then(results => {
res.send(results);
})
for the latest mongoose version :
the code is :
Modal.aggregate([{
$geoNear: {
near: {
type: 'Point',
coordinates: [parseFloat(req.query.lng), parseFloat(req.query.lat)]
},
distanceField: 'distance',
maxDistance: 100000,
spherical: true
}
}])
.then(results => {
res.send(results);
})
Hope i answer or solve your problem, happy coding :D
The answer given by user : phao5814 is quite right I tried it out and must say It worked out well for me
index(req, res, next)
{
const { lng, lat } = req.query;
Driver.aggregate([
{
'$geoNear': {
"near": { 'type': 'Point',
'coordinates': [parseFloat(lng), parseFloat(lat)] },
"spherical": true,
"distanceField": 'dist',
"maxDistance": 200000
}
}
])
.then(drivers => res.send(drivers))
.catch(next);
}

How to set allowSorting false for particular column after IgGrid getting loaded?

I have globalized this below Grid initialization as a common function. I have used sorting as default for all columns. I need to set "allowSorting" for particular column after IgGrid getting loaded. I have added a sample code here. I am unable to set "allowSorting" in this variable "colSettings".
$("#gridSorting").igGrid({
primaryKey: "ProductID",
columns: [
{ headerText: "Product ID", key: "ProductID", dataType: "number" },
{ headerText: "Product Name", key: "Name", dataType: "string", template: '<a id="name${ProductID}">${Name}</a>' },
{ headerText: "Product Number", key: "ProductNumber", dataType: "string", template: '<input type="text" class="txtBox" id="${ProductID}" value="" />' },
{ headerText: "Product Key", key: "ProductKey", dataType: "string", template: '<input type="text" class="txtBox1" maxlength="4" id="${ProductKey}" value="${ProductKey}" />' }
],
features: [
{
name: "RowSelectors",
enableCheckBoxes: true,
enableRowNumbering: false
},
{
name: "Selection",
mode: 'row',
multipleSelection: true
},
{
name: "Sorting",
type: "local"
}
],
width: "500px",
dataSource: products
});
var colSettings = [
{
columnKey: 'ProductID',
allowSorting: true
},
{
columnKey: 'Name',
allowSorting: true
},
{
columnKey: 'ProductNumber',
allowSorting: false
}
];
$("#gridSorting").igGridSorting("option", "columnSettings", colSettings);
Please advise me how to set "allowSorting: false" for particular column in this example.

Resources