后端使用 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 没有关联上
希望有兴趣的朋友可以一起探讨一下这个问题,我可以提供更多细节
技术上来说,我实际开发中很少碰到需要动态填充 outlet,回忆了一下,类似的 UI 需求,基本是用以下几种方式解决了:
考虑会不会引起 route 变化,即这个点击操作需不需要用 url 表示状态以便刷新。如果需要,可以把按钮换成 link-to,然后用嵌套 route 去做,特殊情况可以用 route 的 renderTemplate 回调手动填充。
不涉及到 route 变化,使用 controller 中的特殊标记(比如 isEditing)配合 template 中的 if/else,控制某一块区域的显示和隐藏。按钮绑定的 action 只修改那个特殊标记。
{{#if isEditing}}
{{render "post-form" post}}
{{! you can also use component instead of render }}
{{/if}}
notification 网上也有一堆例子,你可以 Google notification 或者 flash,这里就不提了。
最后,可以看一下 Ember API 中的 connectOutlet 和 disconnectOutlet,看是不是你需要的。不过就我的感觉,大部分情况用不到偏底层的东西。
@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'
另外说两点:
events
是已经废弃的写法,现在改名叫 actions
了。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}}
@tyaccp_guojian ember-rails 只是帮助你建立目录结构的,你想全部写一个文件里面都可以……写个测试代码还需要定目录结构么?App 只是我定的全局命名空间,用 Em.Application.create()
生成的。你的情况下,就看你用哪个 subapp 了,把 route 和 view 定义在你自己的 subapp 下就行了。
我的有很多 App,不是一个,还是上个图吧
blog.js
和cloudpanel.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}}的话也是可以关联的
@tyaccp_guojian 你直接写 view 当然可以用……只是通过 render
需要通过 Ember 的约定去找 view,没符合约定你就拿它没办法。
不大了解你们的情况,每个项目都有所不同。不过建议你去看 Em.TEMPLATES
里面的东西,了解下 handlebars 怎么执行的。ember-rails 只是帮你在服务器端把 template 编译成了一个个函数。然后把目录名称对应 template 名称而已。template 名称才是 Ember 按约定找到 view 的根本,而不是你的目录结构。
@mjf429 嗯,我加你了,有空交流下技术吧,国内搞 Ember 的本来就不多。