I'm using Tabular in a Next/React environment and I occasionally get the following when clicking a button that's rendered via a custom formatter:
component.getComponent is not a function
The button/custom formatter:
const EditButton = () => {
const editor = document.createElement('button');
editor.style.backgroundColor = '#000';
editor.style.color = '#FFF';
editor.innerHTML = 'Edit';
editor.style.width = '100%';
return editor;
}
The button event:
const handleEditButtonClick = (e, c) => {
const el = e.target as HTMLButtonElement;
const data = c.getRow().getData();
console.log(el, data);
}
Tabulator is initialized via a useEffect:
let el = React.createRef();
let tabulator: typeof Tabulator | null = null;
useEffect(() => {
if (el.current) {
tabulator = new Tabulator(el.current, {
data: [...productsData()],
columns: columns,
});
}
}, [el]);
And rendered:
return (
<div ref={el} style={{ width: '100%', height: '100%' }} />
);
The column:
{ formatter: EditButton, cellClick: handleEditButtonClick }
Any ideas?
Related
When testing the API I have noticed that it is getting called around 6 times, there is no for/foreach loop that would make it run several times in the code so am unsure as to why this may be happening.
The API runs every time a user goes onto the Landing Screen.
Any advice would be appreciated
exports.getFilteredOpportunities = async (req, res) => {
let mongoQuery = generateMongoQuery(req);
try {
const opps = await Opportunity.find(mongoQuery)
.select(removeItems)
.sort({
createdAt: -1
});
res.status(200).json({
opportunities: opps,
});
} catch (error) {
res.status(500).json({
status: "error",
message: error,
});
}
};
GenerateMongoQuery function
const generateMongoQuery = (req) => {
let query = {};
// search param used searching through Title field, ie. search=food
if (req.query.search && req.query.search.length > 0) {
query.$or = [{}, {}];
query.$or[0].Title = query.$or[1].Description = new RegExp(
`${req.query.search}`,
"i"
);
}
// location param, i.e location=Glasgow,Manchester
if (req.query.location && req.query.location.length > 0) {
query.location = {
$in: req.query.location.split(",")
};
}
// timeslot param, i.e timeslot=Evening,Morning
if (req.query.timeslot && req.query.timeslot.length > 0) {
query.timeslot = {
$in: req.query.timeslot.split(",")
};
}
// category param, returning corresponding id i.e category=
if (req.query.category && req.query.category.length > 0) {
query.category = {
$in: req.query.category.split(",")
};
}
// Dont load expired opportunities
query.eventDate = {
$gte: new Date().toDateString()
};
// Only return non-cancelled opportunities
query.isCancelled = false;
return query;
};
The landing Screen
import { useState, useEffect } from "react";
import Opportunities from "../components/molecules/Opportunities";
import Filters from "../components/organisms/Filters";
import { IOpportunity } from "../Models/IOpportunity";
import OpportunitiesClient from "../Api/opportunitiesClient";
import Utils from "../Utils/Utils";
import FiltersClient from "../Api/filtersClient";
import { IFilters } from "../Models/IFilters";
import { ISelectedFilters } from "../Models/ISelectedFilters";
import Header from "../components/atoms/Header";
import Footer from "../components/atoms/Footer";
export default function LandingScreen(props) {
const [opportunities, setOpportunities] = useState<IOpportunity[]>([]);
const [filters, setFilters] = useState<IFilters[]>([]);
const [checkedFilters, setCheckedFilters] = useState<
ISelectedFilters | undefined
>({
Location: [],
Category: [],
Timeslot: [],
});
const [isLoading, setLoading] = useState<boolean>(true);
const [allResultsLoaded, setAllResultsLoaded] = useState<boolean>(false);
let pageToGet = 1;
const [scrollPosition, setScrollPosition] = useState(0);
const [totalOpps, setTotalOpps] = useState(0);
useEffect(() => {
getOpportunities();
getFilters();
}, []);
useEffect(() => {
setTotalOpps(opportunities.length);
}, [opportunities]);
const handleScroll = () => {
setScrollPosition(window.pageYOffset);
let scrollHeight = 0;
if ((props.scrollHeight === null) !== undefined) {
scrollHeight = +props.scrollHeight;
} else {
scrollHeight = document.scrollingElement.scrollHeight;
}
if (
window.innerHeight + document.documentElement.scrollTop >=
scrollHeight
) {
if (allResultsLoaded === false) {
setLoading(true);
}
setTimeout(() => {
pageToGet += 1;
getOpportunities();
}, 600);
}
window.removeEventListener("scroll", handleScroll);
};
window.addEventListener("scroll", handleScroll, { passive: true });
const setItemChecked = (filtername: string, filterType: string) => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
setCheckedFilters(
Utils.setItemChecked(filtername, filterType, checkedFilters)
);
};
const resetFilters = () => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
checkedFilters.Location = [];
checkedFilters.Category = [];
checkedFilters.Timeslot = [];
getOpportunities();
};
const getFilters = () => {
FiltersClient.getAll().then((filters) => {
setFilters(filters);
});
};
const getOpportunities = () => {
console.log(opportunities);
OpportunitiesClient.getWithParams(
Utils.getAxiosParams(checkedFilters)
).then((res) => {
definePage(res);
if (opportunities.length === res["opportunities"].length)
setAllResultsLoaded(true);
setLoading(false);
});
};
const definePage = (res) => {
if (pageToGet === 1) {
setOpportunities(res["opportunities"].slice(0, 15));
} else {
setOpportunities(
opportunities.concat(
res["opportunities"].slice(
opportunities.length,
opportunities.length + 15
)
)
);
}
};
return (
<div>
<Header page="landing" includePostButton={true} />
<div
data-testid="test-div1"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4 mb-5 my-10"
>
<div></div>
<div className="card-container-title">
{totalOpps} Results{" "}
{checkedFilters.Location.length > 0 ? "(Filtered)" : ""}
</div>
</div>
<div
data-testid="test-div3"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4"
>
<div>
<Filters
filters={filters}
getChecked={setItemChecked}
urlCall={getOpportunities}
resetFilters={resetFilters}
checkedFilters={checkedFilters}
/>
</div>
<div
data-testid="test-div4"
className={isLoading ? "opacity-50" : ""}
>
<div className="col-span-2">
<Opportunities
items={opportunities}
isLoading={isLoading}
allResultsLoaded={allResultsLoaded}
/>
</div>
</div>
</div>
<Footer />
</div>
);
}
I am trying to get the nested array from a input value of a checkbox.
How do I handle a nested array?
These are the values:
const othersOptions = [
{procedure:'ORAL PROPHYLAXIS',price: 1000},
{procedure:'TOOTH RESTORATION',price:1200},
{procedure:'TOOTH EXTRACTION',price:800}
];
This is how I get the values from checkbox. I am guessing that value={[item]} is procedure:'ORAL PROPHYLAXIS',price: 1000 if the ORAL PROPHYLAXIS checkbox is checked
<Form>
{othersOptions.map((item, index) => (
<div key={index} className="mb-3">
<Form.Check
value={[item]}
id={[item.procedure]}
type="checkbox"
label={`${item.procedure}`}
onClick={handleChangeCheckbox('Others')}
required
/>
</div>
))}
</Form>
When I console.log the value it shows that the value is [Object object] this is the value.
const handleChangeCheckbox = input => event => {
var value = event.target.value;
console.log(value, "this is the value")
var isChecked = event.target.checked;
setChecked(current =>
current.map(obj => {
if (obj.option === input) {
if(isChecked){
return {...obj, chosen: [{...obj.chosen, value}] };
}else{
var newArr = obj.chosen;
var index = newArr.indexOf(event.target.value);
newArr.splice(index, 1);
return {...obj, chosen: newArr};
}
}
return obj;
}),
);
console.log(checked);
}
and this is how I save the nested array:
const [checked, setChecked] = useState([
{ option: 'Others',
chosen: [],
]);
The reason why I need the procedure and price is so that I can save the values to MongoDB and get the values to another page which is a Create Receipt page. I want the following procedures price to automatically display in the Create Receipt page.Thank you for the help!
If anyone is wondering how I fixed it.
I stringfy the input values and parsed the e.target.values
import "./styles.css";
import { Form } from "react-bootstrap";
import { useState } from "react";
import React from "react";
const othersOptions = [
{ procedure: "ORAL PROPHYLAXIS", price: 1000 },
{ procedure: "TOOTH RESTORATION", price: 1200 },
{ procedure: "TOOTH EXTRACTION", price: 800 },
{ procedure: "DEEP SCALING", price: 10200 },
{ procedure: "PTS AND FISSURES SEALANT", price: 700 },
{ procedure: "FLOURIDE TREATMENT", price: 5500 },
{ procedure: "INTERMEDIATE RESTORATION", price: 7000 },
{ procedure: "ORTHODONTICS", price: 48000 }
];
export default function App() {
const [checked, setChecked] = useState([{ option: "Others", chosen: [] }]);
console.log(checked);
const handleChangeCheckbox = (input) => (event) => {
var value = JSON.parse(event.target.value);
var isChecked = event.target.checked;
console.log("value is:", value[0].procedure);
var tempArr = { procedure: value[0].procedure, price: value[0].price };
setChecked((current) =>
current.map((obj) => {
if (obj.option === input) {
if (isChecked) {
return { ...obj, chosen: [...obj.chosen, tempArr] };
} else {
var newArr = obj.chosen;
var index = newArr.indexOf(event.target.value);
newArr.splice(index, 1); // 2nd parameter means remove one item only
return { ...obj, chosen: newArr };
}
}
return obj;
})
);
};
return (
<Form>
{othersOptions.map((item, index) => (
<div key={index} className="mb-3">
<Form.Check
value={JSON.stringify([item])}
id={[item]}
type="checkbox"
label={`${item.procedure}`}
onClick={handleChangeCheckbox("Others")}
required
/>
</div>
))}
</Form>
);
}
RUN THE CODE HERE
I have a layout component that needs onAppBarInputChange prop. The onAppBarInputChange prop expected a function that take the input value from the layout component, and filter the todos based on that input value.
How do I pass the props from the todos page to the layout component?
todos.jsx
import {useState} from 'react'
import Layout from './layout'
const Todos = () => {
const [query, setQuery] = useState('')
const todos = [
{
id: 0,
text: 'make some projects'
},
{
id: 1,
text: 'fix some bugs'
},
{
id: 2,
text: 'cook food at home'
}
]
const searchedTodos = todos.filter(todo => todo.toLowerCase().includes(query.toLowerCase()))
return (
<ul>
{searchedTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}
Todos.getLayout = function getLayout(page) {
return (
{/* how to set the query like this? */}
<Layout onAppBarInputChage={() => setQuery(e.targe.value)}>
{page}
</Layout>
)
}
export default Todos;
layout.jsx
const Layout = ({children, onAppBarInputChange}) => {
return (
<div>
<header>
<div>Todo page</div>
<input onChange={onAppBarInputChange} />
</header>
<main>{children}</main>
<footer>
some footer here
</footer>
</div>
)
}
export default Layout
Note: I had read the documentation from the next.js website, about how to add layout in next.js, however they don't show any examples on how to pass the props to the layout component
How about passing the input value through Context?
By adopting Context every component can observe the input value easily.
context/app.jsx
const AppContext = createContext(null);
const AppContextProvider = ({ children }) => {
const [query, setQuery] = useState("");
const value = {
query,
setQuery,
};
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};
const useAppContext = () => useContext(AppContext);
export { AppContext, AppContextProvider, useAppContext };
pages/_app.jsx
function App({ Component, pageProps }) {
return (
<AppContextProvider>
{Component.getLayout(<Component {...pageProps} />)}
</AppContextProvider>
);
}
component/layout.jsx
const Layout = ({ children }) => {
const { setQuery } = useAppContext();
const onAppBarInputChange = (e) => setQuery(e.target.value);
...(snip)...
todos.jsx
const Todos = () => {
const { query } = useAppContext();
...(snip)...
};
Todos.getLayout = function getLayout(page) {
return <Layout>{page}</Layout>;
};
I'm trying to figure out how to properly lay out a few items
on a page in my Xamarin.Forms project.
I need to create a login page and the graphical layout should look something as presented below:
I am guessing that I should use Grid but I have a really hard time figuring out how to use it.
How would I go about to create the presented layout?
Note:
This question isn't about the layout I desire. The layout I desire
would however give me a real-world sample to learn how to properly do
layouts in Xamarin.Forms.
Here is the solution
public partial class ComplexRelativeLayoutPage : ContentPage
{
public ComplexRelativeLayoutPage()
{
InitializeComponent();
RelativeLayout layout = new RelativeLayout();
Label topLabel = new Label
{
Text = "I am a label",
};
layout.Children.Add(topLabel,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - topLabel.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width / 2;
}),
Constraint.Constant(10)
);
Image blueImage = new Image
{
Source= ImageSource.FromResource("ButtonRendererDemo.Resources.test.jpg")
};
layout.Children.Add(blueImage,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - 300 / 2;
}),
Constraint.RelativeToView(topLabel, (parent, label) =>
{
return label.Bounds.Bottom + 20;
}),
Constraint.Constant(300),
Constraint.Constant(250)
);
Entry e1 = new Entry
{
Placeholder="Input Box 1",
};
layout.Children.Add(e1,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 10;
}),
Constraint.RelativeToView(blueImage, (parent, img) =>
{
return img.Bounds.Bottom + 20;
}),
Constraint.RelativeToParent((parent) =>
{
return parent.Width - 20;
})
);
Entry e2 = new Entry
{
Placeholder = "Input Box 2",
};
layout.Children.Add(e2,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 10;
}),
Constraint.RelativeToView(e1, (parent, e) =>
{
return e.Bounds.Bottom;
}),
Constraint.RelativeToParent((parent) =>
{
return parent.Width - 20;
})
);
Button bLeft = new Button
{
Text = "Button",
BackgroundColor = Color.Pink
};
layout.Children.Add(bLeft,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 20;
}),
Constraint.RelativeToView(e2, (parent, e) =>
{
return e.Bounds.Bottom;
})
);
Button bRight1 = new Button
{
Text = "Button",
BackgroundColor = Color.Pink
};
layout.Children.Add(bRight1,
Constraint.RelativeToParent((parent) =>
{
return parent.Width - bRight1.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width - 20;
}),
Constraint.RelativeToView(bLeft, (parent, b) =>
{
return b.Y;
})
);
Button bRight2 = new Button
{
Text = "Button",
BackgroundColor=Color.Pink
};
layout.Children.Add(bRight2,
Constraint.RelativeToView(bRight1, (parent, b) =>
{
return b.X;
}),
Constraint.RelativeToView(bRight1, (parent, b) =>
{
return b.Bounds.Bottom + 10;
})
);
Button bBottom1 = new Button
{
Text = "Button",
BackgroundColor = Color.Lime
};
layout.Children.Add(bBottom1,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - bBottom1.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width / 2;
}),
Constraint.RelativeToView(bRight2, (parent, b) =>
{
return b.Bounds.Bottom + 20;
})
);
Button bBottom2 = new Button
{
Text = "Button",
BackgroundColor = Color.Lime
};
layout.Children.Add(bBottom2,
Constraint.RelativeToView(bBottom1, (parent, b) =>
{
return b.X;
}),
Constraint.RelativeToView(bBottom1, (parent, b) =>
{
return b.Bounds.Bottom + 10;
})
);
ScrollView v = new ScrollView
{
Content=layout
};
Content = v;
}
}
I'd try a StackLayout first
<StackLayout>
<Label />
<Image />
<Entry />
<Entry />
<StackLayout Orientation="Horizontal">
<Button />
<StackLayout>
<Button />
<Button />
</StackLayout>
</StackLayout>
<Button />
<Button />
</StackLayout>
i am new in react native,i try to developing native application in react native but i have onPress click event issue.
InfoSwipper.js file i was click on Sign In button display "undefined is not an object(evaluating 'this.props.navigator.push') ". I am not sure what I am missing. please some one help me.
Thank you in advance.
index.android.js file code
"use strict";
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
StatusBar,
AsyncStorage,
} from 'react-native';
var SignUp = require("./components/SignUp");
var InfoScreens = require("./components/InfoScreens");
import SplashScreen from 'react-native-splash-screen'
var Kahaani = React.createClass({
_renderScene(route, navigator) {
if (route.id === 0) {
return <InfoScreens navigator={navigator} />
}
else if (route.id === 1) {
StatusBar.setHidden(false);
console.log('WRONG SIGNIN', "here");
return <LogIn navigator={navigator} />
}
},
componentDidMount() {
// do anything while splash screen keeps, use await to wait for an async task.
SplashScreen.hide();
},
render: function() {
return (
<Navigator
style={styles.navigationContainer}
initialRoute={{id: 0, }}
renderScene={this._renderScene} />
);
}
});
var styles = StyleSheet.create({
navigationContainer: {
flex: 1,
},
});
AppRegistry.registerComponent('Kahaani', () => Kahaani);
swipperScreen.js
'use strict';
import React, { Component } from 'react';
import {
Dimensions, // Detects screen dimensions
Platform, // Detects platform running the app
ScrollView, // Handles navigation between screens
StyleSheet, // CSS-like styles
View,
Text,Navigator, TouchableHighlight, // Container component
} from 'react-native';
import LogIn from './LogIn';
var navigator;
class InfoScreenSwiper extends Component {
constructor(props)
{
super(props);
}
static defaultProps = {
horizontal: true,
pagingEnabled: true,
showsHorizontalScrollIndicator: false,
showsVerticalScrollIndicator: false,
bounces: false,
scrollsToTop: false,
removeClippedSubviews: true,
automaticallyAdjustContentInsets: false,
index: 0
};
state = this.initState(this.props);
initState(props) {
const total = props.children ? props.children.length || 1 : 0,
index = total > 1 ? Math.min(props.index, total - 1) : 0,
offset = width * index;
const state = {
total,
index,
offset,
width,
height,
};
this.internals = {
isScrolling: false,
offset
};
return state;
}
onScrollBegin = e => {
// Update internal isScrolling state
this.internals.isScrolling = true;
}
onScrollEnd = e => {
// Update internal isScrolling state
this.internals.isScrolling = false;
// Update index
this.updateIndex(e.nativeEvent.contentOffset
? e.nativeEvent.contentOffset.x
// When scrolled with .scrollTo() on Android there is no contentOffset
: e.nativeEvent.position * this.state.width
);
}
onScrollEndDrag = e => {
const { contentOffset: { x: newOffset } } = e.nativeEvent,
{ children } = this.props,
{ index } = this.state,
{ offset } = this.internals;
if (offset === newOffset &&
(index === 0 || index === children.length - 1)) {
this.internals.isScrolling = false;
}
}
updateIndex = (offset) => {
const state = this.state,
diff = offset - this.internals.offset,
step = state.width;
let index = state.index;
if (!diff) {
return;
}
index = parseInt(index + Math.round(diff / step), 10);
this.internals.offset = offset;
this.setState({
index
});
}
swipe = () => {
if (this.internals.isScrolling || this.state.total < 2) {
return;
}
const state = this.state,
diff = this.state.index + 1,
x = diff * state.width,
y = 0;
this.scrollView && this.scrollView.scrollTo({ x, y, animated: true });
this.internals.isScrolling = true;
if (Platform.OS === 'android') {
setImmediate(() => {
this.onScrollEnd({
nativeEvent: {
position: diff
}
});
});
}
}
renderScrollView = pages => {
return (
<ScrollView ref={component => { this.scrollView = component; }}
{...this.props}
contentContainerStyle={[styles.wrapper, this.props.style]}
onScrollBeginDrag={this.onScrollBegin}
onMomentumScrollEnd={this.onScrollEnd}
onScrollEndDrag={this.onScrollEndDrag}
>
{pages.map((page, i) =>
// Render each slide inside a View
<View style={[styles.fullScreen, styles.slide]} key={i}>
{page}
</View>
)}
</ScrollView>
);
}
renderPagination = () => {
if (this.state.total <= 1) {
return null;
}
const ActiveDot = <View style={[styles.dot, styles.activeDot]} />,
Dot = <View style={styles.dot} />;
let dots = [];
for (let key = 0; key < this.state.total; key++) {
dots.push(key === this.state.index
// Active dot
? React.cloneElement(ActiveDot, { key })
// Other dots
: React.cloneElement(Dot, { key })
);
}
return (
<View
pointerEvents="none"
style={[styles.pagination, styles.fullScreen]}>
{dots}
</View>
);
}
onMainPressLogIn= () =>
{
console.log('WRONG SIGNIN', "onMainPressLogIn");
this.props.navigator.push({
id:1
});
}
onSubmitPressed () {
console.log('WRONG SIGNIN', "onSubmitPressed");
this.props.navigator.push({
id:2
})
}
renderButtonBottom = () => {
const lastScreen = this.state.index === this.state.total - 1;
return (
<View pointerEvents="box-none" style={[styles.buttonWrapper]}>
<View style={ [styles.viewButton]}>
<Text style={[styles.buttonLogIn,styles.buttonAll]} onPress={this.onMainPressLogIn} >LOG IN</Text>
<Text style={[styles.buttonSignUp,styles.buttonAll]} onPress={(this.onSubmitPressed.bind(this))}>SIGN UP</Text>
</View>
</View>
);
}
render = ({ children } = this.props) => {
return (
<View style={[styles.container, styles.fullScreen]}>
{/* Render screens */}
{this.renderScrollView(children)}
{/* Render pagination */}
{this.renderPagination()}
{/* Render Continue or Done button */}
{this.renderButtonBottom()}
</View>
);
}
}
module.exports = InfoScreenSwiper;