Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material UI - Open LeftNav / Drawer on AppBar click

Tags:

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;
like image 726
T00rk Avatar asked Feb 24 '15 09:02

T00rk


2 Answers

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 ...
like image 101
steinso Avatar answered Sep 27 '22 22:09

steinso


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.

like image 26
dbarth Avatar answered Sep 27 '22 20:09

dbarth