How to use datasoure in ng2-ya-table? - grails

In the documentation of ng2-ya-table the datasource function is written in this way :
public datasource: any = (request: any): Observable<any> => {
return this.service.getUsers(request);
}
And used like this :
<ng2-ya-table [options]="options" [columns]="columns" [datasource]="datasource" [paging]="paging">
</ng2-ya-table>
I don't want to use this function in this way because I have static
data = [
{
name: 'Patricia',
email: 'Julianne.OConner#kory.org',
username: 'Yes',
},
{
name: 'Chelsey Dietrich',
email: 'Lucio_Hettinger#annie.ca',
username: 'No',
}
]
Is that possible or I am obliged to render observable type?
I tried a lot using static data but in vain
public datasource: any = {
return this.data ;
}
why this function is not working?

Try with:
public datasource: any = (request: any): Observable<any> => {
return Observable.of({
recordsTotal: this.data.length,
recordsFiltered: this.data.length,
data: this.data
});
}
Anyway, you need to perform pagination, sorting and filtering client side (the data source is an Observable in order to perform this operation server side).
For example (pagination only):
public datasource: any = (request: any): Observable<any> => {
let page = (request.start / request.length) + 1;
return Observable.of({
recordsTotal: this.data.length,
recordsFiltered: this.data.length,
data: this.data.slice(request.length * (page - 1), request.length * page)
});
}

I tried:
public datasource: any = (request: any): Observable<any> => {
return Observable.of(this.data);
}
but it causes a cascade of errors starting with:
Ng2YaTableComponent.html:53 ERROR TypeError: Cannot read property
'length' of undefined
If someone can improve this answer perhaps we can find an solution

Related

Response from GraphQL Mutation not working as expected

I am running a GraphQl mutation via a resolver. Its against a Neo4j database. The query is working fine but I am not able to structure the results in a way that they are displayed back in the result. I've tried several different combinations of results and record mapping but I can't get the properties of the returned result set to show in the outputs.
Specifically, you will see in the below examples that the name field in the response (via GraphiQL) is null when I expect it to be set to the value returned.
Resolver:
AddIdiomHasChildIdiom(object, params, ctx, resolveInfo) {
//************************************************************************************************************************************
//** Author: MOS
//** Date: 22/07/2019
//** Description: Add a child Idiom
//************************************************************************************************************************************
//Build the cypher query across multiple lines so that we can adjust the query depending on which parameters are passed
let query = new StringBuilder();
query.appendLine("MATCH (p:entity:Idiom {id:'" + params.from.id + "'}), (c:entity:Idiom {id:'" + params.to.id + "'})")
query.appendLine("MERGE (p)-[r:HAS_CHILD_IDIOM]->(c)")
query.appendLine("RETURN p,c")
console.log(query)
//Execute the query and send the results back to the user
return ctx.driver.session().run(query.toString(), {props:params})
.then(result => {
return {
from: result.records.map(record => { return record.get("p").properties}),
to: result.records.map(record => { return record.get("c").properties})
}
})
.catch(error => {
//ToDo: Error handling code need to go here
})
}
GraphQL Query
mutation{
AddIdiomHasChildIdiom(from:{id:"d94676b0-ac6c-11e9-a7a1-edf120d553ac"},to:{id:"e730a720-ac74-11e9-a45f-df629a6df5e1"})
{
from{
name
}
to{
name
}
}
}
Output:
{
"data": {
"AddIdiomHasChildIdiom": {
"from": {
"name": null
},
"to": {
"name": null
}
}
}
}
Relevant Schema Parts
type Mutation{
AddIdiomHasChildIdiom(
from: _IdiomInput!
to: _IdiomInput!
): _AddIdiomHasChildIdiomPayload
}
type _AddIdiomHasChildIdiomPayload {
from: Idiom
to: Idiom
}
input _IdiomInput {
id: ID!
}
type _AddIdiomHasChildIdiomPayload {
from: Idiom
to: Idiom
}
type Idiom {
id: ID
name: String
type: IdiomType
description: String
lifecycle: IdiomLifecycle
quality: Float
numStates: Int
numChildIdioms: Int
hasChildIdiom: [Idiom]
first: Int
offset: Int
orderBy: [_IdiomOrdering]
}
The problem in your code isthat you have mistmatch type between your graphql schema and the implementation of your resolver.
Your mutation query should return this :
type _AddIdiomHasChildIdiomPayload {
from: Idiom
to: Idiom
}
And your resolver returns this :
return {
from: result.records.map(record => { return record.get("p").properties}),
to: result.records.map(record => { return record.get("c").properties})
}
Here you are using a map function, and its result is an array.
So you are returning something like that :
{
from : [ { id:'', name:'', ... } ]
to : [ { id:'', name:'', ... } ]
}
And [ { id:'', name:'', ... } ] doesn't validate the Idiom type of your schema, it's an array : Idiom[].
So you need to change your resolver to return just one element for from & to.
And moreover, your implementation is weird : you are doing two loop against the result of Neo4j, due to the use of two map and you don't close the session.
You should try something like that :
const session = ctx.driver.session();
return session.run(query.toString(), {props:params})
.then(result => {
return result.records.map(record => {
return {
from:record.get("p").properties
to:record.get("c").properties
}
})[0]
})
.catch(error => {
//ToDo: Error handling code need to go here
})
.finally(() => {
session.close();
})
Moreover

