How to implement Ngx-Datatable server side pagination if pagination is not zero based - angular5

I am trying to implement server-side pagination in angular 5 based web app.
Problem is the API requires pagination to start from index 1 whereas the library "ngx-datatable" pagination is 0 based.
Here's my current implementation:
mycomponent.html
<ngx-datatable
#usersTable
class='material text-centered'
[rows]='users'
[columns]="usersColumn"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="'auto'"
[externalPaging]="true"
[offset]="page.offset"
[limit]="page.pageSize"
[count]="page.totalElements"
(page)='fetchList($event)'>
</ngx-datatable>
mycomponent.ts
page: any = {
offset: 0,
pageNumber: 1,
pageSize: 10,
totalElements: '',
sortBy: "id",
sortOrder: "desc"
};
ngOnInit() {
this.usersColumn = [
{
name: 'S.NO',
prop: 'sno'
},
{
name: 'First Name',
prop: 'firstName'
},
{
name: 'Last Name',
prop: 'lastName'
},
{
name: 'Email',
prop: 'email',
width: 200
},
{
name: 'Action',
cellTemplate: this.action
}
];
this.fetchList({ offset: 0 });
}
generateSerialNo(pageNo, size, i) {
const index = i + 1;
return pageNo == 1 ? index : (pageNo - 1) * size + index;
}
fetchList(pageInfo) {
this.page.pageNumber = pageInfo.offset + 1;
const { pageNumber, pageSize, sortBy, sortOrder } = this.page);
this.ListService.fetchList(pageNumber, pageSize, sortBy, sortOrder).subscribe(
success => {
if (success && !success['isError']) {
const responseObj = success['responseObject'];
if (responseObj) {
const List = responseObj.content || [];
const { totalElements } = responseObj;
this.users = List.map((item, i) => {
// set serial no. for user in current iteration
const serialNumber = this.generateSerialNo(this.page.pageNumber, this.page.pageSize, i);
return { sno: serialNumber, ...item };
});
this.page.totalElements = totalElements;
}
} else {
this.toastr.error(success['message'], 'Oops!');
}
},
errorResp => {
const error = errorResp['error'];
this.toastr.error(error['message'], 'Oops!');
}
);
Now Say, there's an action column in the table which consists of buttons to block or unblock user which on click calls a function as follows:
blockUnblockUser(toBlock: boolean) {
this.ListService.blockUnblockUser(toBlock, this.selectedUser.id).subscribe(
success => {
if (success && !success['isError']) {
this.fetchList(this.page);
this.utilService.closeModal();
this.toastr.success(success['message'], 'Success!');
} else {
this.utilService.closeModal();
this.toastr.error(success['message'], 'Oops!');
}
},
errorResp => {
const error = errorResp['error'];
this.utilService.closeModal();
this.toastr.error(error['message'], 'Oops!');
});
}
Here's the problem. On page load, I get my list for page number one and page number one is the active page. Now Click on page number 2, in the request param of API this is what goes:
list?pageNumber=2&pageSize=10&sortBy=id&sortOrder=desc
and in response, I get 3 data for page number 2 with 3 items.
Now if I click on the action button to either block or unblock particular user this is what sent in request params:
list?pageNumber=1&pageSize=10&sortBy=id&sortOrder=desc
and in response, I get data based on the page number 1 with 10 items.
but this time the active page in pagination is still "2"?
Please let me know where I am making mistake and how do I fix this. The backend team cannot make the pagination zero-based index for some reason.

Related

How to search inside an array property of an index

I have to retrieve a list of apps that have certain string inside of an array property:
const users = algoliaClient.initIndex('apps')
return users.search('myId123', { hitsPerPage: 50 });
The apps objects are just like this:
{categories: ['an', 'interesting', 'category']}
How can I search the index for items with a categories property that contains any item in an array of strings?
const search = ["cat1", "cat2", "cat3"];
const apps = [
{
name: "app1",
categories: ["cat1", "cat5"],
},
{
name: "app2",
categories: ["cat2", "cat4"],
},
];
is that what you mean ? if so :
var myIndexes = [];
apps.forEach((app, index) => {
search.every((search) => {
if (app.categories.includes(search)) {
myIndexes.push(index);
return false; // return false here is for stoping the EVERY loop
} else {
return true; // keep running the EVERY loop
}
});
});

Error: Exception in HostFunction: Attempting to create an object of type 'sets' with an existing primary key value '6' in react native

