Custom pipe doesn't work properly in Angular2 - sorting

The pipe has to sort an array of objects by the name property.
sort-by.pipe.ts:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'sortBy'
})
export class SortByPipe implements PipeTransform {
private name: string;
transform(array: Array<any>, args: string[]): Array<any> {
array.sort((a: any, b: any) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
return 1;
} else {
return 0;
}
});
return array;
}
}
app.component.html:
<table>
<tr *ngFor="let elem of _values | sortBy">
<td>{{ elem.name }}</td>
<td>{{ elem.ts }}</td>
<td>{{ elem.value }}</td>
</tr>
</table>
app.module.ts:
//other imports
import { SortByPipe } from './sort-by.pipe';
#NgModule({
declarations: [
SortByPipe
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: []
})
export class AppModule { }
The array of objects:
[{
"name": "t10",
"ts": 1476778297100,
"value": "32.339264",
"xid": "DP_049908"
}, {
"name": "t17",
"ts": 1476778341100,
"value": "true",
"xid": "DP_693259"
}, {
"name": "t16",
"ts": 1476778341100,
"value": "true",
"xid": "DP_891890"
}];
It doesn't sort the objects properly, but also doesn't throw any errors.
Maybe there's something wrong with my files?
Any help appreciated!

The problem is that you are piping the elements and not the array itself.
You should change this
<tr *ngFor="let elem of _values | sortBy">
to this:
<tr *ngFor="let elem of (_values | sortBy)">

Have you tried adding pure: false?
This makes it so that Angular doesn't care if the input is changed or not
#Pipe({
name: "sort",
pure: false
})

Related

How do I render my results using graphql in Vuejs

I am learning graphql and using strapi as a backend nuxt as a front end
I have set up the backend and am now trying to display the results
I have the following code, it is returning the results but I cannot for the life of me figure out how to display just the name field, can you assist
<template>
<div>
<!-- Events are displayed here -->
<div
v-for='organisation in organisations'
:key='organisation.id'
>
test {{ organisation }}
</div>
</div>
</template>
<script>
import gql from "graphql-tag";
export default {
data() {
return {
};
},
apollo: {
organisations: gql`
query organisations {
organisations {
data {
attributes {
name
}
id
}
}
}`
}
};
</script>
returns
test [ { "attributes": { "name": "Organisation 1", "__typename": "Organisation" }, "id": "1", "__typename": "OrganisationEntity" }, { "attributes": { "name": "test2", "__typename": "Organisation" }, "id": "2", "__typename": "OrganisationEntity" } ]
test OrganisationEntityResponseCollection
if i try {{ organisation.name }} it returns no error but nothing displayed, if I try {{ organisation.attributes.name }} i get an error
Thanks
Ah, I should have had
v-for='organisation in organisations.data'
in my v-for, now working

How to sort from latest date and time in datatables