InvalidOperationException: Incorrect Content-Type: Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()

I am new to Asp.net MVC Core. I am working on Server-side loading of JQuery Datatables.net using Asp.Net Core MVC Middleware.
I have used this tutorial to learn how to create a handler and then this article to migrate to middleware but are running into some issues that I hope you can help me with.
I have refined using this tutorial
I get error
"InvalidOperationException: Incorrect Content-Type: Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()"
when I run the solution.
Here is my code:
View
<script type="text/javascript">
$(document).ready(function () {
$('#datatable').DataTable({
//"paging": true,
//"ordering": true,
//"info": true,
'columns' : [
{ 'data': 'InsertedDateUtc' },
//{ 'data': 'EventId' },
{ 'data': 'UserId' },
{ 'data': 'Action' },
{ 'data': 'Context' },
{ 'data': 'RecordId' },
{ 'data': 'Property' },
{ 'data': 'OldValue' },
{ 'data': 'NewValue' },
],
'processing': true,
'serverSide': true,
'ajax' : {
'type' : 'POST',
'url' : '../AuditEventData.cs',
//'url': '../APIController/GetAuditEvents'
//'url' : '#Url.Action("GetAuditEvents", "APIController")'
'datatype': 'json',
}
});
});
</script>
Middleware
public class AuditEventData
{
private readonly RequestDelegate _next;
private readonly IDataGet _dataGet;
public AuditEventData(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
string result = null;
int filteredCount = 0;
var draw = httpContext.Request.Form["draw"].FirstOrDefault();
var start = int.Parse(httpContext.Request.Form["start"].FirstOrDefault());
var length = int.Parse(httpContext.Request.Form["length"].FirstOrDefault());
var sortCol = int.Parse(httpContext.Request.Form["columns[" + httpContext.Request.Form["order[0][column]"].FirstOrDefault() + "][name]"].FirstOrDefault());
var sortDir = httpContext.Request.Form["order[0][dir]"].FirstOrDefault();
var search = httpContext.Request.Form["search[value]"].FirstOrDefault();
try
{
var auditEvents = await _dataGet.GetServerSideAuditEvents(length, start, sortCol, sortDir, search);
filteredCount = auditEvents.Count();
var data = new
{
iTotalRecords = await _dataGet.GetTotalAuditEventCount(),
iTotalDisplayRecords = filteredCount,
aaData = auditEvents
};
result = JsonConvert.SerializeObject(data);
await httpContext.Response.WriteAsync(result);
}
catch (Exception e)
{
await ErrorHandler.HandleException(e);
}
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseAuditEventDataMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<AuditEventData>();
}
}
Startup.cs
app.MapWhen(
context => context.Request.Path.ToString().EndsWith("ViewAudit"),
appBranch =>
{
appBranch.UseAuditEventDataMiddleware();
});
In the middleware class the line
var start = int.Parse(httpContext.Request.Form["start"].FirstOrDefault());
gives me the error - the tutorials and Microsoft documentation here seem to indicate that I do not need to use the ".Form" and should be able to just use
var start = int.Parse(httpContext.Request["start"].FirstOrDefault());
however, when I do that, I get this error
cannot apply indexing with [] to an expression of type 'HttpRequest'
I cannot find any examples on how to do this and any help will be appreciated
Thanks
In order to expect to have a Form in your HttpContext.Request you must change your ajax datatype to 'application/x-www-form-urlencoded'. Now whether you want to do that is another question.
From here: https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data