I'm trying to store history of workout in realm, my addHistory function looks like this
export function addHistory(workout, exercise, sets, _id) {
console.log({
workout,
exercise,
sets,
_id,
});
if (
_id !== undefined &&
workout !== undefined &&
exercise !== undefined &&
sets !== undefined
) {
// return console.log("HISTORY ", { workout, exercise, sets, _id });
return realm.write(() => {
return realm.create("workoutData", {
_id: _id,
exercise,
workout,
sets,
workoutDate: new Date(Date.now()),
});
});
} else {
alert("History is incomplete");
}
}
Schema of the workoutData is as follows:
exports.workoutData = {
name: "workoutData",
primaryKey: "_id",
properties: {
_id: "int",
workout: "workouts",
exercise: "exercise",
workoutDate: "date",
sets: "sets[]",
},
};
Now when I add sets and click on finishWorkoutHandler the logic works fine before the addHistory function but when addHistory is executed it throws the error as stated in the question.
//finish workout handler
const finishWorkoutHandler = () => {
if (sets.length == 0) {
return;
}
let setsFromRealm = realm.objects("sets");
let workoutData = realm.objects("workoutData");
let setsArray = [];
exercises.forEach((exercise) => {
sets
.filter((items) => items.exercise._id == exercise._id)
.forEach((sets) => {
let _id = 0;
if (setsFromRealm.length > 0) {
_id = realm.objects("sets").max("_id") + 1;
}
addSet(
sets.name,
parseInt(sets.weight),
parseInt(sets.reps),
parseInt(sets.rmValue),
sets.isHeighest,
sets.exercise,
_id,
sets.profile,
sets.failedSet,
sets.warmupSet,
sets.notes
);
let indiSet = {
name: sets.name,
weight: parseInt(sets.weight),
reps: parseInt(sets.reps),
rmValue: parseInt(sets.rmValue),
isHeighest: sets.isHeighest,
_id: _id,
profile: sets.profile,
failedSet: sets.failedSet,
warmupSet: sets.warmupSet,
notes: sets.notes,
createdDate: new Date(Date.now()),
};
setsArray.push(indiSet);
});
let workoutDataId = 0;
let setsArrcopy = setsArray;
console.log("SETS ", realm.objects("sets"));
console.log("SETS ", setsArrcopy);
if (workoutData.length > 0) {
workoutDataId = realm.objects("workoutData").max("_id") + 1;
}
**WORKING AS EXPECTED TILL HERE**
// problem lies here
addHistory(params.workout, exercise, setsArrcopy, workoutDataId);
});
dispatch(setsEx([]));
goBack();
};
the structure of setsArrCopy containing sets is as follows
[
({
_id: 6,
createdDate: 2022-09-29T16:27:06.128Z,
failedSet: false,
isHeighest: false,
name: "Thai",
notes: "",
profile: [Object],
reps: 12,
rmValue: 64,
warmupSet: false,
weight: 56,
},
{
_id: 7,
createdDate: 2022-09-29T16:27:06.151Z,
failedSet: false,
isHeighest: false,
name: "Thsi 3",
notes: "",
profile: [Object],
reps: 10,
rmValue: 75,
warmupSet: false,
weight: 66,
})
];
the logic is also working fine in terms of assigning new ids to the sets being added in a loop. But somehow its throwing error when passing setArrCopy to addHistory function. Although its an array of sets not a single object?

Wait until API fully loads before running next function -- async/await -- will this work?

I am a beginner with Javascript with a bit of knowledge of VueJs. I have an array called tickets. I also have a data api returning two different data objects (tickets and user profiles).
The tickets have user ids and the user profiles has the ids with names.
I needed to create a method that looks at both of that data, loops through it, and assigns the full name of the user to the view.
I was having an issue where my tickets object were not finished loading and it was sometimes causing an error like firstname is undefined. So, i thought I'd try and write an async/await approach to wait until the tickets have fully loaded.
Although my code works, it just doesn't "feel right" and I am not sure how reliable it will be once the application gets larger.
Can I get another set of eyes as to confirmation that my current approach is OK? Thanks!
data() {
return {
isBusy: true,
tickets: [],
userProfiles: [],
}
},
created() {
this.getUserProfiles()
this.getTickets()
},
methods: {
getUserProfiles: function() {
ApiService.getUserProfiles().then(response => {
this.userProfiles = response.data
})
},
getTickets() {
ApiService.getTickets().then(response => {
this.tickets = response.data
this.assignNames(this.tickets)
this.isBusy = false
})
},
// lets wait until the issues are loaded before showing names;
async assignNames() {
let tickets = await this.tickets
var i
for (i = 0; i < this.tickets.length; i++) {
if (tickets[i].assigned_to !== null) {
const result = this.userProfiles.filter(profile => {
return profile.uid == tickets[i].assigned_to
})
tickets[i].assigned_to = result[0].firstname + ' ' + result[0].lastname
}
}
}
}
}
</script>
There are several ways you could do this. Here is the one I prefer without async/await:
created() {
this.load();
},
methods: {
getUserProfiles: function() {
return ApiService.getUserProfiles().then(response => {
this.userProfiles = response.data
})
},
getTickets() {
return ApiService.getTickets().then(response => {
this.tickets = response.data
})
},
load() {
Promise.all([
this.getUserProfiles(),
this.getTickets()
]).then(data => {
this.assignNames();
this.isBusy = false;
});
},
assignNames(){
const tickets = this.tickets;
for (let i = 0; i < this.tickets.length; i++) {
if (tickets[i].assigned_to !== null) {
const result = this.userProfiles.filter(profile => {
return profile.uid == tickets[i].assigned_to
})
tickets[i].assigned_to = result[0].firstname + ' ' + result[0].lastname
}
}
}
}

