I'm new to react, and I'm working on a small project that uses a search bar in Single js Web page to find data from API.
The main code for this component is:
const main = () => {
const searchElement = document.querySelector("search-bar");
const clubListElement = document.querySelector("club-list");
const onButtonSearchClicked = async() => {
try{
const result = await DataSource.searchClub(searchElement.value);
renderResult(result);
} catch (message) {
fallbackResult(message)
}
};
const renderResult = (results) => {
clubListElement.clubs = results;
};
const fallbackResult = message => {
clubListElement.renderError(message);
};
searchElement.clickEvent = onButtonSearchClicked;
};
export default main;
also my Search-Bar component:
class SearchBar extends HTMLElement{
constructor(){
super();
this.shadowDOM = this.attachShadow({mode: "open"});
}
connectedCallback(){
this.render();
}
set clickEvent(event){
this._clickEvent = event;
this.render();
}
get value(){
//return this.querySelector("#searchElement").value;
return this.shadowDOM.querySelector("#searchElement").value;
}
render(){
//this.innerHTML = `
this.shadowDOM.innerHTML = `
<style>
.search-container {
max-width: 800px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
border-radius: 5px;
display: flex;
position: sticky;
top: 10px;
background-color: white;
}
.search-container > input {
width: 75%;
padding: 16px;
border: 0;
border-bottom: 1px solid cornflowerblue;
font-weight: bold;
}
.search-container > input:focus {
outline: 0;
border-bottom: 2px solid cornflowerblue;
}
.search-container > input:focus::placeholder {
font-weight: bold;
}
.search-container > input::placeholder {
color: cornflowerblue;
font-weight: normal;
}
.search-container > button {
width: 23%;
cursor: pointer;
margin-left: auto;
padding: 16px;
background-color: cornflowerblue;
color: white;
border: 0;
text-transform: uppercase;
}
#media screen and (max-width: 550px){
.search-container {
flex-direction: column;
position: static;
}
.search-container > input {
width: 100%;
margin-bottom: 12px;
}
.search-container > button {
width: 100%;
}
}
</style>
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit">Search</button>
</div>
`;
this.shadowDOM.querySelector("#searchButtonElement").addEventListener("click", this._clickEvent);
}
}
customElements.define("search-bar", SearchBar);
furthermore, can i convert this method to ReactJS? because we know if we can't declare a const in render() from React.
I have been through a bunch of flustered, and I'm not exactly sure how to go about doing that.
Can anyone help me with this please? Any other comments on the code are also welcome because i'm new in Reacts
Thank you before!
First and foremost, you should remove all that CSS declaration within the render method, and abstract them into a separate CSS file (you can consider CSS modules, which is supported by React), use CSS-in-JavaScript libraries(such as styled-components), or inline styles.
Next, instead of using event listeners, you should bind the button element's onClick event with the onButtonSearchClicked method, which is similar to what you have defined.
From that method, you will make the API request, and update your component's state with the response.
class SearchBar extends React.Component {
async onButtonSearchClicked() {
const result = await DataSource.searchClub(searchElement.value);
// set state with return result.
}
render() {
return (
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit" onClick={() => this.onButtonSearchClicked}>Search</button>
</div>
);
}
}
One of many variants how could SearchBar component look like in React
class SearchBar extends React.Component {
constructor() {
this.ref = React.createRef(null);
}
componentDidMount() {
this.shadowDOM = this.ref.current.attachShadow({mode: "open"});
this.shadowDOM.innerHTML = getStyle();
}
onClick(event) {
}
render() {
return <div ref={this.ref} id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button onClick={this.onClick} id="searchButtonElement"
type="submit">Search</button>
</div>
}
Related
I know that static site generation idea, The question here is, if SSG gets fetched data at build time, in my next app I used an event handler to get some data according to the search keyword, and it works, How.
This is my code:
Below is the code for the Input component in the next weather app and I use handleSubmit event handler, It gets weather data from an API endpoint.
import React, { useState } from "react";
import PropTypes from "prop-types";
import axios from "axios";
function Input({ onInput }) {
const [inputText, setInputText] = useState();
const [placeholder, setPlaceholder] = useState("أخل اسم البلد أو المنطقة...");
return (
<form className="hstack gap-3" onSubmit={handleSubmit}>
<input
type="text"
className="form-control me-auto"
placeholder={placeholder}
value={inputText || ""}
onChange={handleInput}
onFocus={handleFocus}
onBlur={handleBlur}
/>
<button type="submit">ابحث</button>
<style jsx>{`
input {
height: 50px;
border-radius: 10px;
color: var(--input-color);
font-weight: 500;
font-size: 1.1em;
direction: rtl;
}
input:focus-visible {
color: var(--input-color);
font-weight: 500;
font-size: 1.1em;
}
input:focus {
box-shadow: 0 0 0 0.25rem var(--input-shadow-color);
}
input::placeholder {
color: var(--input-placeholder-color);
font-weight: 500;
opacity: 0.7;
}
button {
padding: 0.60em 1.3em;
background-color: var(--search-btn-background-color);
color: #fff;
font-weight: 500;
border: 5px solid var(--search-btn-border-color);
background-color: var(--search-btn-background-color);
border-radius: 12px;
transition: 0.5s;
}
button:hover {
color: #fff;
background-color: var(--btn-hover-color);
}
button:focus {
box-shadow: 0 0 0 0.25rem var(--search-btn-shadow-color);
}
`}</style>
</form>
);
function handleFocus() {
setPlaceholder("");
}
function handleBlur() {
setPlaceholder("أخل اسم البلد أو المنطقة...");
}
function handleInput(event) {
setInputText(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
axios
.get(
`https://api.openweathermap.org/data/2.5/weather?q=${inputText}&units=metric&appid=5a60c3e5c70d9f4ad304b2b115b3bf72&lang=ar`
)
.then((response) => {
onInput(response.data);
})
.catch((error) => {
console.log(error);
onInput(0);
});
setInputText("");
}
}
Input.propTypes = {
onInput: PropTypes.func.isRequired,
};
export default Input;
SSG in NextJS only happens for queries inside a getStaticProps function.
In you example, your query is inside the component, so no query is performed at build time, but at run time as usual, so your search feature is working as expected.
I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.
I am new to React.js and trying to create a TODO list. I have two buttons for edit and delete. Delete button is working fine, but not the edit button. I am having a hard time with Edit button. When I click on the edit button, I want the text box to be editable. Then I can edit and when I enter, it should be updated.
I have some css issue with button alignments too. Please help me with this.
App.js
import React from 'react';
import './App.css';
import './AddedTasks.css'
import uuid from 'uuid'
class App extends React.Component{
constructor(props){
super(props);
this.state = {
task:[],
currentTask:{
text:'',
key:''
}
}
this.addTask = this.addTask.bind(this);
this.editTask = this.editTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.handleInput = this.handleInput.bind(this);
}
addTask(event){
event.preventDefault();
const newItem = this.state.currentTask;
if(newItem.text !==""){
const items = [...this.state.task, newItem];
this.setState({
task: items,
currentTask:{
text:'',
key:''
},
})
}
}
editTask(text,key) {
//console.log("items:"+this.state.task);
const items = this.state.task;
items.map(item=>{
if(item.key===key){
//console.log(item.key +" "+key)
// item.text= text.title;
}
})
this.setState({
task: items,
})
}
deleteTask(key){
const currentTaskArray = [...this.state.task]
const taskAfterDeleted = currentTaskArray.filter(deletedTask => deletedTask.key !== key);
this.setState({
task:taskAfterDeleted
})
}
handleInput(event){
this.setState({
currentTask:{
text: event.target.value,
key: uuid()
}
})
}
render(){
return(
<div className='Todo'>
<h1> MyTaskList </h1>
<form id="todo-list" onSubmit={this.addTask}>
<input type="text" className="textInput" placeholder="Enter Item" value={this.state.currentTask.text} onChange={this.handleInput}/>
<button type ="submit">Add</button>
</form>
{this.state.task.map(oneTask=>(
<div className="card">
<div className="container">
<p>{oneTask.text}
<div>
<button className="w3-button delete" onClick={()=> this.deleteTask(oneTask.key)}><i className="fa fa-trash"/></button>
<button className="w3-button edit" onClick={(edit)=>this.editTask(edit.target.value,oneTask.key)}><i className="glyphicon glyphicon-pencil"/></button>
</div>
</p>
</div>
</div>
))}
</div>
);
}
}
export default App;
App.css
_______________
body{
background-color: lightblue;
background-image: url("./todolist.jpg");
background-repeat: no-repeat;
background-size: cover;
alignment: center;
padding-top: 40px;
}
h1{
text-align: center;
color: #bf6318;
padding-right: 17px;
}
.Todo{
background-color: #c1b2cd;
min-height: 500px;
width: 500px;
margin: 150px;
padding-left: 20px;
background-image: url("./pin.jpg");
background-size: 80px;
background-repeat: no-repeat;
}
#todo-list input{
background-color: rgb(95, 83, 135);
border: 0;
width: 250px;
height: 50px;
padding: 0 20px;
margin: 20px;
font-size: 18px;
border-radius: 10px;
color: #ffffff;
}
#todo-list input::placeholder{
color: rgba(255,255,255,0.5);
}
button{
background-color: #008CBA;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
}
AddedTask.cs
_________________
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 70%;
border-radius: 5px;
margin-bottom: 10px;
margin-left: 20px;
background-color: #bf826b;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 4px 16px; /*input/card field*/
height: 40px;
}
.container button.edit{
color: #bf6318;
margin-left: 80px;
margin-right: 10px;
}
.container button.delete{
}
The edit button is not working because you are using edit.target.value which doesn't exist. In my understanding, you are trying to access the text so it will be oneTask.text.
Here is the file:
App.js
import React from "react";
import "./styles.css";
import uuid from "uuid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
task: [],
currentTask: {
text: "",
key: ""
}
};
this.addTask = this.addTask.bind(this);
this.editTask = this.editTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.handleInput = this.handleInput.bind(this);
}
addTask(event) {
event.preventDefault();
const newItem = this.state.currentTask;
if (newItem.text !== "") {
const items = [...this.state.task, newItem];
this.setState({
task: items,
currentTask: {
text: "",
key: ""
}
});
}
}
editTask(text, key) {
//console.log("items:"+this.state.task);
const items = this.state.task;
this.setState({
task: items.filter(item => item.key !== key),
currentTask: {
text
}
});
}
deleteTask(key) {
const currentTaskArray = [...this.state.task];
const taskAfterDeleted = currentTaskArray.filter(
deletedTask => deletedTask.key !== key
);
this.setState({
task: taskAfterDeleted
});
}
handleInput(event) {
this.setState({
currentTask: {
text: event.target.value,
key:uuid()
}
});
}
render() {
return (
<div className="Todo">
<h1> MyTaskList </h1>
<form id="todo-list" onSubmit={this.addTask}>
<input
type="text"
className="textInput"
placeholder="Enter Item"
value={this.state.currentTask.text}
onChange={this.handleInput}
/>
<button type="submit">Add</button>
</form>
{this.state.task.map(oneTask => (
<div key={oneTask.key} className="card">
<div className="container">
<p>
{oneTask.text}
<div>
<button
className="w3-button delete"
onClick={() => this.deleteTask(oneTask.key)}
>
Delete
</button>
<button
className="w3-button edit"
onClick={() => this.editTask(oneTask.text, oneTask.key)}
>
Edit
</button>
</div>
</p>
</div>
</div>
))}
</div>
);
}
}
export default App;
I have also manipulated the edit task code you can revert back to use your code.
Here is the working link: https://codesandbox.io/s/gifted-almeida-uivq0
Hope this helps!
So I'm assuming that this is a view of todo and by default it's disabled. You can use a state variable to make textboxes enabled when clicking on the edit button.
Add a handler to edit button click event and inside this handler change state variable. So react will enable those two textboxes based on that event.
this.state = {
isEditable:false // by defualt this is disables
}
editHandler(){
//change isEditable to true
}
Make sure to bind this new method in constructor. Then add this to the button click event.
Base on isEditable makes your form controls enabled or disabled.
Regarding CSS, just use a new div element and inside that div include two buttons. So it won't come to on top of text boxes.
<div>
<div>text box and button</div>
<div>second row</div>
<div>edit and delete button></div>
</div>
I'm new to Polymer and stared learning it using a course called Polymer 3 - Code like a Google developer.
I would like to call the function: addContactClick() from the child component: side-menu.
To do that I have passed the function as an argument. I have defined it in the side-menu and called it using on-click
start-polymer3.js:
import {
PolymerElement,
html
} from '#polymer/polymer';
import SideMenu from './side-menu.js'
import ContentArea from './content-area.js'
class StartPolymer3 extends PolymerElement {
static get properties() {
return {
addContactClick: {
type: Function,
}
};
}
constructor() {
super();
this.addContactClick = this.addContactClick.bind(this);
}
static get template() {
return html `
<style>
.main-page {
display: grid;
grid-template-columns: 250px 1fr;
}
</style>
<div class="main-page">
<side-menu addContactClick="addContactClick"></side-menu>
<content-area></content-area>
</div>
`;
}
addContactClick() {
console.log('Button Clicked');
}
}
customElements.define('start-polymer3', StartPolymer3);
side-menu.js:
import {
PolymerElement,
html
} from '#polymer/polymer';
export default class SideMenu extends PolymerElement {
constructor() {
super();
}
static get properties() {
return {
addContactClick: {
type: Function
},
};
}
static get template() {
return html `
<style>
#side-menu {
background: #323759;
height: 100vh;
padding: 50px 25px;
}
.logo {
text-align: center;
}
.logo img {
width: 50px;
}
.title {
color: #ccced7;
font-weight: 700;
margin: 1rem 0;
}
#side-menu nav a {
color: #ccced7;
text-decoration: none;
text-transform: capitalize;
display: block;
padding: 10px 10px 10px 0;
}
#side-menu nav a span.icon{
padding: 10px 10px 10px 0;
}
</style>
<section id="side-menu">
<div class="logo">
<img src="https://www.dexterousgroup.com.au/wp-content/uploads/2018/03/google-logo-icon-PNG-Transparent-Background.png">
</div>
<div class="menu">
<div class="title">Contacts</div>
<nav>
<span class="icon"> + </span>Add Contacts
</nav>
</div>
</section>
`;
}
}
customElements.define('side-menu', SideMenu);
When I execute it, I get the following error:listener method addContactClick not defined
handler # template-stamp.js:92
Here are changes you should do and try.
start-polymer3.js
Removed property addContactClick.
Changed definition of addContactClick() function
Added Binding instead of just name of function, while passing our own function in side-menu element.
import {
PolymerElement,
html
} from '#polymer/polymer';
import SideMenu from './side-menu.js'
import ContentArea from './content-area.js'
class StartPolymer3 extends PolymerElement {
static get properties() {
return {
};
}
constructor() {
super();
// this.addContactClick = this.addContactClick.bind(this);
}
static get template() {
return html `
<style>
.main-page {
display: grid;
grid-template-columns: 250px 1fr;
}
</style>
<div class="main-page">
<side-menu add-contact-click="[[addContactClick()]]"></side-menu>
<content-area></content-area>
</div>
`;
}
addContactClick() {
return function() {console.log('Button Clicked')};
}
}
customElements.define('start-polymer3', StartPolymer3);
side-menu.js
Changed type of addContactClick from Function to String.
import {
PolymerElement,
html
} from '#polymer/polymer';
export default class SideMenu extends PolymerElement {
constructor() {
super();
}
static get properties() {
return {
addContactClick: {
type: String
},
};
}
static get template() {
return html `
<style>
#side-menu {
background: #323759;
height: 100vh;
padding: 50px 25px;
}
.logo {
text-align: center;
}
.logo img {
width: 50px;
}
.title {
color: #ccced7;
font-weight: 700;
margin: 1rem 0;
}
#side-menu nav a {
color: #ccced7;
text-decoration: none;
text-transform: capitalize;
display: block;
padding: 10px 10px 10px 0;
}
#side-menu nav a span.icon{
padding: 10px 10px 10px 0;
}
</style>
<section id="side-menu">
<div class="logo">
<img src="https://www.dexterousgroup.com.au/wp-content/uploads/2018/03/google-logo-icon-PNG-Transparent-Background.png">
</div>
<div class="menu">
<div class="title">Contacts</div>
<nav>
<span class="icon"> + </span>Add Contacts
</nav>
</div>
</section>
`;
}
}
customElements.define('side-menu', SideMenu);
If still, you have an issue let me know.
Here is another link you can visit which is for similar question:
Passing a function as an attribute value
I used this example snippet written in ReactJS to try and create a search box in my website, which is written in React TS
https://codemyui.com/input-highlight-seen-tripadvisor/
However when I have tried to convert it, I get the following issues
1) Input is read only, ie I cannot type into it
2) I am not sure what to do with this bit of the style in the original CSS, as it does not compile when I paste it into my CSS
&:focus {
+ .input-highlight {
border-top: 3px solid #fbc91b;
}
}
Can someone point out what I have done wrong in the conversion to TS?
My React control
import * as React from 'react';
import './App.css';
interface ISearchBarState {
inputValue : string
}
class SearchBar extends React.Component<{}, ISearchBarState> {
constructor(props: any) {
super(props);
this.state = {
inputValue: ''
};
this.onInputChange = this.onInputChange.bind(this);
}
public render() {
const { inputValue } = this.state;
return (<div className='input-wrapper'>
<input
placeholder='Search...'
value={inputValue}
spellCheck={false}
/>
<span className='input-highlight'>
{ inputValue.replace(/ /g, "\u00a0") }
</span>
</div>);
}
private onInputChange(e: any) {
const { value } = e.target;
this.setState({
inputValue: value
});
}
}
export default SearchBar;
and my CSS
.input-wrapper {
position: relative;
width: 350px;
margin-left: 5px;
}
.input-highlight {
font-size: 16;
user-select: none;
line-height: 20;
border-top: 1px solid white;
position: absolute;
left: 0;
bottom: 0;
color: #999999;
max-width: 100%;
height: 0;
color: transparent;
font-family: Roboto Slab, sans-serif;
overflow: hidden;
border-top: 3px solid #fbc91b;
}
input {
height: 30px;
width: 100%;
min-width: 100%;
padding: 0;
border-radius: 0;
line-height: 2;
background-color: transparent;
color: #999999;
font-size: 16;
border: none;
outline: none;
border-bottom: 1px solid #666666;
font-family: Roboto Slab, sans-serif;
}
It looks like the bit of css in question
&:focus {
+ .input-highlight {
border-top: 3px solid #fbc91b;
}
}
is actually scss, you'd need to compile that bit to css first or manually compile it to css. Here, below I help to manually change it to css for you
input:focus .input-highlight{
border-top: 3px solid #fbc91b;
}
you can add this css snippet to your css file, put it below the input css block.