How to handle TypeORM entity field unique validation error in NestJS?

I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();

RelayMutation expects prop to be data fetched by Relay, adding mutation to query not working

I am getting the error:
bundle.js:28169 Warning: RelayMutation: Expected prop `group` supplied to `AddBlock` to be data fetched by Relay. This is likely an error unless you are purposely passing in mock data that conforms to the shape of this mutation's fragment.
It might seem similar to the problem described in this question, but the answer (of making sure the mutation is added to the initial query) is not working as a solution for me. I already have the mutation in the original query.
Here is my relevant code:
export class AddBlock extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation { addBlock }`;
}
getVariables() {
return {
body: this.props.body
};
}
getFatQuery() {
return Relay.QL`
fragment on AddBlock {
newBlockEdge,
group {
blocks
}
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'group',
parentID: this.props.group.id,
connectionName: 'blocks',
edgeName: 'newBlockEdge',
rangeBehaviors: {
'': 'append',
},
}];
}
getOptimisticResponse() {
return {
newBlockEdge: {
node: {
body: this.props.body
}
},
group: {
id: this.props.group.id
}
}
}
static get fragments() {
return {
group: () => Relay.QL`
fragment on GroupNode {
id
}
`,
}
}
}
class Designer extends React.Component {
...
addToBlocks(blocks) {
// Create a mutation to save to the blocks.
Relay.Store.commitUpdate(
new AddBlock({
body: blocks[0].block,
group: this.props.group
})
);
}
...
}
Designer = Relay.createContainer(Designer, {
fragments: {
group: (Component) => Relay.QL`
fragment on GroupNode {
title
visibility
blocks(first: 20) {
edges {
node {
${Block.getFragment('block')}
${UpdateBlockBodyMutation.getFragment('block')}
position
title
}
}
}
${AddBlock.getFragment('group')}
}
`,
}
});
What could I be doing wrong here?
I suspect your mutation fragment isn't actually being used - you should run a test to see that if you add other fields to the AddBlock fragment, you'll find that they aren't being requested...? I'm not 100% sure why (likely something about static get fragments), but not quite sure.
Once you get your mutation fragment actually being used, Relay won't complain anymore since it'll be getting data in the correct way :D

angular JS Put method with ASP.NET MVC