Vue.js with iView Table - accessing Elasticsearch search response object

I am using the iView UI kit table in my Vue.js application that consumes an Elasticsearch API with axios. My problem is that I just can't seem to get to access the nested search response object, which is an array list object. I only get to access the 1st level fields, but not the nested ones. I don't know how to set the table row key in the iView table.
This is how my axios call and mapper methods look like:
listObjects(pageNumber){
const self = this
self.$Loading.start()
self.axios.get("/api/elasticsearch/")
.then(response => {
self.ajaxTableData = self.mapObjectToArray(response.data);
self.dataCount = self.ajaxTableData.length;
if(self.ajaxTableData.length < self.pageSize){
self.tableData = self.ajaxTableData;
} else {
self.tableData = self.ajaxTableData.slice(0,self.pageSize);
}
self.$Loading.finish()
})
.catch(e => {
self.tableData = []
self.$Loading.error()
self.errors.push(e)
})
},
mapObjectToArray(data){
var mappedData = Object.keys(data).map(key => {
return data[key];
})
return mappedData
},
The iView table columns look like this:
tableColumns: [
{
title: 'Study Date',
key: 'patientStudy.studyDate',
width: 140,
sortable: true,
sortType: 'desc'
},
{
title: 'Modality',
key: "generalSeries.modality",
width: 140,
sortable: true
},
...
]
The (raw) Elasticsearch documents look like this:
[
{ "score":1, "id":"3a710fa2c1b3f6125fc168c9308531b59e21d6b3",
"type":"dicom", "nestedIdentity":null, "version":-1, "fields":{
"highlightFields":{
},
"sortValues":[
],
"matchedQueries":[
],
"explanation":null,
"shard":null,
"index":"dicomdata",
"clusterAlias":null,
"sourceAsMap":{
"generalSeries":[
{
"seriesInstanceUid":"999.999.2.19960619.163000.1",
"modality":"MR",
"studyInstanceUid":"999.999.2.19960619.163000",
"seriesNumber":"1"
}
],
"patientStudy":[
{
"studyDate":"19990608"
}
]
}
}
]
And this is how the consumed object looks like:
As you can see, the fields I need to access are within the "sourceAsMap" object, and then nested in arrays.
How can I provide the iView table cell key to access them?
UPDATE:
I now "remapped" my Elasticsearch object before displaying it in the Vue.js table, and it works now. However, I don't think that the way I did it is very elegant or clean....maybe you can help me to do it in a better way. This is my method to remap the object:
getData(data){
let jsonMapped = []
for(let i = 0; i < data.length; i++){
let id = {}
id['id'] = data[i].id
let generalData = data[i]['sourceAsMap']['generalData'][0]
let generalSeries = data[i]['sourceAsMap']['generalSeries'][0]
let generalImage = data[i]['sourceAsMap']['generalImage'][0]
let generalEquipment = data[i]['sourceAsMap']['generalEquipment'][0]
let patient = data[i]['sourceAsMap']['patient'][0]
let patientStudy = data[i]['sourceAsMap']['patientStudy'][0]
let contrastBolus = data[i]['sourceAsMap']['contrastBolus'][0]
let specimen = data[i]['sourceAsMap']['specimen'][0]
jsonMapped[i] = Object.assign({}, id, generalData, generalSeries, generalImage, generalEquipment, patient,
patientStudy, contrastBolus, specimen)
}
return jsonMapped
},
The result is this:
Even though it now works, but how can I optimize this code?
A few functions can help you with your situation
let data = [{
key1: ['k1'],
key2: ['k2'],
key3: [{
subKey1: 'sk1',
subKey2: ['sk2'],
subObject: [{ name: 'John', surname: 'Doe' }],
items: [1, 2, 3, 5, 7]
}]
}];
function mapIt(data) {
if (isSingletonArray(data)) {
data = data[0];
}
for(const key in data) {
if (isSingletonArray(data[key])) {
data[key] = mapIt(data[key][0]);
} else {
data[key] = data[key];
}
}
return data;
}
function isSingletonArray(obj) {
return typeof obj === 'object' && Array.isArray(obj) && obj.length === 1;
}
console.log(mapIt(data));
Outputs:
{
key1: 'k1',
key2: 'k2',
key3: {
subKey1: 'sk1',
subKey2: 'sk2',
subObject: { name: 'John', surname: 'Doe' },
items: [ 1, 2, 3, 5, 7 ]
}
}
You can check it in your browser. mapIt unwraps the singleton arrays into objects or primitives.
But I recommend you to watch out special elastic client libraries for javascript. It could save you from writing extra code.

How to chain get and map the result with lodash?

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!
I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>