I had a problem on sorting my date and time in my datatable. The actual result of sorting is look like this.
Jan 13, 2021 12:03 PM
Jan 13, 2021 11:30 AM
Jan 13, 2021 09:03 AM
Jan 13, 2021 08:32 PM <-- Must be on top
Jan 13, 2021 06:33 AM
Jan 13, 2021 01:15 PM <-- Must be 2nd on top
The result did not sort based on the latest time. It sort based on the number regardless of AM and PM.
HTML Code
<table class="table table-bordered" id="example" width="100%" cellspacing="0">
<thead>
<th class="text-center">Name</th>
<th class="text-center">Date & Time Filled</th>
</thead>
<tbody>
#foreach($users as $user)
<tr>
<td>{{ $user->name}}</td>
<td>{{ date('M d, Y h:i A', strtotime($user->created_at)) }}</td>
</tr>
#endforeach
</tbody>
</table>
my javascript
$(document).ready(function() {
$('#example').DataTable( {
"order": [[ 1, "desc" ]] //sort code
});
});
controller code in laravel
$users:: User::latest('created_at')->get();
One way is to use the moment.js library, which can be included in your page as follows:
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
Then, for testing purposes I use the following test JSON data. This is stored in a JavaScript variable in my test, but could just as easily be loaded from the server, as you do in your question:
var dataSource = [
{ "user": "Alfa", "created_at": "2021-01-13 12:03:00" },
{ "user": "Bravo", "created_at": "2021-01-13 11:30:00" },
{ "user": "Charlie", "created_at": "2021-01-13 09:03:00" },
{ "user": "Delta", "created_at": "2021-01-13 20:32:00" },
{ "user": "Echo", "created_at": "2021-01-13 06:33:00" },
{ "user": "Foxtrot", "created_at": "2021-01-13 13:15:00" }
];
My test table has two columns:
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>User</th>
<th>DateTime</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Finally, my DataTables definition uses moment.js to create two versions of each date:
a display date: Jan 13, 2021 8:32 PM
a related sort date (the raw JSON data): 2021-01-13 20:32:00
The render function in the DataTable uses the display date for displaying in the HTML table, and it uses the related raw date value for sorting.
$('#example').DataTable( {
"data": dataSource,
"order": [[ 1, "desc" ]],
"columnDefs":[
{ targets: 0, data: "user" },
{ targets: 1, data: "created_at",
render: function ( data, type, row ) {
var datetime = moment(data, 'YYYY-MM-DD HH:mm:ss');
var displayString = moment(datetime).format('MMM DD, YYYY LT');
if ( type === 'display' || type === 'filter' ) {
return displayString;
} else {
return datetime; // for sorting
}
}
}]
} );
} );
The end result is:
Update
Assuming your data is already loaded into the HTML table (e.g. via Laravel) then your DataTables definition can be simplified.
$('#example').DataTable( {
"order": [[ 1, "desc" ]],
"columnDefs":[
{ targets: 1,
render: function ( data, type, row ) {
var datetime = moment(data, 'YYYY-MM-DD HH:mm:ss');
var displayString = moment(datetime).format('MMM DD, YYYY LT');
if ( type === 'display' || type === 'filter' ) {
return displayString;
} else {
return datetime;
}
}
}]
} );

get value from json nonarray fornmat in vue js (backend use laravel)