I have my angular controller defined like this for Put and Get method. I m new to angular JS. The get method works as expected but the Put method is not getting called. I followed one of the tutorial and implemented. though in the tutorial, they used REST service URL rather than controller method. I m using MVC controller here not the webAPI one.
public JsonResult GetEmployee(int id)
{
Employee empDetail = emp.GetEmployees().FirstOrDefault(i => i.EmpID == id);
return Json(empDetail, JsonRequestBehavior.AllowGet);
}
public JsonResult PutEmployee(int id, Employee updtEmp)
{
updtEmp.EmpID=id;
int index = emp.GetEmployees().FindIndex(i => i.EmpID == updtEmp.EmpID);
emp.GetEmployees().RemoveAt(index);
emp.GetEmployees().Add(updtEmp);
return Json(emp.GetEmployees(), JsonRequestBehavior.AllowGet);
}
Here is my angular Factory and controller method
myservice.factory('empFactory', function ($resource) {
return $resource('../../Employee/GetEmployee/:EmpID', { EmpID: '#EmpID' },
{
show: { method: 'GET' },
update: { method: 'PUT', params: { Employee: '#employee' } }
});
});
myApp.controller('empDetailController', function ($scope, empFactory, $routeParams) {
$scope.Employee = empFactory.show({ EmpID: $routeParams.EmpID });
$scope.UpdateEmp = function () {
// alert($scope.Employee.FirstName);
var employee=$scope.Employee;
empFactory.update({ EmpID: $routeParams.EmpID, Employee: employee })
};
});
Where did you mention in the MVC controller the method is PUT, Until and unless you mention the method type HttpPut or HttpPost etc., they are treated as HttpGet, Mention the method as PUT method in your MVC controller and try again.
Decorate your MVC PUT method like this :
[HttpPut]
public JsonResult PutEmployee(int id, Employee updtEmp)
{
updtEmp.EmpID=id;
int index = emp.GetEmployees().FindIndex(i => i.EmpID == updtEmp.EmpID);
emp.GetEmployees().RemoveAt(index);
emp.GetEmployees().Add(updtEmp);
return Json(emp.GetEmployees(), JsonRequestBehavior.AllowGet);
}
and factory and controller shouldbe like this :
myservice.factory('empFactory', function ($resource) {
var resource = {
employees:$resource('../../Employee/GetEmployee/:EmpID', { EmpID: '#EmpID' }),
empUpdateService:$resource('../../Employee/PutEmployee/:EmpID', { EmpID: '#EmpID' },{
update: { method: 'PUT', params: { Employee: '#employee' } }
})
};
return resource;
});
myApp.controller('empDetailController', function ($scope, empFactory, $routeParams) {
$scope.Employee = empFactory.employees.get({ EmpID: $routeParams.EmpID });
$scope.UpdateEmp = function () {
// alert($scope.Employee.FirstName);
var employee=$scope.Employee;
empFactory.empUpdateService.update({ EmpID: $routeParams.EmpID, Employee: employee })
};
You wanted to do it in a very good way, but for that you should use WEB Api controllers.
Web Api controller should be like this :
public class Employee : ApiController
{
public EmpModel GetEmployee(int id)
{
Employee empDetail = emp.GetEmployees().FirstOrDefault(i => i.EmpID == id);
return empDetail;
}
public bool PutEmployee(int id, Employee updtEmp)
{
updtEmp.EmpID=id;
int index = emp.GetEmployees().FindIndex(i => i.EmpID == updtEmp.EmpID);
emp.GetEmployees().RemoveAt(index);
emp.GetEmployees().Add(updtEmp);
return emp.PutEmployees(updtEmp);
}
public bool PostEmployee(Employee empModel)
{
// do something
return emp.SaveEmployees(empModel);
}
public bool DeleteEmployee(int id)
{
// do something
return emp.DeleteEmployees(id);
}
}
consider the below resource, that can do four operations. they are get, post, put and delete. :
var resource = {
employee: $resource('../../Employee/:EmpID', { EmpID: '#EmpID' },
{
update: { method: 'PUT' },
});
}
return resource;
use your resource in controllers like this, for get :
$scope.Employee = empFactory.employee.get({ EmpID: $routeParams.EmpID }).$promise;
for post :
$scope.SaveEmp = function (employee) {
empFactory.employee.save({
EmpID: $routeParams.EmpID,
Employee: employee
}, function (response) {
// do something with your success response
})
};
for Delete :
$scope.UpdateEmp = function () {
empFactory.employee.delete({
EmpID: $routeParams.EmpID
}, function (response) {
// do something with your success response
})
};
for update :
$scope.UpdateEmp = function (employee) {
empFactory.employee.update({
EmpID: $routeParams.EmpID,
Employee: employee
}, function (response) {
// do something with your success response
})
};
This is my service with multiple resource statements. Its close what you have initially shared as well
var resource = {
employee:
$resource('../../Employee/GetEmployee/:EmpID', { EmpID: '#EmpID' },
{
show: { method: 'GET' }
}),
empUpdate:
$resource('../../Employee/PutEmployee/:EmpID', { EmpID: '#EmpID', empval: '#empl' }, { update: { method: 'PUT', isArray: true } }),
empDelete:
$resource('../../Employee/DeleteEmployee/:EmpID', { EmpID: '#EmpID' }, { del: { method: 'DELETE', isArray: true } }),
empCreate:
$resource('../../Employee/CreateEmployee', { empval: '#empl' }, { create: { method: 'POST', isArray: true } })
}
return resource;
Now i have another problem that my angular js views are not updating and i posted it in stackoverflow. if you help it would be great
Angular JS CRUD update not refreshing

Resources