使用Ember进行导航的正确方法

28

什么是导航侧边栏的“正确方式”(或者至少是选项,如果没有单一的“Ember方式”来完成这个)?我应该以某种方式查看ContainerViews,还是应该只使用新的outlet功能并将导航置于我的应用视图内?

此外,根据URL(我正在使用路由),设置li上的.active类的“正确方法”是什么?是否有一些辅助函数可以实现这个?

2个回答

35

<更新日期="2013-01-16">

由于路由器API的最新更改,之前的示例已不再有效,因此我不会修复它们。所有使用旧路由器的应用程序都应尽快迁移到最新版本。

这是一个更新后的fiddle: 源代码 | 演示

我将定期维护/修复/添加功能。

编辑于2013年4月3日:

另一个示例-WIP:源代码 | 演示

以下所有内容已过时

<更新>

2012年11月9日

只需添加一个链接到一个更好的示例,该示例还解决了OP希望在选择给定路由时设置当前导航栏项类的部分

JSFiddle中查看源代码(单击此处以查看其运行情况)

如果您查看NavigationController,您将看到“selected”属性,我在其中使用它来在子视图NavigationItemView的isActive方法中检查。 isActive将根据相同视图中menu属性的值和控制器中selected属性的值返回true或false。然后检查classNameBinding表达式,这些表达式将 'active'或无设置为该特定子项的类。 还要检查ConnectOutlets,这是我标记nav项为选定项的位置。

此操作正在最新的Ember上运行。 我还使用了一些Ember.Bootstrap以及一些原始Twitter Bootstrap功能/类等(但我已将样式替换为metro-bootstrap)。

不会在此处粘贴整个内容,因为空间有限。我将保留原始代码和链接以供参考。

我会继续更新这个新示例,因为Ember很有趣=)

</Update>

这个 fiddle 显示了一个静态导航栏的视图,outlet 仅用于内容显示,或者你可以直接到显示页面 查看其运行

如你所见,你可以使用简单的视图包含你的动作链接,并在应用程序主视图中呈现此视图。 "starting" 下的子路由也有一个迷你导航栏,来自类似的模板。

Handlebars:

<script type="text/x-handlebars" data-template-name="application">
    <h1>My Ember Application</h1>
    {{view App.NavbarView controllerBinding="controller.controllers.navbarController"}}
    <br /><hr />
    <div class="content">
        {{outlet}}
    </div>
</script>

<script type="text/x-handlebars" data-template-name="navbar">
    <ul>
        <li><a href="#" {{action gotoHome}}>Home</a></li>
        <li><a href="#" {{action gotoStarting}}>Getting Started</a></li>
        <li><a href="#" {{action gotoCommunity}}>Community</a></li>
    </ul>
</script>

<script type="text/x-handlebars" data-template-name="getting-started-menu">
    <ul>
        <li><a href="#" {{action gotoIndex}}>Overview</a></li>
        <li><a href="#" {{action gotoMVC}}>About MVC</a></li>
        <li><a href="#" {{action gotoEmber}}>About Ember</a></li>
    </ul>
</script>
    
<script type="text/x-handlebars" data-template-name="home">
    <h2>Welcome</h2>
    <br />
    <img src="http://emberjs.com/images/about/ember-productivity-sm.png" alt="ember logo" />
    <br />
    <br />
    <p>Bacon ipsum dolor sit amet qui ullamco exercitation, shankle beef sed bacon ground round kielbasa in. Prosciutto pig bresaola, qui meatloaf ea tongue non dolore et pork belly andouille ribeye spare ribs enim. Enim exercitation elit, brisket nisi ex swine in jerky consequat pastrami dolore sed ad. In drumstick cow, salami swine fatback short ribs ham ut in shankle consequat corned beef id. Deserunt prosciutto beef speck. Sirloin incididunt kielbasa excepteur irure.</p>
    <p>Do beef ribs dolore swine chicken shankle, venison officia qui magna ea anim. Jerky shank shankle, tongue in pork loin commodo boudin elit cupidatat turducken id capicola meatball. Strip steak ham hock tenderloin, id chicken drumstick sint jerky. Dolore veniam cillum minim, pariatur est beef. Sunt fatback tri-tip ex chuck.</p>
    <br />
    <br />
    <strong>Note</strong>: This is a basic template with no <i>bindings</i>
</script>

