Link to another list (not full list, results only meet some condition) when fields in specific colum are clicked - react-admin

Im using
"react": "^16.13.1",
"react-admin": "^3.6.0",
what I try to do is that when specific column's field(list A) is clicked, go to another List B.
for example,
List A has columns (ProductId, ProductModel, Product Version, In Stock, Out of Stock, Disposal)
if 'In stock' fields are clicked, is has to go to List B filtered by that row's product Id and status is 'In stock'.
if 'Out Of stock' fields are clicked, is has to go to List B filtered by that row's product Id and status is 'Out of stock'.
using reference fields for 'In stock' and 'Out of Stock' is not appropriate. because there is no api only for them.
and I tried with hitory.push('List B') but it just show full list of List B.
what should I do this case???

This is actually documented in the react-admin <List> component documentation, section 'Linking to a pre-filtered list' (https://marmelab.com/react-admin/List.html#linking-to-a-pre-filtered-list).
As the filter values are taken from the URL, you can link to a pre-filtered list by setting the query parameter.
For instance, if you have a list of tags, you can display a button for each category to link to the list of posts filtered by that tag:
import * as React from "react";
import Button from '#material-ui/core/Button';
import { Link } from 'react-router-dom';
import { stringify } from 'query-string';
const LinkToRelatedProducts = ({ record }) => {
const translate = useTranslate();
return record ? (
<Button
color="primary"
component={Link}
to={{
pathname: '/posts',
search: stringify({
page: 1,
perPage: 25,
sort: 'id',
order: 'DESC',
filter: JSON.stringify({ category_id: record.id }),
}),
}}
>
All posts with the category {record.name} ;
</Button>
) : null;
};
You can use this button e.g. as a child of . You can also create a custom Menu button with that technique to link to the unfiltered list by setting the filter value to {}.
Tip: You have to pass all the query string parameters - not just filter. That’s a current limitation of react-admin.

Related

Sorting columns that are aggregating a reference ( e.g. sum)

In react-admin, for one record in a data grid, I want to get the total number of a relation to that record, and then be able to sort it.
For example, let's say I have a join table for authors and followers. For the author list I want a column that tells me the total number of followers for this author. Then I want to sort that column so that I can see which authors have the most/least followers.
To get the "number of followers"-column I created a custom Component that can return the follower-count for each author:
const ReferenceCount = ({ reference, target }) => {
const record = useRecordContext();
const { total, isLoading, error } = useGetManyReference(reference, {
target: target,
id: record.id
//sort: { field: "?", order: "DESC" }
});
if (isLoading) {
return <p>loading...</p>;
}
if (error) {
return <p>ERROR</p>;
}
return total;
};
and used it within a datagrid (source is authors):
<List>
<Datagrid>
<TextField source="name" />
<ReferenceCount
reference="authorFollower"
target="authorId"
label="Number of Followers"
/>
</Datagrid>
</List>
It works! But the column is not sortable as the values are not related to any field. So I cannot add the sort property to the custom component, or use the sortBy-tag. It seems that the custom sort button also depends on a field.
Is there a any way to achieve this?

VUEJS - Append <tr> element when button pressed

I have a table in my Vuejs project similar to the one that I shared its screenshot above.
My question is; How can I delete a <tr> element from the table when the Delete button of it is pressed for each row?
On the other hand, I want the Add button at the top right of the table to be functional as well. When I click the Add button, I want another <tr> element to be created at the bottom of the table. However, there should be input fields on this element where the user can enter information for each column. After the user has written the information in each column, that row should be added to the table. How can I do that?
if I am correct you are using v-for loop to render all <tr>s
then use the v-for with index like v-for="(obj, index) in objects" to obtain index
to add use Array.prototype.push() to add empty row e.g. objects.push({x: null, y: null})
to remove use Array.prototype.splice() e.g objects.splice(index, 1)
just assign those functionalities to respective buttons.
You could attempt this using a data property in your component and then implement two methods: deleteRow and addRow.
This could be an example code
data() {
return {
dataTable: [
{
id: 1,
code: code1,
description: description1,
},
{
id: 2,
code: code2,
description: description3,
},
...
]
}
}
methods: {
deleteRow(id) {
this.dataTable = this.dataTable.splice(
this.dataTable.findIndex(item => item.id === id)
)
}
addRow(newRow) {
// newRow = {id: 3, code: code3, description: description3}
this.dataTable = [...this.dataTable, newRow]
}
},
Your component will be updated by Vue.js reactivity system.
Probably addRow method should be called from a modal component where you put the input fields to set newRow.

Can VueX's store also emit events to components?

I have the following component tree:
App
List
ItemDetails
Widget1
...
the List component has a filters form, those are applied on button press
Widget1 has a button which applies another filter (by id) to the list, applying that one removes filters from the filter form and vice versa
the list is also live-updated via polling (later will be via WebSockets), so I have to separate v-model of the filter fields in List and the actually applied filters (those are in the store)
Currently, I've put the following state to the VueX store:
state: {
filters: {
// these come from the List's filter panel
result: '',
name: '',
date: '',
// this one comes from Widget1
id: '',
},
pagination: {
perPage: 10,
page: 0,
total: 0,
},
items: [],
selectedItem: null,
},
and both filters from the List and from the button in Widget1 are applied via dispatching the following action to the store:
applyFilters ({ state, commit, dispatch }, filters) {
if(filters.id) {
for(let filterName in state.filters)
if(filterName !== 'id')
filters[filterName] = '';
} else {
filters.id = '';
}
commit('setFilters', filters);
commit('setPage', 0);
dispatch('loadExaminations');
},
But here's the problem: the List component has its model of filter fields (on press of the button those are gathered and applyFilters is dispatched). This works well except when the id filter (from Widget1) is applied, the filter fields in the List component are not emptied.
One obvious option to handle this is to move the model of those filter fields to the store as well. That doesn't look that nice since for each field (that uses v-model) I have to create a computed (in the List component) and write a setter and a getter from the store. It seems nicer to send an event from applyFilters to List saying "empty the filter fields", but I'm not sure if VueX can do this. Is this possible? Or should I stick with the "move filter fields' model to the store" plan? Or do you know a nice alternative? I'm aware of the event bus concept, but that one uses Vue instance which shouldn't be used in store, I guess.
You can listen to vuex's actions by using subscribeAction.
// List.vue
mounted() {
this.$store.subscribeAction({
before: (action, state) => {
if (action.type === "applyFilters" && action.payload.id) {
this.emptyComponentFields();
}
},
after: (action, state) => {}
});
}
CodeSandbox.

How do I render a message when there are no filtered results in React?

I have a search bar that updates the state based on user input. Components that don't match the keywords I've set up will get filtered out and will not render to the screen. How do I display a message when there are no matches in the search bar?
An example can be found on CodeSandbox here:
https://codesandbox.io/embed/search-bar-test-odxw8
I am using an array of objects to store the data. I'm using the map function to separate the data into individual components, and the filter function to perform a re-render as the user inputs a word.
I tried using logic that says "if the array length is 0, display the message". The problem with that is the array is apparently always the same length and isn't connected to the amount of items being rendered to the screen.
I also tried using logic that says "if the inputted word doesn't match the object's keywords, display the message". This doesn't throw an error, but it also does not work as expected. The message remains visible at all times.
My state:
this.state = {
AnimalList,
AnimalFilter: ""
};
An example of my conditional logic:
{/* This is where I'm trying to place the conditional logic */}
{/* For example, typing "pig" should make this appear */}
{this.state.AnimalFilter!== AnimalList.keywords && (
<h2>No Results</h2>
)}
The filter and map methods:
<div className="container">
{AnimalList.filter(animal =>
animal.keywords
.toLocaleLowerCase()
.includes(this.state.AnimalFilter.toLocaleLowerCase())
).map(({ object1, object2, object3 }, index) => (
<AnimalCard
key={index}
object1={object1}
object2={object2}
object3={object3}
/>
))}
</div>
The Data:
export default [
{
object1: "horse",
object2: "goat",
object3: "panda",
keywords: "horse goat panda"
},
{
object1: "horse",
object2: "cat",
object3: "cow",
keywords: "horse cat cow"
},
{
...
},
...
];
I expect for the message to display to the screen when a user inputs a word that "doesn't exist". As of now, the message will either display the entire time or not at all.

Editing the selected option in the select dropdown(<q-select> of quasar framework)

I am trying the edit the previously selected option in the select drop down.I am able to show the checked options based on the data driven from the service call, but not able to choose other select option in the drop down.I am using quasar framework and vue.js.
Code:
<q-select
multiple
stack-label="Actions"
v-model="multiSelect"
:options="options"/>
Script:
import {QCheckbox,QSelect} from 'quasar'export
default {components: {QCheckbox,QSelect},
data () {return {
multSelect: [],
options1: [{label: 'X-B',value: 'x-b'},{label: 'RT-Builder',value: 'rt-builder'},{label: 'Com',value: 'com'},{label: 'Max',value: 'max'},{label: 'Runner',value: 'runner'},{label: 'Opto',value: 'opto'}],
....................
created () {
axios.get('http://*********/getDetails').then(response => {
this.multiSelect = response.data
})
}
Can someone help me with this?
The value you store in your component property multiSelect should be an array of the selectable values you want to be checked:
For example (following your data set):
this.multiSelect = ['x-b', 'rt-builder', 'max']
Whereas for "simple" select fields (single choice)
<q-select ... v-model="selectedValue" :options="options" />
you simply do
this.selectedValue = 'identifier'