I have a pagination system that allows me to go to the next page and previous page.
I want to be able to implement that if the user runs out of data to return on the page then they should be redirected back to the first page again.
Can someone help me do this? thanks
You can see below my .ts file and .html file.
import { Component } from '#angular/core';
import { WebService } from './web.service';
import { AuthService } from './auth.service';
#Component({
selector: 'hotels',
templateUrl: './hotels.component.html',
styleUrls: ['./hotels.component.css']
})
export class HotelsComponent {
constructor(private webService: WebService, private authService:
AuthService) {}
ngOnInit() {
if (sessionStorage.start) {
this.start = sessionStorage.start;
}
this.webService.getHotels(this.start);
}
nextPage() {
this.start = Number(this.start) + 5;
sessionStorage.start = Number(this.start);
this.webService.getHotels(this.start);
}
previousPage() {
if (this.start > 0) {
this.start = Number(this.start) - 5;
sessionStorage.start = Number(this.start);
this.webService.getHotels(this.start);
}
}
hotel_list;
start = 0;
}
HTML
<div class="container" style="margin-top:100px;">
<div class="row">
<div class="col-sm-12">
<div *ngFor="let hotel of webService.hotel_list | async">
<div class="card text-white bg-primary mb-3"
[routerLink]="['/hotels', hotel._id]" style="cursor: pointer">
<div class="card-header">
{{ hotel.Name }}
</div>
<div class="card-body">
This hotel is based in
{{ hotel.Location }}
</div>
<div class="card-footer">
{{ hotel.review_count }}
reviews available
</div>
</div>
</div>
</div> <!-- col -->
</div> <!-- row -->
<div class="row">
<div class="col-sm-6">
<button (click)="previousPage()">Previous</button>
</div>
<div class="col-sm-6 text-right">
<button (click)="nextPage()">Next</button>
</div>
</div>
</div> <!-- container -->
Web Service
import { Http, URLSearchParams } from '#angular/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/toPromise';
import { Subject } from 'rxjs/Rx';
#Injectable()
export class WebService {
hotelID;
private hotels_private_list = [];
private hotelsSubject = new Subject();
hotel_list = this.hotelsSubject.asObservable();
private hotel_private_list = [];
private hotelSubject = new Subject();
hotel = this.hotelSubject.asObservable();
private reviews_private_list = [];
private reviewsSubject = new Subject();
reviews = this.reviewsSubject.asObservable();
url: string = 'http://localhost:3000/api/hotels/';
hotelsArray = [];
constructor(private http: Http) {
}
getHotels(start) {
return this.http.get(
'http://localhost:3000/api/hotels?start=' + start)
.subscribe(response => {
this.hotels_private_list = response.json();
this.hotelsSubject.next(this.hotels_private_list);
})
}
getHotel(id: string) {
return this.http.get(
'http://localhost:3000/api/hotels/' + id)
.subscribe(response => {
this.hotel_private_list = [];
this.hotel_private_list.push(response.json());
this.hotelSubject.next(this.hotel_private_list);
this.hotelID = id;
})
}
getReviews(id) {
this.http.get(
'http://localhost:3000/api/hotels/' + id + '/reviews')
.subscribe(
response => {
this.reviews_private_list = response.json();
this.reviewsSubject.next(
this.reviews_private_list);
}
)
}
postReview(review) {
let urlSearchParams = new URLSearchParams();
urlSearchParams.append('username', review.name);
urlSearchParams.append('text', review.review);
urlSearchParams.append('stars', review.stars);
this.http.post(
"http://localhost:3000/api/hotels/" +
review.hotelID + "/reviews",
urlSearchParams)
.subscribe(
response => {
this.getReviews(review.hotelID);
}
)
}
}
I have changed a few things. no need to call API for every next/prev if already exists use that. no need to write async in HTML.
SERVICE
getHotels(start) {
return this.http.get('http://localhost:3000/api/hotels?start=' + start);
}
HTML
<div *ngFor="let hotel of pages[start]">
COMPONENT
pages = {};
ngOnInit() {this.getdata(this.start)}
getdata(start) {
if (pages[start]) return; //don't do anything
this.pages[start] = [];
this.webService.getHotels(start).subscribe(response => {
const result = response.json();
if(!result){ // data does not exist go to first page
this.start = 0;
}else{
pages[start] = result;
}
})
}
nextPage() {
this.start = this.start + 5;
sessionStorage.start = this.start;
getdata(this.start);
}
previousPage() {
if (this.start > 0) {
this.start = this.start - 5;
sessionStorage.start = this.start;
getdata(this.start);
}
}
Related
I've got a React App, that scans products via a Scanner(using serialport.io + socket.io) and adds each scanned product into my frontend cart component.
Right now I got it working, but my solution creates a new row in my cart per product scanned as you can see here and I need it to display a new row only the first time a prod is scanned and then if the same product is detected it only updates the quantity and total per product and also the cart total, something like this...
From what I've searched the best way to do this would be by using react useContext and useReducer but I can't get it working.
This is my code on server side index.js:
io.on("connection", (socket) => {
console.log("Socket Connected");
const port = new SerialPort({
path: "COM6",
baudRate: 9600,
autoOpen: false,
});
socket.on("start_scanner", () => {
port.open(function (err) {
if (err) {
console.log("Error opening port: ", err.message);
}
else{
console.log("Scanner Connected");
}
});
port.on("open", function () {
setInterval(function () {
const portReader = port.read();
if (portReader != null) {
const sensorVal = Buffer.from(portReader).toString();
const soap = require("soap");
const url = "http://example.com?wsdl";
soap.createClient(url, function (err, client) {
client.GetProductById(
{
UserId: "1",
ProductId: sensorVal,
},
function (err, result) {
if (err) return console.log(err);
let productScanned = result.GetProductByIdResult;
socket.broadcast.emit("add_product_to_list", productScanned);
}
);
});
}
}, 700);
});
});
This is my Cart component code:
import { useState, useEffect } from "react";
import io from "socket.io-client";
import ProductRow from "./ProductRow";
import "./ProductsList.css";
const socket = io.connect("http://localhost:5000");
const ProductsList = (props) => {
const [scannedData, setScannedData] = useState([]);
useEffect(() => {
socket.on("add_product_to_list", (productScanned) => {
setScannedData((prevProducts) => [...prevProducts, productScanned]);
});
}, [socket]);
return (
<div className="w-9/12 h-full px-20 py-20 flex flex-col ">
<div className="w-full h-auto my-2 px-3 py-3 font-semibold grid grid-cols-3 bg-blue-600 rounded-xl">
<div className="w-[60%] text-left">Product</div>
<div className="w-[20%] text-left">Quant.</div>
<div className="w-[20%] text-left">Price</div>
</div>
<div
id="products-wrapper"
className="w-full h-[95%] flex flex-col overflow-x-hidden overflow-y-scroll"
>
{scannedData.map((productScanned) => (
<ProductRow data={productScanned} />
))}
</div>
<div className="w-full h-[15%] flex flex-row justify-end">
<div className="w-[20%] h-auto px-3 py-3 font-semibold flex flex-col justify-center bg-blue-600 rounded-xl ">
<div className="w-full text-left">
Total: {/* Total price amount */}{" "}
</div>
<div className="w-full text-left">
Qty: {0}
</div>
</div>
</div>
</div>
);
};
export default ProductsList;
This is my Cart Row component:
import "./ProductsList.css";
const ProductRow = ({ data }) => {
return (
<div
className="product-row w-full h-auto my-2 px-3 py-3 text-black text-center grid grid-cols-3 rounded-xl "
key={data._x003C_Id_x003E_k__BackingField}
>
<div className="w-[60%] text-left">
{data._x003C_Name_x003E_k__BackingField}
</div>
<div className="w-[20%] text-left">{1}</div>
<div className="w-[20%] text-left">
{parseFloat(data._x003C_Price_x003E_k__BackingField).toFixed(2)}€
</div>
</div>
);
};
export default ProductRow;
I've also got a cart-context.js and a CartProvider.js files which I was using to achieve my goal but can't get it to work.
/*cart-context.js*/
import React from "react";
const CartContext = React.createContext({
items: [],
totalAmount: 0,
addItem: (item) => {},
removeItem: (id) => {},
});
export default CartContext;
/*CartProvider.js*/
import { useContext, useReducer } from "react";
import CartContext from "./cart-context";
const defaultCartState = {
items: [],
totalAmount: 0,
};
const cartReducer = (state, action) => {
if (action.type === "ADD_ITEM") {
const updatedTotalAmount = state.totalAmount + action.item.price * action.item.amount;
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.item.id
)
const existingCartItem = state.items[existingCartItemIndex];
let updatedItems;
if(existingCartItem){
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount
}
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
} else{
updatedItems = state.items.concat(action.item);
}
return {
items: updatedItems,
totalAmonut: updatedTotalAmount,
};
}
return defaultCartState;
};
const CartProvider = (props) => {
const [cartState, dispatchCartAction] = useReducer(
cartReducer,
defaultCartState
);
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: "ADD_ITEM", item: item });
};
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: "REMOVE_ITEM", id: id });
};
const cartContext = {
items: cartState.items,
totalAmonut: cartState.totalAmonut,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
return (
<CartContext.Provider value={cartContext}>
{props.children}
</CartContext.Provider>
);
};
export default CartProvider;
Could anyone help me out and help me understand my mistakes?
Thanks in advance.
I'm making an React application that involves fetching an API. The code is below, but basically I'm fetching the data from the API and then mapping over it to present information about each character. It works with the class component, but I tried to change it to a function component and it no longer works. There is no error, it simply doesn't display anything.
From using console.log(), I know that I'm successfully getting the information into the "characters" variable from the API when using the function component, and from comparing the two applications and using console.log() on "this.state.characters" (class) and "characters" (function), I'm pretty sure that getting the exact same data, an array of like 660 of the characters from the show where each character's information is inside of an object. When I tried to add paragraph tags with {characters[0].name} inside of the function component, I got the error "TypeError: Cannot read property 'name' of undefined".
I don't know if it's because I messed up something stupid or because I don't understand something about some detail about the difference between class and function components, either is obviously very possible. Thank you for any help.
Here is the code from the class component:
import React, { Component } from 'react';
export class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
characters: [],
nameInput: '',
locationInput: '',
loading: true
};
};
async componentDidMount() {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
this.setState({characters: array, loading: false}, () => console.log(this.state.characters));
}
readInput = (e) => {
this.setState({nameInput: e.target.value});
console.log(this.state.nameInput);
}
readLocationInput = (e) => {
this.setState({locationInput: e.target.value});
console.log(this.state.locationInput);
}
render() {
return (
<div className="all">
<h4>Search by name:</h4>
<input onChange={this.readInput} />
<h4>Search by location:</h4>
<input onChange={this.readLocationInput} />
<br />
<div className="row m-1">
{this.state.loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : this.state.characters.filter((item) => {
if (this.state.nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(this.state.nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (this.state.locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(this.state.locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
);
}
};
Here is the code from the function component:
import React, { Component, useEffect, useState } from 'react';
import logo from '../rickandmortylogo.png';
export const Body = () => {
const [characters, setCharacters] = useState([]);
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const fetchAPI = async () => {
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
}
fetchAPI();
setCharacters(array);
setLoading(false);
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
console.log(nameInput);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
console.log(locationInput);
}
return (
<>
<div className="text-center">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
);
})}
</div>
</div>
</>
);
};
I'm building a blog application based on MEAN stack, using Angular in front-end, node in back-end and mongoDB for server. When I'm trying to access a particular blog by it's blogId, the browser is not showing anything. Although I'm getting the right data fetched from backend with a 304 status and the right blogId is also passing by the route. The console is also logging the right object. Below is my blog-view.component.ts file
import { ActivatedRoute, Router } from '#angular/router';
import { ToastrManager } from 'ng6-toastr-notifications';
import { BlogService } from '../blog.service';
import { BlogHttpService } from '../blog-http.service';
import { Location } from '#angular/common';
#Component({
selector: 'app-blog-view',
templateUrl: './blog-view.component.html',
styleUrls: ['./blog-view.component.css'],
providers: [Location]
})
export class BlogViewComponent implements OnInit, OnDestroy {
public currentBlog;
constructor(private _route: ActivatedRoute, private router: Router, public blogHttpService:BlogHttpService, public toastr: ToastrManager, private location: Location) {
console.log("view-blog constructor called")
}
ngOnInit() {
console.log("view-blog ngOnInIt called");
let myBlogId = this._route.snapshot.paramMap.get('blogId');
console.log(myBlogId);
this.currentBlog = this.blogHttpService.getSingleBlogInformation(myBlogId).subscribe(
data =>{
console.log(data);
this.currentBlog = data["data"];
},
error =>{
console.log("some error occured");
console.log(error.errorMessage);
})
}
public deleteThisBlog(): any {
this.blogHttpService.deleteBlog(this.currentBlog.blogId).subscribe(
data =>{
console.log(data);
this.toastr.successToastr('This blog is successfully deleted.', 'Success!');
setTimeout(() =>{
this.router.navigate(['/home']);
}, 1000)
},
error =>{
console.log(error);
console.log(error.errorMessage);
this.toastr.errorToastr('Some Error Occured.', 'Oops!');
}
)
}
public goBackToPreviousPage(): any {
this.location.back();
}
ngOnDestroy(){
console.log("view-blog component destroyed");
}
}
blog-view.component.html
<div class="row" *ngIf="currentBlog" style="text-align: center;">
<div class="col-md-12">
<h2>{{currentBlog.title}}</h2>
<p>posted by {{currentBlog.author}} on {{currentBlog.created | date:'medium'}}</p>
<p *ngIf="currentBlog.tags!=undefined && currentBlog.tags.length>0">tags : <span *ngFor="let tag of currentBlog.tags;let first=first;let last=last">{{tag}}{{last ? '' : ', '}}</span></p>
<hr>
<div [innerHtml]="currentBlog.bodyHtml"></div>
<hr>
<h5>category - {{currentBlog.category}}</h5>
</div>
<hr>
<div class="row" *ngIf="currentBlog">
<div class="col-md-4">
<a class="btn btn-primary" [routerLink]="['/edit',currentBlog.blogId]">Edit</a>
</div>
<div class="col-md-4">
<a class="btn btn-danger" (click)="deleteThisBlog()">Delete</a>
</div>
<div class="col-md-4">
<a class="btn btn-warning" (click)="goBackToPreviousPage()">Go Back</a>
</div>
</div>
</div>
</div>
blog-http.service.ts
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
#Injectable()
export class BlogHttpService {
public allBlogs;
public currentBlog;
public baseUrl = 'http://localhost:3000/api/v1/blogs';
constructor(private _http:HttpClient) {
console.log('blog http service constructor called');
}
private handleError(err:HttpErrorResponse){
console.log("handle error http calls");
console.log(err.message);
return Observable.throw(err.message)
}
public getAllBlogs(): any {
let myResponse = this._http.get(this.baseUrl + '/all');
console.log(myResponse);
return myResponse;
}
public getSingleBlogInformation(currentBlogId): any {
let myResponse = this._http.get(this.baseUrl + '/view/' + currentBlogId);
return myResponse;
}
public createBlog(blogData): any {
let myResponse = this._http.post(this.baseUrl + '/create', blogData);
return myResponse;
}
public deleteBlog(blogId): any {
let data = {}
let myResponse = this._http.post(this.baseUrl + '/' + blogId + '/delete', blogId);
return myResponse;
}
public editBlog(blogId, blogData): any {
let myResponse = this._http.put(this.baseUrl + '/' + blogId + '/edit' , blogData);
return myResponse;
}
}
my nodejs routes with controller for blog-view
app.get(baseUrl+'/view/:blogId', (req, res) => {
BlogModel.findOne({ 'blogId': req.params.blogId }, (err, result) => {
if (err) {
console.log(err)
res.send(err)
} else if (result == undefined || result == null || result == '') {
console.log('No Blog Found')
res.send("No Blog Found")
} else {
res.send(result)
}
})
});
Below is a sample document object from which the frontend should render
{
"_id": "5e0e8ac6dcfc4e2008390cdf",
"blogId": "XAY2Qlhb",
"__v": 0,
"lastModified": "2020-01-03T00:28:54.638Z",
"created": "2020-01-03T00:28:54.638Z",
"tags": [
"english movies, action movies"
],
"author": "Decardo",
"category": "Hollywood custom",
"isPublished": true,
"views": 8,
"bodyHtml": "<h1>Heading of the body</h1>\n<p>This is the first blog data getting uploaded n blog project</p>",
"description": "long description>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
"title": "Blog Title 1 custom"
}
This line of code will first store a Subscription in this.currentBlog:
this.currentBlog = this.blogHttpService.getSingleBlogInformation(myBlogId).subscribe(...)
Then the subscription will be overwritten by this.currentBlog = data["data"];
this.currentBlog = is actually not needed there.
Not sure if that is the problem. At least it is not proper :)
I have a component with a forum (redux-form) that adds a food to the data. After I click "ADD" the information I add in the form is added to the database, however, the component that renders the list of food does not update automatically unless I manually refresh the page. Then the new food is rendered in the component.
Not sure why this is not rendering the new information automatically
Stock.js (where the food list is rendered)
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFoodList, addToPot, deleteFood } from '../actions';
import Header from './header';
import Pot from './pot';
import AddFood from './add_food';
import RecipeList from './recipe_list';
import classes from "../index.css";
import { Row, Col } from 'react-flexbox-grid';
class FoodList extends Component {
componentDidMount () {
this.props.fetchFoodList();
}
addIngredientToPot = (ev) => {
const val = ev.target.dataset.value;
const newPot = [ ...this.props.pot,
val
];
this.props.addToPot(newPot)
}
onDeleteClick = (ev) =>{
// const {id} = this.props.match.params;
const val = ev.target.dataset.value;
this.props.deleteFood(val);
}
displayFoodList() {
return _.map(this.props.foods, food => {
return (
<li key={food._id} className={classes.tablerow}>
<div data-value={food.name}
onClick={this.addIngredientToPot.bind(this)}
className={classes.col1}>{food.name}
</div>
<div className={classes.col2}>{food.type}</div>
<div className={classes.col3}>
<button data-value={food._id}
onClick={this.onDeleteClick.bind(this)}
className={classes.throwoutbutton}
> Throw Out
</button>
</div>
</li>
);
});
}
render () {
// console.log(this.props.foods);
// console.log(this.props.pot)
return (
<div className={classes.stockpilecontainer}>
<Header />
<Row>
<Col lg >
<h2>StockPile</h2>
<ul className={classes.responsivetable}>
<li className={classes.tableheader}>
<div className={classes.col1}>Name</div>
<div className={classes.col2}>Type</div>
<div className={classes.col3}>Throw Out</div>
</li>
{this.displayFoodList()}
</ul>
<div>
<AddFood/>
</div>
</Col>
<Col/>
<Pot/>
<Col/>
</Row>
<Row>
<Col xs={12}>
<Row center="xs">
<RecipeList xs={6} />
</Row>
</Col>
</Row>
</div>
);
};
}
function mapStateToProps(state) {
return {
foods: state.foods,
pot: state.pot.pot,
};
}
export default connect (mapStateToProps, { fetchFoodList, addToPot, deleteFood })(FoodList);
Newfood.js (component with form to add a new food item)
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { addFood } from '../actions';
import classes from "../index.css";
import { Redirect } from 'react-router-dom';
const renderField= field => {
const { input, type } = field;
return (
<div className={classes.addfoodinput}>
<label className={classes.addfoodlabel}>{field.label}</label>
<input {...input} type={type} className="form-control" />
</div>
)
}
class AddFood extends Component {
onSubmit(values) {
this.props.addFood(values);
}
render () {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))} className={classes.addfoodform}>
<Field
label="Food "
name="name"
component={renderField}
/>
<Field
label="Type "
name="type"
component={renderField}
/>
<button type="submit" className={classes.addfoodbutton} >ADD</button>
<button className={classes.addfoodbutton}>Cancel</button>
{/* <Redirect to={'/stockpile'} /> */}
</form>
)
}
};
AddFood = reduxForm({
form: 'NewFoodForm',
fields: ['name', 'type'],
})(AddFood);
export default connect(null, {addFood})(AddFood)
Reducer for the food list:
import _ from 'lodash';
import { FETCH_FOODLIST } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case FETCH_FOODLIST:
return _.mapKeys(action.payload.data, '_id');
default:
return state;
}
}
Action creators for adding new food and get the food list:
// Action to get the food list:
export const fetchFoodList = () => dispatch => {
Axios
.get(`${ROOT_URL}/myfoodlist`, {
headers: { auth: localStorage.getItem('token') }
})
.then( response =>
dispatch({
type: FETCH_FOODLIST,
payload: response
}));
};
// Add a food to the DB:
export function addFood (values, history) {
return function (dispatch) {
Axios.post(`${ROOT_URL}/myfoodlist/foods`, values)
.then( response => {
dispatch ({
type: ADD_NEW_FOOD,
payload: response
});
// history.push('/');
history.pushState(null, '/stockpile');
})
.catch( () => {
});
};
}
I have no luck with adding spinner/progress bar to the page.
Tried many different libraries , without no success.
I need it to show on
//SHOW SPINNER
and then hide on
//HIDE SPINNER
line in the bellow code
Any help appreciated.
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { deleteUser } from '../actions/Actions';
import { updateUser as APIUpdateUser, addUser as APIAddUser } from '../utils/APIUtils';
import language from '../language';
import scrollToElement from 'scroll-to-element';
import CarsSelect from './CarsSelect';
var _ = require('lodash');
function mapStateToProps(state) {
return {
};
}
class UserTableRow extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
number: PropTypes.number.isRequired,
}
static contextTypes = {
store: React.PropTypes.object
}
constructor(props) {
console.log("Progress");
super(props);
this.state = {
edit: false,
data: props.data
};
this._handleChange = this._handleChange.bind(this);
this._handleCarChange = this._handleCarChange.bind(this);
this._startEdit = this._startEdit.bind(this);
this._cancelEdit = this._cancelEdit.bind(this);
this._handleSave = this._handleSave.bind(this);
this._handleShow = this._handleShow.bind(this);
}
componentDidMount() {
const {data} = this.props;
if (typeof data.new !== 'undefined' && data.new === true) {
this.setState({
edit: true
});
scrollToElement(this.refs.row, {
offset: 0,
ease: 'out-bounce',
duration: 1000
});
}
}
componentWillReceiveProps(nextProps) {
this.setState({
data: nextProps.data,
});
}
render() {
const {number} = this.props;
const {data} = this.state;
const lang = language[language.default];
var showClass = 'more';
var rowHidden = 'hidden';
if (this.state.more === true) {
showClass = 'less';
rowHidden = 'visible';
}
var editClass = '';
if (this.state.edit === true) {
editClass = 'table__row--edit';
}
return (
<div>
<div ref='row' className={'table__row table__row--outer ' + editClass}>
<div className='table__elements-wrapper'>
<div className='table__element'>{number}</div>
<div className='table__element'>
<span className='table__editable-data'>{data.email}</span>
<input onChange={this._handleChange.bind(this, 'email')} className='table__editable-hidden' ref='email' name='email' type='email' value={data.email} />
</div>
<div className='table__element'>
<span className='table__editable-data'>{data.firstName}</span>
<input onChange={this._handleChange.bind(this, 'firstName')} className='table__editable-hidden' ref='firstName' name='firstName' type='text' value={data.firstName} />
</div>
<div className='table__element'>
<span className='table__editable-data'>{data.lastName}</span>
<input onChange={this._handleChange.bind(this, 'lastName')} className='table__editable-hidden' ref='lastName' name='lastName' type='text' value={data.lastName} />
</div>
<div className='table__element'>
<span className='table__editable-data'>{data.phone}</span>
<input onChange={this._handleChange.bind(this, 'phone')} className='table__editable-hidden' ref='phone' name='phone' type='text' value={data.phone} />
</div>
<div className='table__element'>
<span className='table__editable-data'>{lang.userArr[data.type]}</span>
<select onChange={this._handleChange.bind(this, 'type')} className='table__editable-hidden' ref='type' name='type' type='text' value={data.type}>
<option value='0'>{lang.userArr[0]}</option>
<option value='1'>{lang.userArr[1]}</option>
<option value='2'>{lang.userArr[2]}</option>
</select>
</div>
<div className='table__element'>
<input className='table__editable-hidden' ref='password' name='password' type='password' />
</div>
<div className='table__element table__element--avatar'>
<img lightbox='lightbox' className='table__editable-data' src={data.image} alt=''/>
<input className='table__editable-hidden' type='file' ref='image' name='image' />
</div>
<div className='table__element'>
{(() => {
if (data.superAdmin !== true) {
return <div className='table__buttons'>
<div onClick={this._startEdit} className='table__button table__button--edit'></div>
<div onClick={this._cancelEdit} className='table__button table__button--cancel '></div>
<div onClick={this._handleSave} className='table__button table__button--ok'></div>
</div>;
} else {
return false;
}
})()}
</div>
<div className='table__element'><div onClick={this._handleShow} className={'table__show table__show--' + showClass}></div></div>
</div>
<div ref='hiddenRow' className={'table__row table__row--inner table__row--' + rowHidden}></div>
</div>
</div>
);
}
_handleShow() {
var more = this.state.more;
if (!more) {
ReactDOM.render(<CarsSelect handleCarChange={this._handleCarChange} user={this.state.data} />, this.refs.hiddenRow);
} else {
ReactDOM.unmountComponentAtNode(this.refs.hiddenRow);
}
this.setState({
more: !more
});
}
_handleCarChange(e) {
var {data} = this.state;
var values = [];
var userValues = [];
_.each(e, function(item) {
values.push({nameplate: item.label, _id: item.value});
userValues.push(item.value);
});
data.cars = values;
this.setState({
data: data
});
var user = {};
user._id = data._id;
user.cars = userValues;
APIUpdateUser(user);
}
_handleChange(type, event) {
var data = this.state.data;
data[type] = event.target.value;
this.setState({
data: data
});
}
_startEdit() {
this.setState({
edit: true
});
}
_cancelEdit() {
const {data} = this.props;
if (data.new === true) {
this.context.store.dispatch(deleteUser(data));
}
this.setState({
edit: false
});
}
_handleSave() {
//SHOW SPINNER
const {data} = this.props;
var user = {};
user._id = data._id;
user.email = this.refs.email.value;
user.firstName = this.refs.firstName.value;
user.lastName = this.refs.lastName.value;
user.phone = this.refs.phone.value;
user.type = this.refs.type.value;
if (this.refs.password.value) {
user.password = this.refs.password.value;
}
if (this.refs.image.files[0]) {
var reader = new window.FileReader();
reader.readAsDataURL(this.refs.image.files[0]);
reader.onloadend = function() {
user.image = reader.result;
console.log(user.image);
if (data.new === true) {
this.context.store.dispatch(deleteUser(data));
APIAddUser(user);
} else {
APIUpdateUser(user);
}
};
} else {
if (data.new === true) {
this.context.store.dispatch(deleteUser(data));
APIAddUser(user);
} else {
APIUpdateUser(user);
}
}
this.setState({
edit: false
});
//HIDE SPINNER
}
}
var getOptions = function(input, callback) {
setTimeout(function() {
callback(null, {
options: [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
],
// CAREFUL! Only set this to true when there are no more options,
// or more specific queries will not be sent to the server.
complete: true
});
}, 500);
};
export default connect(mapStateToProps)(UserTableRow);
Install
$ npm install react-spinkit --save
Usage
import Spinner from 'react-spinkit';
<Spinner spinnerName='double-bounce' />
see more here https://github.com/KyleAMathews/react-spinkit