T00rk
T00rk

Reputation: 2287

Material UI - Open LeftNav / Drawer on AppBar click

I'm discovering ReactJS and material-ui (http://material-ui.com/).

I try to create a default template for my application. I want to use an AppBar and a LeftNav (renamed in Drawer in new versions) in separated components.

The AppBar has a menu button on it by default. I want to use it to open the LeftNav but I don't know how I can call my LeftBarComponent component function to open it.

I've almost understood how to communicate between components but in my case, I don't know how I could do because there is no documentation about it. The only thing I know to open the LeftNav element is to use LeftNav.toggle()

http://material-ui.com/#/components/left-nav

Thanks for your help.

Default.jsx

use strict';

var React = require('react');
var pageStore = require('../../stores/page');

var MainNavbar = require('../modules/navbar.jsx');
var LeftNavbar = require('../modules/leftbar.jsx');

var getState = function() {
  return {
    title: pageStore.get().title
  };
};

var DefaultComponent = React.createClass({
  mixins: [pageStore.mixin],
  componentDidMount: function() {
    pageStore.emitChange();
  },
  getInitialState: function() {
    return getState();
  },
  render: function() {
    return (
      /* jshint ignore:start */
      <div className="main-container">
        <MainNavbar />
        <LeftNavbar />
        <div className="content">
          {this.props.children}
        </div>
      </div>
      /* jshint ignore:end */
    );
  },
  // Event handler for 'change' events coming from store mixins.
  _onChange: function() {
    this.setState(getState());
  }
});

module.exports = DefaultComponent;

navbar.jsx

'use strict';

var React = require('react');
var routeActions = require('../../actions/routes');

var MUI = require('material-ui');
var AppBar = MUI.AppBar;

// Navbar Component
// Application main menu
// Usage: <Navbar />
var NavbarComponent = React.createClass({
  render: function() {
    return (
      /* jshint ignore:start */
      <AppBar title="MyApp" onMenuIconButtonTouchTap={ this._handleClick }>
        <div className="clear"></div>
      </AppBar>
      /* jshint ignore:end */
    );
  },
  _handleClick: function()
  {
    console.log('ok');
  }
});

module.exports = NavbarComponent;

leftbar.jsx

'use strict';

var React = require('react');
var routeActions = require('../../actions/routes');

var MUI = require('material-ui');
var LeftNav = MUI.LeftNav;
var MenuItem = MUI.MenuItem;

var menuItems = [
  { route: 'home', text: 'Home' },
  { route: 'about', text: 'About' },
];

// LeftBar Component
// Application left menu
// Usage: <LeftBar />
var LeftBarComponent = React.createClass({
  render: function() {
    return (
      /* jshint ignore:start */
      <LeftNav menuItems={menuItems} docked={false} />
      /* jshint ignore:end */
    );
  },
  _handleClick: function()
  {
    console.log('ok');
  }
});

module.exports = LeftBarComponent;

Upvotes: 21

Views: 17937

Answers (3)

Rohit Luthra
Rohit Luthra

Reputation: 1286

I am using React with Material-UI and I have done this using onLeftIconButtonTouchTap={this.handleToggle} event in AppBar. For more detail, you can refer official documentation http://www.material-ui.com/#/components/app-bar

App.js

import React from 'react';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';  
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; 
import Dashboard from './Dashboard/dashboard.js';
import Information from './Information/information.js';
import './App.css';

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {open: false};
  }

  handleToggle = (event) => this.setState({open: !this.state.open});

  render() {

    const {styleFromProps} = this.props;
    const contentStyle = {  ...styleFromProps, transition: 'margin-left 450ms cubic-bezier(0.23, 1, 0.32, 1)' };

    if (this.state.open) {
      contentStyle.marginLeft = 256;
    }

    return (
        <Router>
          <div>
          <AppBar title="AppTitle" onLeftIconButtonTouchTap={this.handleToggle} />

            <Drawer containerStyle={{height: 'calc(100% - 64px)', top: 64}} docked={true} width={200} open={this.state.open} zDepth={2}>
              <Link to="/dashboard" style={{ textDecoration: 'none' }}><MenuItem>Dashboard</MenuItem></Link>
              <Link to="/information" style={{ textDecoration: 'none' }}><MenuItem>Information</MenuItem></Link>
            </Drawer>

            <Route path="/dashboard" component={(props) => <Dashboard {...props} open={this.state.open}/>} />
            <Route path="/information" component={(props) => <Information {...props} open={this.state.open}/>} />
          </div>
        </Router>
    );
  }
}

export default App;

I think It will help you.

Upvotes: 1

dbarth
dbarth

Reputation: 238

To clarify what steinso said, you need to send the event to an Action using the Flux architecture and then change it in a Store. Then dispatch an emit change event with EventEmitter. This will fire off a render on the effected components with the updated state.

https://facebook.github.io/flux/

If you must have them separated then, that would be the best way. However, if you can have them in the same component, such as 'Main' then, you can simply use a ref attribute and reference the component.

module.exports = React.createClass({

  childContextTypes: {
    muiTheme: React.PropTypes.object
  },

  getChildContext: function() {
    return {
      muiTheme: ThemeManager.getCurrentTheme()
    };
  },

  _toggleNav: function(){
    this.refs.leftNav.toggle();
  },

  render: function() {
    return (
      <div>
        <mui.LeftNav
          ref='leftNav'
          menuItems={menuItems}
          docked={false} />
        <mui.AppBar
          title='Default'
          onLeftIconButtonTouchTap={this._toggleNav} />
      </div>
    );
  }

});

Like so.

Upvotes: 5

steinso
steinso

Reputation: 167

In this case you need to pass the event upwards through your components, and then down again.

    <MainNavbar onClickMenu=this.onClickMenu/>
    <LeftNavbar isOpen=this.state.leftNavIsOpen/>

However, using this in conjunction with Reactjs is not optimal, as the LeftNav material component does not react to attributes, but to method calls like you were saying.

To work around, you could put a check in the LeftNav render method, that checks the current state, and calls toggle() if needed. In principle this goes against the React way of thinking, as a state is contained inside the specific child component and not in the DefaultComponent that handles it.

render:function(){
  if(this.props.isOpen){
     LeftNav.open();
  }
  return ...

Upvotes: 6

Related Questions