Related
I’ve got a Next.JS app. I want to render a todo feed on the homepage, but also the user page. I'm a bit stuck on how to break down my Prisma queries.
I fetch a big data object using getServerSideProps and pass this to the page component (and using react-query to hydrate and do re-fetching, but not relevant now)
- getRecentTodos (includes todos) for my homepage
- getUserDetailsByName (includes todos) for the user page
export type getRecentTodos = ReturnType<typeof getRecentTodos> extends Promise<
infer T
>
? T
: never
export const getRecentTodos = async (recentItemsAmount = 20) => {
return await prisma.todos.findMany({
where: { done: true },
select: {
id: true,
userId: true,
content: true,
done: true,
createdAt: true,
attachments: true,
todoReplies: {
select: {
id: true,
userId: true,
content: true,
todoReplyLikes: true,
todoId: true,
user: { select: { name: true, displayName: true, image: true } },
},
orderBy: { createdAt: 'asc' },
},
todoLikes: {
select: {
user: true,
},
},
user: {
select: {
name: true,
displayName: true,
image: true,
},
},
},
orderBy: { createdAt: 'desc' },
take: recentItemsAmount,
})
}
export const getUserDetailsByName = async (username: string) => {
return await prisma.user.findUnique({
where: {
name: username,
},
select: {
name: true,
displayName: true,
bio: true,
location: true,
twitter: true,
image: true,
createdAt: true,
todos: {
select: {
id: true,
content: true,
userId: true,
done: true,
updatedAt: true,
createdAt: true,
attachments: true,
user: true,
todoLikes: true,
todoReplies: {
take: 30,
orderBy: { createdAt: 'desc' },
select: {
id: true,
userId: true,
todoId: true,
createdAt: true,
content: true,
user: true,
},
},
},
take: 30,
orderBy: { createdAt: 'desc' },
},
projects: true,
},
})
}
Both queries return ‘todos,’ but they can return it in a slightly different way. The todo feed component expects certain properties to be available
- E.g. displayName on todoReplies
- But on getUserDetailsByName the displayName might not be part of the response or it’s nested one layer deeper or something
How to keep this from getting complex very fast?
You more or less want to select todos in your queries the same way (returning the same and omitting the same, apart of some things like order)
But manually keeping these things in sync over lot’s of queries qet’s complex quickly
Possible solutions?
Should I break the getServerSideProps into multiple fetches?
So instead of one ‘getUserDetailsByName’ which has todos as a relationfield included
fetch user details
fetch todos
This would mean I also have to write more react-query code for refetching etc… because you are dealing with multiple objects. But it does seperate concerns more.
Using Typescript to catch it in my codebase when a function tries to access a property which is not returned from that specific Prisma query? (I’m just now starting to see the possibilities of Typescript for stuff like this)
Should I just standardize the way the todos get created in a prisma query with a function and include that function inside of the Prisma queries? you can include like:
const todoSelect = {
id: true,
userId: true,
content: true,
{.......}
user: {
select: {
name: true,
displayName: true,
image: true,
},
},
}
export type getRecentTodos = ReturnType<typeof getRecentTodos> extends Promise<
infer T
>
? T
: never
export const getRecentTodos = async (recentItemsAmount = 20) => {
return await prisma.todos.findMany({
where: { done: true },
select: todoSelect,
orderBy: { createdAt: 'desc' },
take: recentItemsAmount,
})
}
const userSelect = {
name: true,
{......}
todos: {
select: todoSelect,
take: 30,
orderBy: { createdAt: 'desc' },
},
projects: true,
}
export const getUserDetailsByName = async (username: string) => {
return await prisma.user.findUnique({
where: {
name: username,
},
select: userSelect,
})
}
I am using kaktana-react-lightweight-charts, everything works perfectly until I got e.timeScale(...).unsubscribeVisibleTimeRangeChange is not a function error, as I did not implement it in my code. I do not know why I need this?.. The scenario was, At first my chart will displayed nicely as I wanted, then when I click at other data, it should display new data with same chart but instead I get this error. So I think I will implement unsubscribeVisibleTimeRangeChange function but dont know how..
<Chart
lineSeries={this.getChartData1("line", mainData)}
options={optionMaster}
autoWidth
height={indexMode ? 320 : 240}
onCrosshairMove={(e) => this.createLineChart(e, mainData)}
onClick={(e) => this.openWindow(e, mainData)}
( ....unsubscriberVisibleTimeRangeChange..how to call from here?...)
/>
This is my options
optionMaster: {
alignLabels: true,
localization: {
dateFormat: "dd MMM 'yy",
},
handleScroll: {
mouseWheel: false,
pressedMouseMove: false,
horzTouchDrag: false,
vertTouchDrag: false,
},
handleScale: {
axisPressedMouseMove: false,
mouseWheel: false,
pinch: false,
},
priceScale: {
position: "left",
autoScale: true,
invertScale: true,
alignLabels: false,
borderVisible: false,
borderColor: "#555ffd",
scaleMargins: {
top: 0.3,
bottom: 0.25,
},
},
timeScale: {
fixLeftEdge: true,
lockVisibleTimeRangeOnResize: true,
},
},
Currently I am trying to render a menu with sub menus. As you guys can see I can have sub menus with sub menus too. I was able to create a JavaScript
This is what I have right now
$scope.menu = [
{
icon: 'glyphicon glyphicon-home', title: 'Home', path: '/', tile: false, fn: null, allowAll: true, childs: []
},
{
icon: 'glyphicon glyphicon-home', title: 'Reporting', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Compensation Report', path: '/Reports/Compensation', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Representative Level Report', path: '/Reports/AgentId', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Territory Report', path: '/Reports/Territory', tile: false, fn: null, allowAll: false, childs: [] },
{ title: 'Reporting Access', path: '/Reports/Acces', tile: false, fn: null, allowAll: false, childs: [] }
]
},
{ icon: 'glyphicon glyphicon-home', title: 'Administrator', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Users', path: '', tile: false, fn: null, allowAll: true, childs:
[
{ title: 'Users managment', path: '', tile: false, fn: null, allowAll: true, childs: [] },
{ title: 'Invite user', path: '', tile: false, fn: null, allowAll: true, childs: [] },
{ title: 'Create user account', path: '', tile: false, fn: null, allowAll: true, childs: [] }
]
},
]}];
This is the Javascript I am using to render the menu but it only renders the some menu items so I think I need to render the child sub menus
<nav role="navigation" class="navbar navbar-default navbar-bigfont" style="border-width: 0">
<ul class="nav nav-pills nav-stacked">
<li class="portal-text-19b" ng-repeat="item in menu" ng-class="{active: hightlight === item.title && item.path !== '#'}" ng-show="showItem(item.path, item.allowAll)">
<a href="{{item.path}}" ng-click="item.fn()">
<span class="{{item.icon}}"></span>
{{item.title}}
</a>
</li>
</ul>
</nav>
It's quite simple! Inside your current ng-repeat which is ng-repeat="item in menu", you can have another one of those. Something like,
<li class="portal-text-19b" ng-repeat="item in menu" ...>
<a href="{{item.path}}" ng-click="item.fn()">
<span class="{{item.icon}}"></span>{{item.title}}
</a>
<ul>
<li ng-repeat="child in item.childs">
{{child.title}}
<ul>
<li ng-repeat="child2 in child.childs">
{{child2.title}}
</li>
</ul>
</li>
</ul>
</li>
Now, since this has slightly duplicated HTML, you might want to consider creating a template and use it by having ng-include which would be useful in case you want to make this dynamic till N levels.
working plunker example
I have the following code from angular site:
<form name='form'>
<div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
<pre>{{form | json}}</pre>
</form>
I outputs the following thing below the desired input:
{
"$error": {},
"$name": "form",
"$dirty": true,
"$pristine": false,
"$valid": true,
"$invalid": false,
"$submitted": false
}
Sadly I can't check the validity/ditryness/etc of the input as there is no input property on the form.
Why are the field not present on the form object in angular?
Relevant plunker
Angular version: 1.4.4
It turns out that a property is added to the form object only (relates to reasonable changes only in HTML) when the name attribute of the input is present.
Thus this code (I've added name='firstName' and some basic validation : ng-required="true" for sake of test):
<form name='form'>
<div contentEditable="true"
ng-model="content"
title="Click to edit"
ng-required="true"
name='firstName'> Some </div>
<pre>{{form | json}}</pre>
</form>
Will output the form object with the firstName property related to input:
{
"$error": {},
"$name": "form",
"$dirty": true,
"$pristine": false,
"$valid": true,
"$invalid": false,
"$submitted": false,
"firstName": {
"$viewValue": " Some ",
"$modelValue": " Some ",
"$validators": {},
"$asyncValidators": {},
"$parsers": [],
"$formatters": [],
"$viewChangeListeners": [],
"$untouched": false,
"$touched": true,
"$pristine": false,
"$dirty": true,
"$valid": true,
"$invalid": false,
"$error": {},
"$name": "firstName",
"$options": null
}
}
Relevant plunker
I am using Kendo UI Grid with row filters. i am facing filters options issue. I am using Filterbale.cell.template for filters to display kendo autoComplete.
Issue is as displayed in image autocomplete options are not updating on selecting of one of the filters.
Below is my html
<div ng-controller="VehiclesController" class="my-grid" >
<kendo-grid options="vehiclesGridOption">
</kendo-grid>
</div>
Below is my Controller
$scope.vehiclesGridOption = {
dataSource: {
schema: {
id: "_id",
model: {
fields: {
make: {type: "string"},
model: {type: "string"},
year: {type: "number"}
}
}
},
transport: {
read: function (e) {
vehicleService.vehicles().then(function (response) {
e.success(response);
console.log(response.length);
}).then(function () {
console.log("error happened");
})
}
},
pageSize: 12,
pageSizes: false,
},
sortable: {
mode: "multiple",
allowUnsort: true
},
filterable: {
mode: "row"
},
pageable: {
buttonCount: 5
},
columns: [
{
title: "",
template: '',
width: "3%" // ACTS AS SPACER
},
{
field: "make",
title: "Make",
filterable: {
cell: {
operator: "contains",
template: function (args) {
args.element.kendoAutoComplete({
dataSource: args.dataSource,
dataTextField: "make",
dataValueField: "make",
valuePrimitive: true,
placeholder: "Make",
});
}
}
},
width: "29%",
}, {
field: "model",
filterable: {
cell: {
operator: "contains",
template: function (args) {
console.log(args);
args.element.kendoAutoComplete({
dataSource: args.dataSource,
dataTextField: "model",
dataValueField: "model",
valuePrimitive: true,
placeholder: "Model",
});
}
}
},
title: "Model",
width: "29%",
}, {
field: "year",
title: "Year",
filterable: {
cell: {
template: function (args) {
args.element.kendoAutoComplete({
dataSource: args.dataSource,
dataTextField: "year",
dataValueField: "year",
placeholder: "Year",
suggest: true,
ignoreCase: true,
filter: "gte"
});
}
}
},
width: "29%",
},
{
field: "",
title: "Edit",
template: '<a class=\"k-link text-center grid-edit-btn vehicle-grid-edit-btn\" ui-sref="vehicleDetails({\'id\': \'#=_id #\' })"><span class=\"icon-editpencil icon-grid\"></span></a>',
width: "10%",
}],
};
Below is the Issue if user selects the Make in the first column filter then Model filter should display only selected make models like Honda (make)-> Accord , Civic ..etc but its displaying all unique values irrespective of model filter..
Kendo filter row uses the same dataSource from the grid component, just providing unique values. Since the autocomplete components are initialized when the grid dataSource is empty, they always show all the values.
You can manually filter based on current filter row values.
Firstly, add ids for your coresponding autocomplete components i.e. inside template functions:
args.element.attr('id', 'make');
//<...>
args.element.attr('id', 'model');
//<...>
args.element.attr('id', 'year');
Then add a data bound event to the grid (since autocomplete components do not fire change events when filters are cleared).
$scope.vehiclesGridOption = {
//...
dataBound : function(e) {
setTimeout(function() { //timeout is to make sure value() is already updated
var make = $('#make').data('kendoAutoComplete').value();
if (make) {
$('#model').data('kendoAutoComplete').dataSource.filter({field: 'make', operator: 'eq', value: make });
} else {
$('#model').data('kendoAutoComplete').dataSource.filter({});
}
});
}
}
Or if you also want to filter by "Year" column, it could go like this:
$scope.vehiclesGridOption = {
//...
dataBound: function(e) {
setTimeout(function() { //timeout is to make sure value() is already updated
var make = $('#make').data('kendoAutoComplete').value();
var model = $('#model').data('kendoAutoComplete').value();
if (make) {
$('#model').data('kendoAutoComplete').dataSource.filter({field: 'make', operator: 'eq', value: make });
} else {
$('#model').data('kendoAutoComplete').dataSource.filter({});
}
var yearFilter = {filters: [], logic: 'and'};
if (make) {
yearFilter.filters.push({field: 'make', operator: 'eq', value: make });
}
if (model) {
yearFilter.filters.push({field: 'model', operator: 'eq', value: model });
}
$('#year').data('kendoAutoComplete').dataSource.filter(yearFilter.filters.length ? yearFilter : null);
});
}
}