emberjs - how to mark active menu item using router infrastructure

前端 未结 15 1407
悲&欢浪女
悲&欢浪女 2020-11-27 13:06

I\'m trying to create navigation tabs (taken from Twitter Bootstrap):

相关标签:
15条回答
  • 2020-11-27 13:27

    A lot of the proposed solutions here do not work for any recent Ember version (e.g. views being deprecated). Moreover just using the link-to helper will not solve the issue, as bootstrap expects the active class to be present on the <li> not the <a>!

    So I will try to summarize the solutions that do in fact work as of now:

    use ember-cli-active-link-wrapper

    The addon provides a component for this special use case:

    <ul class="nav nav-tabs">
      {{#active-link}}
        {{#link-to "foo"}}Foo{{/link-to}}
      {{/active-link}}
      {{#active-link}}
        {{#link-to "bar"}}Bar{{/link-to}}
      {{/active-link}}
    </ul>
    

    Taken from https://stackoverflow.com/a/29939821/5556104

    use ember-bootstrap

    ember-bootstrap provides many components that integrate bootstrap functionality into your ember app, amongst them the nav components:

    {{#bs-nav type="tabs"}}
       {{#bs-nav-item}}
          {{#link-to "foo"}}Foo{{/link-to}}
       {{/bs-nav-item}}
       {{#bs-nav-item}}
          {{#link-to "bar"}}Bar{{/link-to}}
       {{/bs-nav-item}}
     {{/bs-nav}}
    

    Taken from https://stackoverflow.com/a/38279975/5556104

    link-to Hack

    Somewhat hacky, but should work without any additional addon:

    <ul class="nav nav-tabs">
      {{#link-to "foo" tagName="li"}} 
        {{#link-to "foo"}}Foo{{/link-to}}
      {{/link-to}}
      {{#link-to "bar" tagName="li"}} 
        {{#link-to "bar"}}Bar{{/link-to}}
      {{/link-to}}
    </ul>
    

    Taken from https://stackoverflow.com/a/23966652/5556104

    0 讨论(0)
  • 2020-11-27 13:35

    Not sure if it's very dynamic but try to see solution at http://codebrief.com/2012/07/anatomy-of-an-ember-dot-js-app-part-i-redux-routing-and-outlets/ The main idea is to check state of your app

    JavaScript:

    function stateFlag(name) {
      return Ember.computed(function() {
        var state = App.router.currentState;
        while(state) {
          if(state.name === name) return true;
          state = state.get('parentState');
        }
        return false;
      }).property('App.router.currentState');
    }
    
    ApplicationController: Ember.Controller.extend({
        isHome: stateFlag('home'),
        isSections: stateFlag('sections'),
        isItems: stateFlag('items')
      })
    

    Handlebars:

    <li class="home" {{bindAttr class="isHome:active"}}>
    </li>
    <li class="sections" {{bindAttr class="isSections:active"}}>
    </li>
    <li class="items" {{bindAttr class="isItems:active"}}>
    </li>
    

    Update: pangratz's solution looks prettier

    0 讨论(0)
  • 2020-11-27 13:42

    Some of the above suggestions are still valid for twitter bootstrap case. You can also try something like this

    {{#link-to 'dashboard' tagName='li'}} 
      {{#link-to 'dashboard'}}Link Title{{/link-to}}
    {{/link-to}}
    
    1. The link-to with li tagName applies the active class to the li
    2. The inner link-to would be a anchor element which gives you Open in New Tab functionality when right-clicked
    0 讨论(0)
  • 2020-11-27 13:42

    You can also change the isActive method into something like this:

    isActive: function() {
        return App.getPath('router.currentState.path') === "root.firms";
    }.property("App.router.currentState"),
    

    or

    isActive: function() {
        return this.get('controller.target.currentState.path') === "root.firms";
    }.property("controller.target.currentState"),
    
    0 讨论(0)
  • 2020-11-27 13:43

    If you're using Ember >= 1.11, then https://stackoverflow.com/a/14501021/65542 below is the correct answer.


    I would create a NavigationView, see http://jsfiddle.net/pangratz666/z8ssG/:

    Handlebars:

    <script type="text/x-handlebars" data-template-name="navigation">
        <ul class="nav nav-tabs">
            {{#view view.NavItemView item="home" }}
                <a {{action gotoHome}} >Home</a>
            {{/view}}
            {{#view view.NavItemView item="profiles" }}
                <a {{action gotoProfiles}} >Profiles</a>
            {{/view}}
            {{#view view.NavItemView item="messages" }}
                <a {{action gotoMessages}} >Messages</a>
            {{/view}}        
        </ul>
    </script>
    

    JavaScript:

    App.NavigationView = Em.View.extend({
        templateName: 'navigation',
        selectedBinding: 'controller.selected',
        NavItemView: Ember.View.extend({
            tagName: 'li',
            classNameBindings: 'isActive:active'.w(),
            isActive: function() {
                return this.get('item') === this.get('parentView.selected');
            }.property('item', 'parentView.selected').cacheable()
        })
    });
    

    And inside your route's connectOutlets you have to set the current navigation item via router.set('navigationController.selected', 'home'); ...


    Also take a look at the ember-bootstrap repository, which wraps this and more features of Bootstrap inside Ember.js

    0 讨论(0)
  • 2020-11-27 13:43

    Sooner or later want to change the naming of your states or whatever you have to go through the code AND the view as well, also adding a function to transitionTo every route seems not desirable. My approach is a bit more programmatic and modularized:

    # Parent View-Tamplate, holding the navbar DOM elements
    App.NavView = Ember.View.extend( 
      controller: App.NavArrayController
      templateName: "ember-nav"
    )
    # We push NavItems into this array
    App.NavArrayController = Ember.ArrayController.create(
      content: Ember.A([])
    )
    # NavItem has two settable properties and 
    # an programmatic active state depending on the router
    App.NavItem = Ember.Object.extend(
      title: ''
      goto: null    # <=this is the name of the state we want to go to!
      active: (->
        if App.router.currentState.name == @.get "goto"
          true
        else
          false
      ).property('App.router.currentState.name').cacheable()
    )
    # the actual NavElement which gets the class="active" if the 
    # property "active" is true, plus a on-click binding to
    # make the Router transition to this state
    App.NavItemView = Ember.View.extend(
     tagName: "li"
      classNameBindings: ["active"]
      click: ->
        App.router.transitionTo(@get('goto'))
        false
    )
    

    nav-view.hbs (for twitter-bootstrap-style navs)

    <div class="nav-collapse collapse">
      <ul class="nav">
        {{#each App.NavArrayController}}
          {{#view App.NavItemView classBinding="active" gotoBinding="goto"}}
            <a href="#" {{bindAttr data-goto="goto"}}> {{title}}</a>
          {{/view}}
        {{/each}}
      </ul>
    </div>
    

    This way, I can just create and mess around with my routes in the Router, and keep the Nav-Definitions side-by-side:

    # put this somewhere close to the Router 
    App.NavArrayController.pushObjects(
      [
        App.NavItem.create(
          title: 'Home'
          goto: 'home'
        ),
        App.NavItem.create(
          title: 'Chat'
          goto: 'chat'
        ),
        App.NavItem.create(
          title: 'Test'
          goto: 'test'
        )
      ]
    )
    
    0 讨论(0)
提交回复
热议问题