<script type="text/x-handlebars" data-template-name="starting">
    <h2>Getting Started with Ember</h2>
    {{view App.StartingMenuView}}
    <br />
    <br />
    <br />
    <p>Bacon ipsum dolor sit amet qui ullamco exercitation, shankle beef sed bacon ground round kielbasa in. Prosciutto pig bresaola, qui meatloaf ea tongue non dolore et pork belly andouille ribeye spare ribs enim. Enim exercitation elit, brisket nisi ex swine in jerky consequat pastrami dolore sed ad. In drumstick cow, salami swine fatback short ribs ham ut in shankle consequat corned beef id. Deserunt prosciutto beef speck. Sirloin incididunt kielbasa excepteur irure.</p>
    <p>Do beef ribs dolore swine chicken shankle, venison officia qui magna ea anim. Jerky shank shankle, tongue in pork loin commodo boudin elit cupidatat turducken id capicola meatball. Strip steak ham hock tenderloin, id chicken drumstick sint jerky. Dolore veniam cillum minim, pariatur est beef. Sunt fatback tri-tip ex chuck.</p>
    <br />
    <br />
    <strong>Note</strong>: This is a basic template has a menu view embedded
</script>

<script type="text/x-handlebars" data-template-name="about-mvc">
    <h2>About MVC</h2>
    {{view App.StartingMenuView}}
    <br /><br />
    <br /><p>
        Model–View–Controller (MVC) is a software design for interactive computer user interfaces that separates the representation of  information from the user's interaction with it.[1][8] The model consists of application data and business rules, and the controller mediates input, converting it to commands for the model or view.[3] A view can be any output representation of data, such as a chart or a diagram. Multiple views of the same data are possible, such as a pie chart for management and a tabular view for accountants.
    </p>
    Read more at <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" target="_blank">Wikipedia</a>
    <br />
    <br />
    <strong>Note</strong>: This is a basic template has a menu view embedded
</script>

<script type="text/x-handlebars" data-template-name="about-ember">
    <h2>About Ember</h2>
    {{view App.StartingMenuView}}
    <br /><br />
    <br />
    <p>A framework for creating <strong>ambitious</strong> web applications</p>
    Read more at <a href="http://emberjs.com/about/" target="_blank">emberjs.com</a>
    <br />
    <br />
    <p>Bacon ipsum dolor sit amet qui ullamco exercitation, shankle beef sed bacon ground round kielbasa in. Prosciutto pig bresaola, qui meatloaf ea tongue non dolore et pork belly andouille ribeye spare ribs enim. Enim exercitation elit, brisket nisi ex swine in jerky consequat pastrami dolore sed ad. In drumstick cow, salami swine fatback short ribs ham ut in shankle consequat corned beef id. Deserunt prosciutto beef speck. Sirloin incididunt kielbasa excepteur irure.</p>
    <p>Do beef ribs dolore swine chicken shankle, venison officia qui magna ea anim. Jerky shank shankle, tongue in pork loin commodo boudin elit cupidatat turducken id capicola meatball. Strip steak ham hock tenderloin, id chicken drumstick sint jerky. Dolore veniam cillum minim, pariatur est beef. Sunt fatback tri-tip ex chuck.</p><br />
    <br />
    <strong>Note</strong>: This is a basic template has a menu view embedded
</script>

