其他 Ember.js: 怎么在点击按钮的时候动态的将某个 View 渲染到 applicatoin 模板的某个 outlet 中?[已更新]

tyaccp_guojian · 2013年11月29日 · 最后由 tyaccp_guojian 回复于 2013年12月02日 · 4695 次阅读

后端使用 Rails,前端使用 Ember.js

怎么在点击按钮的时候动态的将某个 View 渲染到 applicatoin 模板的某个 outlet 中?


已修改

写了一个简单的例子是出来了,但是我们的项目结构有点复杂,用你博客中的文章举例子说一下

Rails 项目,使用ember-rails

然后,app/assets/javascripts/emberapps 目录有很多子目录,每个目录都是一个完整的 Ember 应用

假设现在有一个目录名叫:photoalbums,这个目录下面是一个 Ember 应用,在 /photoalbums 的模板中点击按钮执行 PhotoalbumsRoute 中定义的 showUploadPicture 方法,在这个方法中调用 Ember.Route.reopen 中自定义的方法 showModal 方法,关键就在这个地方:

调用 showModal方法的时候,需要传递一个 view name,但是因为项目结构,所以这个地方不像例子里面的 name 简单, view name 得写 emberapps/photoalbums/upload_picture,然后定义 Ember.UploadPictureView,继承自 Ember.ModalBodyView

代码是下面这样:

# PhotoalbumsRoute
...
events:
  showUploadPicture:->
    Ember.Route.showModal this, "emberapps/photoalbums/upload_picture"
...
Ember.Route.reopenClass
  showModal: (router, name, model) ->
    router.render name,
      into: "emberapps/cloudpanel/modal"
      outlet: "modalBody"

运行,inspect 看代码,发现 html 都 render 进去了,但是 Ember.ModalBodyView 的 didInsertElement 没有执行,我认为原因是emberapps/photoalbums/upload_picture这个 view name 和 UploadPictureView 没有关联上

希望有兴趣的朋友可以一起探讨一下这个问题,我可以提供更多细节

使用 ajax?建议再详细描述下需求

不好意思,忘了写是在 Ember.js 了,不是 Rails 的模板

#3 楼 @tyaccp_guojian 怪不得看着 outlet 眼熟但记不起……

@Rei 你有没有在项目中用过 Ember.js?

貌似这里没多少人用这框架

涉及到按钮的就用一个 action 来执行好了

刚才去 Ember.js 论坛和官网看了下找到办法了,谢谢大家

另外,公司正在招人,前端、IOS、Android 都需要,求扩散,招聘贴地址:猛戳

帖子是老大发的,老大的技术还是很厉害的,老大曾经的求职贴:猛戳

技术上来说,我实际开发中很少碰到需要动态填充 outlet,回忆了一下,类似的 UI 需求,基本是用以下几种方式解决了:

  1. 考虑会不会引起 route 变化,即这个点击操作需不需要用 url 表示状态以便刷新。如果需要,可以把按钮换成 link-to,然后用嵌套 route 去做,特殊情况可以用 route 的 renderTemplate 回调手动填充。

  2. 不涉及到 route 变化,使用 controller 中的特殊标记(比如 isEditing)配合 template 中的 if/else,控制某一块区域的显示和隐藏。按钮绑定的 action 只修改那个特殊标记。