if i use postman the data to get profile like this
{
"status": "success",
"status_code": 200,
"message": "OK",
"data": {
"id": 2,
"name": "ahsan",
"email": "ahsan#gmail.com",
"email_verified_at": "2019-12-24 08:40:35",
"password": "$2y$10$cr3bRfqZ3Fp3uxnqgInNCugwAdSNury3Nsn6GMl9T36kxT.36GmzS",
"remember_token": null,
"created_at": "2019-12-24 08:40:35",
"updated_at": "2019-12-24 08:40:35",
"member_pin": "xxxxxxx",
"google2fa_secret": null,
"google_2fa enum \"\"Y\"\",\"\"N\"\",": "N",
"reff_id": ""
}
}
how to get value? because the data not array format?
i have tried using v-for all data will be showing not rendering like list array format
You can use Object.keys to get all keys as array and use v-for to get values
var app = new Vue({
el: '#app',
data:{
test:{
"status": "success",
"status_code": 200,
"message": "OK",
"data": {
"id": 2,
"name": "ahsan",
"email": "ahsan#gmail.com",
"email_verified_at": "2019-12-24 08:40:35",
"password": "$2y$10$cr3bRfqZ3Fp3uxnqgInNCugwAdSNury3Nsn6GMl9T36kxT.36GmzS",
"remember_token": null,
"created_at": "2019-12-24 08:40:35",
"updated_at": "2019-12-24 08:40:35",
"member_pin": "xxxxxxx",
"google2fa_secret": null,
"google_2fa enum \"\"Y\"\",\"\"N\"\",": "N",
"reff_id": ""
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div v-for="key in Object.keys(test.data)"><strong>{{key}}</strong> : {{test.data[key]}}</div>
</div>
Another way to loop in vuejs v-for="key,index in test.data"
var app = new Vue({
el: '#app',
data:{
test:{
"status": "success",
"status_code": 200,
"message": "OK",
"data": {
"id": 2,
"name": "ahsan",
"email": "ahsan#gmail.com",
"email_verified_at": "2019-12-24 08:40:35",
"password": "$2y$10$cr3bRfqZ3Fp3uxnqgInNCugwAdSNury3Nsn6GMl9T36kxT.36GmzS",
"remember_token": null,
"created_at": "2019-12-24 08:40:35",
"updated_at": "2019-12-24 08:40:35",
"member_pin": "xxxxxxx",
"google2fa_secret": null,
"google_2fa enum \"\"Y\"\",\"\"N\"\",": "N",
"reff_id": ""
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div v-for="key,index in test.data"><strong>{{key}}</strong> : {{test.data[index]}}</div>
</div>
In your case you just need to store the response data in the data property like user and then simply write following code in your HTML template:
{{ user.email }}
You might not need v-for unless you have multiple users.
Consider the following component as an example (for multiple users).
<template>
<div>
<h1>Users</h1>
<table class="table">
<thead>
<tr>
<th scope="col">Email</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.email">
<td> {{ user.email }} </td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
users:null,
}
},
created() {
this.getUsers(); // Will make Laravel API call
},
methods: {
getUsers(){
this.axios.get("https://reqres.in/api/users").then((response) => {
this.users = response.data.data;
});
}
}
}
</script>

GraphQL resolver with DataLoader and nested endpoints

Adapting the example of GraphQL best practices created by the Apollo Team (https://github.com/apollographql/GitHunt-API/tree/master/api), I'm having hard time to come up with a resolver that would result in a list of Person using DataLoaders.
Here's an example of the api (data from: https://github.com/steveluscher/zero-to-graphql/tree/master/zero-node)
Given the output of /people/ endpoint like:
{
"people": [
{
"username": "steveluscher",
"id": "1",
},
{
"username": "aholovaty",
"id": "2",
},
{
"username": "swillison",
"id": "3",
},
{
"username": "gvr",
"id": "4",
}
]
}
And a person from the endpoint /people/1/
{
"person": {
"last_name": "Luscher",
"username": "steveluscher",
"friends": [
"/people/2/",
"/people/3/"
],
"id": "1",
"email": "steveluscher#fb.com",
"first_name": "Steven"
}
I would like to have a resolver what would give me a list of Person like:
[
{
"person": {
"last_name": "Luscher",
"username": "steveluscher",
"friends": [
"/people/2/",
"/people/3/"
],
"id": "1",
"email": "steveluscher#fb.com",
"first_name": "Steven"
}
},
{
"person": {
"last_name": "Holovaty",
"username": "aholovaty",
"friends": [
"/people/1/",
"/people/4/"
],
"id": "2",
"email": "a.holovaty#django.com",
"first_name": "Adrian"
}
},
...
]
This is what I got so far:
server.js
import { ApiConnector } from './api/connector';
import { People } from './api/models';
import schema from './schema';
export function run() {
const PORT = 3000;
const app = express();
app.use(bodyParser.json());
app.use('/graphql', graphqlExpress((req) => {
const query = req.query.query || req.body.query;
if (query && query.length > 2000) {
throw new Error('Query too large.');
}
const apiConnector = new ApiConnector();
return {
schema,
context: {
People: new People({ connector: apiConnector }),
},
};
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}));
const server = createServer(app);
server.listen(PORT, () => {
console.log(`API Server is now running on http://localhost:${PORT}`);
});
return server;
}
models.js
export class People {
constructor({ connector }) {
this.connector = connector;
}
getPeople() {
return this.connector.get(`/people/`);
}
getPerson(id) {
return this.connector.get(`/people/${id}/`);
}
}
connector.js
const API_ROOT = 'http://localhost:8080';
export class ApiConnector {
constructor() {
this.rp = rp;
this.loader = new DataLoader(this.fetch.bind(this));
}
fetch(urls) {
const options = {
json: true,
resolveWithFullResponse: true,
headers: {
'user-agent': 'Request-Promise',
},
};
return Promise.all(urls.map((url) => {
return new Promise((resolve) => {
this.rp({
uri: url,
...options,
}).then((response) => {
const body = response.body;
resolve(body);
}).catch((err) => {
console.error(err);
resolve(null);
});
});
}));
}
get(path) {
return this.loader.load(API_ROOT + path);
}
And the resolver in the schema would have something like:
const rootResolvers = {
Query: {
people(root, args, context) {
return context.People.getPeople();
},
person(root, { id }, context) {
return context.People.getPerson(id)
}
},
};
Until now I can get the first endpoint /people/ and a person from /people/id/. But how to change it to have a list of person? I'm not quite sure how/where should this code be.
Thanks a lot!
You could change your people resolver to something like the code bellow:
const rootResolvers = {
Query: {
people(root, args, context) {
const list = context.People.getPeople();
if (list && list.length > 0) {
return list.map(item => context.People.getPerson(item.id))
}
},
...
},
};
Ps: You said that you are using dataLoader, so i think your API calls is just being cached, but if it is not the case, you need to implement some cache to avoid calling same endpoints a lot times.

Angular 2: Sort array based on the attribute of the nested object

Following is my JSON data
{
"items": [
{
"id": 26,
"email": "pk#gmail.com",
"firstName": "Poornima ",
"lastName": "karuppu",
"role": "Student",
"studentDetails": {
"discipline": "History",
"currentDegree": "Master",
"currentSemester": 58
},
"fullName": "Poornima karuppu"
},
{
"id": 149,
"email": "na#mail.uni-paderborn.de",
"firstName": "raj",
"lastName": "naga",
"role": "Student",
"studentDetails": {
"discipline": "German Lingustics",
"currentDegree": "Master",
"currentSemester": 5
},
"fullName": "raj naga"
},
{
"id": 134,
"email": "testuser#testapp.de",
"firstName": null,
"lastName": null,
"role": "Student",
"studentDetails": {
"discipline": "History",
"currentDegree": "Master",
"currentSemester": 15
},
"fullName": " "
},
{
"id": 20,
"email": "nn#upb.de",
"firstName": "null",
"lastName": "null",
"role": "Student",
"studentDetails": {
"discipline": "History and Arts",
"currentDegree": "Master",
"currentSemester": 4
},
"fullName": "null null"
},
{
"id": 184,
"email": "pk#mail.upb.de",
"firstName": "Rob",
"lastName": "Pat",
"role": "Student",
"studentDetails": {
"discipline": "Computer Science",
"currentDegree": "Bachelor",
"currentSemester": 25
},
"fullName": "Rob Pat"
},
{
"id": 151,
"email": "nn#gmail.com",
"firstName": null,
"lastName": null,
"role": "Student",
"studentDetails": {
"discipline": "Art",
"currentDegree": "Master",
"currentSemester": 5
},
"fullName": " "
},
{
"id": 3,
"email": "student3#hipapp.de",
"firstName": "Lamija",
"lastName": "Halvadzija",
"role": "Student",
"studentDetails": {
"discipline": "Lingustics",
"currentDegree": "Master",
"currentSemester": 5
},
"fullName": "Lamija Halvadzija"
},
{
"id": 25,
"email": "neuerstudent#hipapp.de",
"firstName": "Rolans",
"lastName": "Mustermann",
"role": "Student",
"studentDetails": {
"discipline": "Linguistics",
"currentDegree": "Bachelor",
"currentSemester": 2
},
"fullName": "Rolans Mustermann"
},
{
"id": 178,
"email": "student123#hipapp.de",
"firstName": null,
"lastName": null,
"role": "Student",
"studentDetails": null,
"fullName": " "
},
{
"id": 140,
"email": "neela_upb#hip.com",
"firstName": "Nilakshi",
"lastName": "Naphade",
"role": "Student",
"studentDetails": null,
"fullName": "NN"
},
{
"id": 40,
"email": "pr#gmail.com",
"firstName": "P",
"lastName": "K",
"role": "Student",
"studentDetails": null,
"fullName": "PK"
}
],
}
Its a students' list that I am dispalying on UI using GET API. I am sorting these records using angular 2 pipe. Following is sort.pipe.ts code:
import { Injectable, Pipe, PipeTransform } from '#angular/core';
import { User } from '../../../core/user/user.model';
#Pipe({
name: 'hipUsersSorter'
})
#Injectable()
export class UsersSorter implements PipeTransform {
transform(users: any, key: string, direction: number): User[] {
if (key !== '' && users !== null) {
users.sort(
(a: any, b: any) => {
if (a[key] < b[key]) {
return -1 * direction;
} else if (a[key] > b[key]) {
return 1 * direction;
} else {
return 0;
}
}
);
}
return users;
}
}
Using this pipe, I am able to sort data based on firstName, lastName and email fields. However, I am not able to sort the records based on nested attributes viz. discipline, currentDegree and currentSemester. Following is HTML template from where I am calling this sort pipe:
<table>
<thead>
<tr>
<th (click)="sort('lastName')">{{ 'last name' | translate }}</th>
<th (click)="sort('firstName')">{{ 'first name' | translate }}</th>
<th (click)="sort('email')">{{ 'email' | translate }}</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of students | hipUsersFilter: query: selectedOption: selectedRole | hipUsersSorter: key: direction
| paginate: { id: 'server', itemsPerPage: 10, currentPage: _page, totalItems: _total }">
<td>{{ user.lastName }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.email }}</td>
<td>{{ user.studentDetails.discipline }}</td>
<td>{{ user.studentDetails.currentDegree }}</td>
<td>{{ user.studentDetails.currentSemester }}</td>
</tr>
</tbody>
</table>
This is my sort function in component:
direction = -1;
sort(value: string) {
this.direction = this.direction * -1;
this.key = value;
}
How can I sort the data based on these nested fields? Can someone please provide their inputs on this issue?
Thanks in advance
I'd create a method passing the obj and the nested 'path', as below:
import { Injectable, Pipe, PipeTransform } from '#angular/core';
import { User } from '../../../core/user/user.model';
#Pipe({
name: 'hipUsersSorter'
})
#Injectable()
export class UsersSorter implements PipeTransform {
transform(users: any, key: string, direction: number): User[] {
if (key && users !== null && users.length > 0) {
users.sort(
(a: any, b: any) => {
const propertyA: number|string = this.getProperty(a, key);
const propertyB: number|string = this.getProperty(b, key);
if (propertyA < propertyB) {
return -1 * direction;
} else if (propertyA > propertyB) {
return 1 * direction;
} else {
return 0;
}
}
);
}
return users;
}
private getProperty (value: { [key: string]: any}, key: string): number|string {
if (value == null || typeof value !== 'object') {
return undefined;
}
const keys: string[] = key.split('.');
let result: any = value[keys.shift()];
for (const key of keys) {
if (result == null) { // check null or undefined
return undefined;
}
result = result[key];
}
return result;
}
}
In your template:
<th (click)="sort('studentDetails.discipline')">{{ 'discipline' | translate }}</th>
<th (click)="sort('studentDetails.currentDegree')">{{ 'degree' | translate }}</th>
<th (click)="sort('studentDetails.currentSemester')">{{ 'semester' | translate }}</th>
Simple demo: DEMO.

Resources