<script type="text/x-handlebars" data-template-name="community">
    <h1>Ember Community</h1>
    <p>
        Get in touch with the community
    </p>
    <p>Featured contact info:</p>
    {{#each item in content}}
        <a {{bindAttr href="item.linkUrl" }} target="_blank">
            <img height="32" width="32" {{bindAttr src="item.imageUrl" title="item.displayName" alt="item.displayName"}} /><br />
            {{item.displayName}}
        </a><br />
    {{/each}}
    <br />
    Check more information about ember community at <a href="http://emberjs.com/community/" target="_blank">emberjs.com</a>
    <br />
    <br />
    <strong>Note</strong>: This is a template with a <i>foreach</i> type of loop
</script>

JavaScript:

App = Em.Application.create();

App.ApplicationController = Em.Controller.extend();
App.ApplicationView = Em.View.extend({
    templateName: 'application'
});

App.HomeController = Em.Controller.extend();
App.HomeView = Em.View.extend({
    templateName: 'home'
});

App.NavbarController = Em.Controller.extend();
App.NavbarView = Em.View.extend({
    templateName: 'navbar'
});

App.StartingController = Em.Controller.extend();
App.StartingView = Em.View.extend({
    templateName: 'starting'
});


App.StartingMenuController = Em.Controller.extend();
App.StartingMenuView = Em.View.extend({
    templateName: 'getting-started-menu'
});

App.AboutMVCController = Em.Controller.extend();
App.AboutMVCView = Em.View.extend({
    templateName: 'about-mvc'
});

App.AboutEmberController = Em.Controller.extend();
App.AboutEmberView = Em.View.extend({
    templateName: 'about-ember'
});

App.CommunityModel = Em.Object.extend({
    displayName: null,
    linkUrl: null,
    imageUrl: null
});

App.CommunityController = Em.ArrayController.extend({
    content: [],
    init: function() {
        this._super();
        this.pushObject(
        App.CommunityModel.create({
            displayName: 'Twitter',
            linkUrl: 'https://twitter.com/#!/emberjs',
            imageUrl: 'http://icons.iconarchive.com/icons/iconshots/social-media-network/32/twitter-icon.png'
        }));
        this.pushObject(
        App.CommunityModel.create({
            displayName: 'GitHub',
            linkUrl: 'https://github.com/emberjs/ember.js',
            imageUrl: 'http://www.workinprogress.ca/wp-content/uploads/github.png'
        }));
    }
});

App.CommunityView = Em.View.extend({
    templateName: 'community',
    contentBinding: 'App.CommunityController.content'
});

App.Router = Em.Router.extend({
    enableLogging: true,
    location: 'hash',

    root: Em.Route.extend({
        // EVENTS
        gotoHome: Ember.Route.transitionTo('home'),
        gotoStarting: Ember.Route.transitionTo('starting.index'),
        gotoCommunity: Ember.Route.transitionTo('community.index'),

        // STATES
        home: Em.Route.extend({
            route: '/',
            connectOutlets: function(router, context) {
                router.get('applicationController').connectOutlet('home');
            }
        }),
        starting: Em.Route.extend({
            // SETUP
            route: '/starting',
            connectOutlets: function(router, context) {
                router.get('applicationController').connectOutlet('starting');
            },
            // EVENTS
            gotoMVC: Ember.Route.transitionTo('mvc'),
            gotoEmber: Ember.Route.transitionTo('ember'),
            gotoIndex: Ember.Route.transitionTo('index'),

            // STATES
            index: Em.Route.extend({
                route: '/',
                connectOutlets: function(router, context) {
                    router.get('applicationController').connectOutlet('starting');
                }
            }),
            mvc: Em.Route.extend({
                route: '/mvc',
                connectOutlets: function(router, context) {
                    router.get('applicationController').connectOutlet('aboutMVC');
                }
            }),
            ember: Em.Route.extend({
                route: '/ember',
                connectOutlets: function(router, context) {
                    router.get('applicationController').connectOutlet('aboutEmber');
                }
            })
        }),
        community: Em.Route.extend({
            // SETUP
            route: '/community',
            connectOutlets: function(router, context) {
                router.get('applicationController').connectOutlet('community');
            },
            // EVENTS
            // STATES
            index: Em.Route.extend({
                route: '/',
                connectOutlets: function(router, context) {
                    router.get('applicationController').connectOutlet('community');
                }
            })
        })
    })
});
App.initialize();​

6
太棒了,比我快得多。在我看来,这个例子应该成为Ember官网示例的一部分。 - sly7_7
1
我正在编写一个示例,所以我已经有一些模板了。不过我可能会稍后修改这个小工具来解决CSS类部分的问题。 - MilkyWayJoe
4
太好了,谢谢!我同意这应该放在Ember网站上。我发现Ember文档实际上相当缺乏——把它们整合起来花费了很长时间。该网站的文档让我有很多问题,主要是如何将其全部整合到一个实际应用程序中,而不仅仅是单个视图。相比之下,我学习过的Rails(在1.0时代)具有非常好的入门点,尤其是那些录屏教程。我希望Ember也有类似的东西。 - user1499207
@AndrejKaurin 请检查我添加的新链接。 它解决了从服务器导航的问题(由于我无法在此处执行ajax,因此我将其添加到“init”函数内)。 我正在定义一个带有“路由”属性的NavBarItem模型,我正在使用完整路由(例如“root.index.home”)填充该属性,并将操作设置为在我的路由器中触发“navigateTo”,并将该属性作为“transitionTo”的参数传递。希望这会有所帮助。 - MilkyWayJoe
1
如果我有一美元换每个破碎的Ember fiddle……我肯定可以买午餐。 - Jeffrey Knight
显示剩余6条评论

8

我不喜欢MilkyWayJoe的回答,因为如果你早晚想要更改你的状态或其他内容的命名,你必须同时修改代码和视图,而且为每个路由添加一个transitionTo函数似乎并不理想。 我的方法更加程序化和模块化:

# 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
)
<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>

这样,我可以在路由器中创建和调整路由,并将Nav-Definitions并排放置:
# 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'
    )
  ]
)

此外,这种方法的优点是链接实际上是链接而不是操作 - 因此它们具有href和浏览器操作(如“在新标签页中打开”等),将起作用。 - Ginty
1
只有当您将a href="#"设置为href="/#/{{unbound goto}}"时才会生效。 - Thomas
1
我同意你们的看法。我试图在代码的更新版本中解决这个问题,但并没有达到我想要的效果。现在新路由器已经上线,我会在某个时候更新它,以便更符合你们对这个实现的看法。我的借口是我在学习的同时编写它,所以这是一个发现的过程 :P - MilkyWayJoe
@Thomas:我把您的这个想法放在了这里。我已经将代码翻译成了JavaScript(原先是Ruby,对吧?),并更新了一个更新版本的Ember(1.1.2)。 - blueFast

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接