{{#if isEditing}}
  {{render "post-form" post}}
  {{! you can also use component instead of render }}
{{/if}}
  1. 又看了一下你的需求,需要某个 action 去改变 application 中的 outlet,我能想到的大概就是 modal 和 notification 了。这些东西的做法因人而异,modal 可以参考一下 Discourse 的做法,简单有效。我写过一篇文章分析 Discourse 的做法,可以看看:http://david-chen-blog.logdown.com/posts/98657-how-discourse-implement-modal

notification 网上也有一堆例子,你可以 Google notification 或者 flash,这里就不提了。

最后,可以看一下 Ember API 中的 connectOutlet 和 disconnectOutlet,看是不是你需要的。不过就我的感觉,大部分情况用不到偏底层的东西。

@darkbaby123 看完了大神的博客,感觉真不错,对我这新手来说有点深入了,以后会经常关注的

@tyaccp_guojian 按照 Ember 的约定,emberapps/photoalbums/upload_picture 对应的 View 应该是 EmberappsPhotoalbumnsUploadPicture。

不过这样做名字实在太长了。我也不确定 renderTemplate 第一个参数是 template 名称还是 view 名称,你可以试试,如果是 template 名称就没办法了,但如果是 view 名称的话,可以考虑这样:

# view
App.UploadPicture = Em.View.extend
  templateName: 'emberapps/photoalbums/upload_picture'

# route
App.PhotoAlbumsRoute = Em.Route.extend
  renderTemplate: ->
    Em.Route.showModal 'upload_picture'

另外说两点:

  1. 不知道你们使用的 Ember 是什么版本,不过 events 是已经废弃的写法,现在改名叫 actions 了。
  2. 不知道你们的项目结构是怎么放的,不过如果每个 template 都需要写 emberapps 前缀未免太过麻烦,可以在 config/application.rb 里调整 config.handlebars.template_root ,在 ember-rails 生成的 template 名称中把 emberapps 去掉,比如:
config.handlebars.template_root = 'emberapps'

然后可以在浏览器里看看 Em.TEMPLATES 里面的 template 是什么名字。

@darkbaby123 emberapps/photoalbums/upload_picture 为什么会有这么长的模板呢?其实模板名称只是upload_picture,但是因为我们有很多个应用,目录结构是这样的:

/app/assets/javascripts/emberapps/emberapp1 /app/assets/javascripts/emberapps/emberapp2 /app/assets/javascripts/emberapps/emberapp3

1、我们还是用的旧版本,没升级 2,这种指定方式应该只能指定一个 ember 应用,但是我们有多个 ember 应用

@tyaccp_guojian 我只是觉得起这么长的模板名字对 Ember 的 COC 而言不方便。比如:

{{render "posts"}}

App.SomeRoute = Em.Route.extend
  renderTemplate: ->
    @render 'posts'

这两处的 render 应该都是传的 template 名称,然后 Ember 会根据约定去找对应的 view。这两种情况下你都没有办法去修改使用哪个 view。要定义 view 并且关联 template 就只能照着约定来。

这也是为什么你的问题里使用 UploadPictureView 没效果的原因,Ember 并不认为它跟 emberapps/photoalbums/upload_picture 有联系,它只会去找 EmberappsPhotoAlbumsUploadPictureView。

{{render 'emberapps/photoalbums/upload_picture'}}

我得要这样写才能渲染进来,执行 render 的时候也得这样写,我再试一次写EmberappsPhotoAlbumsUploadPictureView吧,试过一次了

@tyaccp_guojian 奇怪,我刚才测试了一下,是可以的。你是不是写错了?是 EmberappsPhotoalbumsUploadPictureView,albums 没有大写。

我的不行呀,你可以把你的例子给我看下吗?

@tyaccp_guojian 一个简单的例子:template aaa/bbb/ccc 和 view AaaBbbCccView 会自动关联。

# Route
App.ApplicationRoute = Em.Route.extend
  renderTemplate: ->
    @_super()
    @render 'aaa/bbb/ccc', into: 'application', outlet: 'modal'

# View
App.AaaBbbCccView = Em.View.extend
  didInsertElement: ->
    alert 'view rendered'
<!-- application.hbs -->
{{outlet modal}}

目录结构呢?还有 App 在哪里定义的?

@tyaccp_guojian ember-rails 只是帮助你建立目录结构的,你想全部写一个文件里面都可以……写个测试代码还需要定目录结构么?App 只是我定的全局命名空间,用 Em.Application.create() 生成的。你的情况下,就看你用哪个 subapp 了,把 route 和 view 定义在你自己的 subapp 下就行了。

我的有很多 App,不是一个,还是上个图吧

blog.jscloudpanel.js里面都是 Em.Application.create(),其他同级的目录下面都是一个独立的 App,不是说一个应用下面有一个 template,然后 template 下面有子目录这种模式;

我就是想知道你的 App = Em.Application.create() 定义在哪里了

我的 route 和 view 也都在每个 subapp 对用的目录下面,还有 template,这种目录结构在要鞋 tempalteName 的时候就得写emberapps/XXXXXX/template.hbs

我明白你是 emberapps 下面定义各种 subapps 的模式,包括 template。

比如你是要在 blog 这个 subapp 下测试,假设你的 subapp 叫 Blog,是这么定义的:

# blog.js
window.Blog = Em.Application.create()

那你就把要测试的 route 和 view 都放在 Blog 这个名称下面啊。

。。。。。。。。。。牛

@darkbaby123 是这样呀,我都放在各自的目录下面,所以指定 templateName 的时候就不能只写 XXX.hbs 了,得带着emberapps/appname

@darkbaby123 重新写了一个,确实可以,不好意思;

但是还有一个问题,我其他的 View 都没有写前面的 emberapps/appname,但是确实可以跟 template 关联,但是因为这个 template 是我在 ember.js 渲染完以后手动渲染到 DOM 的,所以它就跟 View 关联不上?如果我直接在 application template 中写{{view Ember.PhotoalbumsView}}的话也是可以关联的

#23 楼 @darkbaby123 研究很深入啊,加个 qq 吧,我发个邮件给你

@tyaccp_guojian 你直接写 view 当然可以用……只是通过 render 需要通过 Ember 的约定去找 view,没符合约定你就拿它没办法。 不大了解你们的情况,每个项目都有所不同。不过建议你去看 Em.TEMPLATES 里面的东西,了解下 handlebars 怎么执行的。ember-rails 只是帮你在服务器端把 template 编译成了一个个函数。然后把目录名称对应 template 名称而已。template 名称才是 Ember 按约定找到 view 的根本,而不是你的目录结构。 @mjf429 嗯,我加你了,有空交流下技术吧,国内搞 Ember 的本来就不多。

@darkbaby123 谢谢你,有时间我去深入了解一下 ember-rails 以及和 handlebars

需要 登录 后方可回复, 如果你还没有账号请 注册新账号