Шаблон не загружается на боковую панель без обновления (React) - PullRequest
0 голосов
/ 20 января 2019

Я новичок в React, и я пытался загрузить некоторые шаблоны на внутренний сервер, чтобы он отображался на боковой панели, однако он не работает, пока страница не обновится (я полагаю, что страница должна отображаться снова ). Я реализовал перезагрузку окна по нажатию кнопки загрузки, но мне было интересно, есть ли лучшее решение для этого. Мои коды для шаблонов ниже. Любая помощь будет принята с благодарностью, поскольку я новичок.

Backend 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>&nbsp;</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>&nbsp;</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;
...