我是React的新手,我一直试图将一些模板上传到后端服务器,以使其显示在侧栏上,但是直到刷新页面后它才起作用(我猜是页面需要再次呈现) )。我单击了上传按钮就实现了窗口重新加载,但是我想知道是否有更好的解决方案来做到这一点。我的模板代码如下。当我是新手时,任何帮助将不胜感激。
后端App.js
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const fileUpload = require('express-fileupload');
const cors = require('cors');
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(cors());
app.use(fileUpload());
app.use('/public', express.static(__dirname + '/public'));
app.post('/upload', (req, res, next) => {
console.log(req);
let xmlFile = req.files.file;
xmlFile.mv(`${__dirname}/public/${req.body.filename}.xml`, function(err) {
if (err) {
return res.status(500).send(err);
}
res.json({file: `public/${req.body.filename}.xml`});
});
})
app.get('/file', function (req, res) {
const fs = require('fs');
//joining path of directory
const directoryPath = path.join(__dirname, 'public');
//passsing directoryPath and callback function
fs.readdir(directoryPath, function (err, files) {
//handling error
if (err) {
return console.log('Unable to scan directory: ' + err);
} else {
res.json({files: files});
}
//listing all files using forEach
files.forEach(function (file) {
// Do whatever you want to do with the file
console.log(file);
});
});
});
app.get('/file/:fileName', function (req, res) {
const fs = require('fs');
console.log(req.params);
const directoryPath = path.join(__dirname, 'public');
fs.readFile(directoryPath + '\\'+ req.params.fileName, 'utf8', function (err, data) {
//handling error
if (err) {
return console.log('Unable to read file: ' + err);
} else {
res.json({content: data});
}
});
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(8000, () => {
console.log('8000');
});
module.exports = app;
`
App.tsx
import * as React from 'react';
import './sass/App.css';
import 'primereact/resources/primereact.min.css';
import 'primereact/resources/themes/omega/theme.css';
import 'font-awesome/css/font-awesome.css';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import './resources/primereact.css';
import * as classNames from 'classnames';
import { BrowserRouter as Router, Redirect, Route, Switch, Link } from 'react-router-dom';
import Sidebar from './Sidebar';
import Home from './Home';
import CapitalHomelessness from './CapitalHomelessness';
import FormTemplateComponent from './FormTemplateComponent';
import { FileUploadComponent } from './FileUploadComponent';
import { Menu } from 'primereact/components/menu/Menu';
interface AppProps { }
interface AppState {
loadedContent: Document;
loadedCapitalHomelessnessContent: Document;
menuActive: boolean;
langCo: string;
items: any;
}
class App extends React.Component<AppProps, AppState> {
loadedContent: Document;
formItems = [];
menu: Menu;
constructor(props: AppProps) {
super(props);
this.state = {
loadedContent: null,
loadedCapitalHomelessnessContent: null,
menuActive: false,
langCo: 'en',
items: [
{
label: 'Admin',
items: [
{label: 'Home', icon: 'fa fa-home',
command: () => { window.location.href = '/Home'; }},
{label: 'New Template', icon: 'fa fa-plus-square',
command: () => { window.location.href = '/FileUploadComponent'; }},
{label: 'List Template', icon: 'fa fa-list-ol',
command: () => { window.location.href = '/CapitalHomelessness'; }}
]
},
{
label: 'Form',
items: this.formItems
}
],
};
this.openMenu = this.openMenu.bind(this);
this.closeMenu = this.closeMenu.bind(this);
this.onSidebarClick = this.onSidebarClick.bind(this);
this.getMenuItems = this.getMenuItems.bind(this);
this.switchLang = this.switchLang.bind(this);
}
async componentWillMount() {
await this.getMenuItems();
this.setState({ menuActive: !this.state.menuActive });
}
componentDidMount() {
// this.formItems = [];
// this.getMenuItems();
// tslint:disable-next-line:no-console
console.log(this.state.items);
}
// tslint:disable-next-line:typedef
openMenu(event) {
// tslint:disable-next-line:no-console
console.log(event);
this.setState({ menuActive: !this.state.menuActive });
event.preventDefault();
}
// tslint:disable-next-line:typedef
closeMenu(event) {
this.setState({ menuActive: false});
event.preventDefault();
}
// tslint:disable-next-line:typedef
onSidebarClick(event) {
// tslint:disable-next-line:no-console
console.log(event);
if (event.target.nodeName === 'A' && event.target.parentNode.className.indexOf('layout-menu') === -1) {
this.closeMenu(event);
}
}
async getMenuItems() {
const response = await fetch('http://localhost:8000/file');
const json = await response.json();
json.files.forEach(async element => {
this.formItems.push({
label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
command: async () => {
window.location.href = `/FormLoading/:${element}`;
this.forceUpdate();
}
});
});
}
switchLang() {
const newLancCode = (this.state.langCo === 'fr' ? 'en' : 'fr');
this.setState({ langCo: newLancCode });
}
render() {
return (
<Router>
<div className="layout-wrapper">
<div id="layout-topbar">
<a className="menu-button" onClick={this.openMenu}>
<i className="fa fa-bars" />
</a>
<Link to="/" className="logo">
<img alt="logo" src="resources/images/ontario-logo.png" id="top-logo" height="50px" width="100px"/>
</Link>
<a className="title">
<h3>Dynamic Template Form</h3>
</a>
<ul className="topbar-menu">
<li>
<Link to="/signin"> Sign in </Link>
</li>
<li>
<Link to="#" onClick={this.switchLang}> {this.state.langCo === 'fr' ? 'english' : 'Français'} </Link>
</li>
</ul>
</div>
{/* tslint:disable-next-line:max-line-length */}
<div id="layout-sidebar" className={classNames({ 'active': this.state.menuActive === true })} onClick={this.onSidebarClick}>
<div className="nano-content">
<div>
<Menu model={this.state.items} ref={el => this.menu = el}/>
</div>
</div>
</div>
<div id="layout-content">
<Switch>
<Route path="/" exact={true} component={Home} />
<Route
path="/Home"
render={(props) => <Home {...props} />}
/>
<Route
path="/FileUploadComponent"
render={(props) => <FileUploadComponent {...props} />}
/>
<Route
path={`/FormLoading/:fileName`}
render={(props) => <FormTemplateComponent {...props} langCo={this.state.langCo}/>}
/>
</Switch>
</div>
</div>
</Router>
);
}
}
export default App;
`
FileUploadComponent.tsx
import * as React from 'react';
import { Growl, GrowlMessage } from 'primereact/components/growl/Growl';
import { Panel } from 'primereact/components/panel/Panel';
import { Button } from 'primereact/components/button/Button';
import sidebar from './Sidebar';
interface Props {
}
interface State {
imageURL: string;
}
export class FileUploadComponent extends React.Component<Props, State> {
growl: Growl;
fileName: HTMLInputElement;
uploadInput: HTMLInputElement;
constructor(props: Props) {
super(props);
this.state = {
imageURL: '',
};
this.setState(this.state);
this.handleUploadImage = this.handleUploadImage.bind(this);
}
handleUploadImage(ev: any) {
ev.preventDefault();
const data = new FormData();
data.append('file', this.uploadInput.files[0]);
data.append('filename', this.fileName.value);
fetch('http://localhost:8000/upload', {
method: 'POST',
body: data,
}).then((response) => {
response.json().then((body) => {
this.setState({ imageURL: `http://localhost:8000/${body.file}` });
});
});
// this.growl.props.show({severity: 'info', summary: 'Success', detail: 'File Uploaded', closable: true, sticky: false, life: 1});
// tell sidebar component to update its menu items
}
reloadPage () {
window.location.reload();
}
render() {
return (
<Panel header="Template Uploading">
<form onSubmit={this.handleUploadImage}>
<div className="ui-g-12">
<input ref={(ref) => { this.uploadInput = ref; }} type="file" />
</div>
<div className="ui-g-12">
<input ref={(ref) => { this.fileName = ref; }} type="text" placeholder="Enter the name of file" /> *Required
</div>
<br />
<div>
<button onClick={this.reloadPage}>Upload</button>
</div>
<Growl ref={(el) => { this.growl = el; }} />
</form>
</Panel >
);
}
}
export default FileUploadComponent
Sidebar.tsx
import * as React from 'react';
import { Link } from 'react-router-dom';
import 'nanoscroller';
import * as $ from 'jquery';
import * as classNames from 'classnames';
import { Menu } from 'primereact/components/menu/Menu';
interface AppProps { loadFileContent: (fileName: string) => any; }
interface AppState {
items: any;
loaded: boolean;
}
class Sidebar extends React.Component<AppProps, AppState> {
scrollContainer: any;
formItems = [];
constructor(props: AppProps) {
super(props);
this.state = {
items: [
{
label: 'Admin',
items: [{label: 'New Template', icon: 'fa fa-plus-square',
command: () => { window.location.href = '/FileUploadComponent'; }},
{label: 'List Template', icon: 'fa fa-list-ol',
command: () => { window.location.href = '/CapitalHomelessness'; }}
]
},
{
label: 'Form',
items: this.formItems
}
],
loaded: false
};
this.openMenu = this.openMenu.bind(this);
this.getMenuItems = this.getMenuItems.bind(this);
}
// tslint:disable-next-line:typedef
openMenu(event, val) {
setTimeout(() => $(this.scrollContainer).nanoScroller(), 350);
event.preventDefault();
}
componentDidMount() {
// this.getMenuItems();
$(this.scrollContainer).nanoScroller({ flash: true });
// get all templates from Templates folder
}
componentWillMount() {
// tslint:disable-next-line:no-console
console.log('will mount');
this.getMenuItems();
}
componentWillUnmount() {
$(this.scrollContainer).nanoScroller({ destroy: true });
}
async getMenuItems() {
const request = async () => {
const response = await fetch('http://localhost:8000/file');
const json = await response.json();
json.files.forEach(async element => {
var loadedContetnt = await this.props.loadFileContent(element);
this.formItems.push({
label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
data: loadedContetnt,
command: async () => {
// var loadedContetnt = await this.props.loadFileContent(element);
// tslint:disable-next-line:no-console
console.log(loadedContetnt);
window.location.href = '/FormLoading';
}
});
});
};
request();
// fetch('http://localhost:8000/file', {
// method: 'GET'
// }).then((response) => {
// response.json().then((body) => {
// body.files.forEach(element => {
// // tslint:disable-next-line:no-console
// console.log('command');
// this.formItems.push({
// label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
// command: () => {
// var loadedContetnt = this.props.loadFileContent(element);
// // tslint:disable-next-line:no-console
// console.log(loadedContetnt);
// // window.location.href = '/FormLoading';
// }
// });
// });
// });
// this.setState({ loaded: true });
// });
// tslint:disable-next-line:no-console
console.log(this.state.items);
}
render() {
return (
<div ref={(el) => this.scrollContainer = el} className="nano">
<div className="nano-content">
<div>
<Menu model={this.state.items} />
</div>
{/* <div className="layout-menu">
<div>
<Link className="fa fa-home" to="/">Home</Link>
</div>
<div> </div>
<div>
<Link className="fa fa-wpforms" to="/CapitalHomelessness">Rent Supplement Form</Link>
</div>
<div>
<Link className="fa fa-wpforms" to="/RentSupplement">Rent Supplement Form (New)</Link>
</div>
<div>
<Link className="fa fa-wpforms" to="/TransformationHousing">Trans. Housing Form (New)</Link>
</div>
<div> </div>
<div>
<Link className="fa fa-upload" to="/upload">Upload Form Template</Link>
</div>
<div>
<Link className="fa fa-database" to="/retrieve">Retrieve Form Template</Link>
</div>
</div> */}
</div>
</div>
);
}
}
export default Sidebar;