1

在准备这个大型项目的过程中,我正在研究一些 JS MV* 框架,这些框架将被大约 10 名开发人员的团队使用。
首先我尝试了 AngularJS,因为它的性能看起来是所有框架中最好的(SO 主题)。它有一些像 DI 一样美好的未来,但我真的不喜欢使用它,因为对我来说它有点混乱的代码很难理解,我喜欢代码尽可能干净,结构良好且易于读。

目前我正在尝试 EmberJS,它看起来像 ember,对开发人员非常友好,并且很容易使用它。所以我写了一个小的“登录小部件”来看看 ember 应用程序应该如何构建。在开发过程中,我试图解决一些问题,如异步请求、动画等......

提琴手

“小部件”已经可以工作,并且可以完成我希望他做的所有事情,但在这一点上,我仍然有一些问题,我想听听经验丰富的 ember 开发人员关于当前代码结构、对象角色和问题的一些建议我可以稍后在使用 ember 时见面。

所以这里有一些问题:

1) Ember 视图很酷,但是如果我想让 ember 处理现有元素的动作和动画怎么办?我的意思是,例如我在服务器上呈现了链接(或通过 AJAX 接收到的 HTML),我如何绑定 ember 动作来处理它的点击动作并为它实例化我的自定义 LinkView?我确定我可以通过使用 jQuery 或其他东西“手动”绑定到 DOM 事件来找到解决方案,但是是否存在“Ember”方法来做到这一点?

2) 是否可以StateManager在进入初始状态之前设置属性,但不在类级别设置?

3)有人可以指点我如何使用的例子Em.Deffered吗?我做出了使用 jQuery 的承诺$.Deferred(),但我不想在我的StateManager.

4) 是否存在其他“Ember”方式来告诉框架在已经隐藏的状态下向 DOM 添加视图?我试图设置 isVisible: false,但它不起作用。

5) 是否存在其他方式然后再次添加隐藏视图,以解决错误“无法对不在 DOM 中的 Metamorph 执行操作。”?

以下是参考来源:

<script type="text/x-handlebars" data-template-name="GuestView">
        User: {{view Ember.TextField valueBinding='controller.username' placeholderBinding='controller.usernameHint'}}
        Pass: {{view Ember.TextField valueBinding='controller.password' placeholderBinding='controller.passwordHint' }}
        <br/>
        <button {{action login this}}>Login</button>
    </script>

<script type="text/x-handlebars" data-template-name="UserView">
        Hello {{ username }}
        <button {{action logout this}}>Logout</button>
    </script>

<script type="text/javascript">
        var App = Em.Application.create({
            authController: null,
            currentUserBinding: 'authController.content',

            ready: function () {
                this.set("authController", App.AuthController.create());
            }
        });

        App.AuthController = Em.ObjectController.extend({
            content: null,
            target: null,
            init: function () {
                this.set("content", this.getUserFromCookieOrDefault());

                //Is it possible in Ember, to set the controller before entering initial state,
                //but without setting it on Class level?
                this.set("target", App.AuthManager.reopen({ controller: this }).create({
                    //initialState: this.get("content").isAuthorized ? "authorized" : "guest"
                }));
                this.get("target").transitionTo(this.get("content").isAuthorized ? "authorized" : "guest");
            },

            doLogin: function (mgr, context) {
                console.log("logging in");

                //Validation, etc...
                //Imulating async Data loading, actually, it should be done with promises
                setTimeout(function () { mgr.transitionTo("authorized"); }, 1000);
            },

            doLogout: function (mgr, context) {
                console.log("logging out");

                //logout logic
                mgr.transitionTo("guest");
                this.set("content", App.Guest.create());
            },

            getUserFromCookieOrDefault: function () {
                return App.Guest.create();
                //return App.User.create({ username: "TestUser" });
            },
        });

        App.AuthManager = Em.StateManager.extend({
            controller: null,
            canTransist: true,

            // { data, commitCallback, abortCallback } 
            transitionTo: function (path, context) {
                var originalTransitionTo = this._super,
                    manager = this,
                    canTransist = "canTransist",
                    deffered = null,
                    originalTransitionToParams = context && (context.data || context.commitCallback || context.abortCallback) ?
                                                 context.data : context;

                if (!manager.get(canTransist)) {
                    Em.assert("Already in Transition");
                    return;
                }

                manager.set(canTransist, false);

                if (!manager.currentState) {
                    originalTransitionTo.apply(manager, [path, originalTransitionToParams]);
                    manager.set(canTransist, true);
                    return;
                }

                if (!manager.currentState.isAsyncExit) {
                    originalTransitionTo.apply(manager, [path, originalTransitionToParams]);
                    manager.set(canTransist, true);
                }
                else {

                    //How to use Em.Deffered?
                    deffered = $.Deferred();
                    deffered.done(commitTransition).fail(abortTransition);
                    manager.currentState.beforeExit(deffered, originalTransitionToParams);
                }

                function commitTransition() {
                    originalTransitionTo.apply(manager, [path, originalTransitionToParams]);
                    manager.set(canTransist, true);
                }

                function abortTransition() {
                    manager.set(canTransist, true);
                }
            },

            states: {
                guest: Em.State.create({
                    isAsyncExit: true,

                    enter: function (mgr) {
                        console.log("guest");

                        App.guestView = App.GuestView.create({
                            templateName: "GuestView",
                            controller: mgr.controller,
                        }).append();
                    },

                    beforeExit: function (promise, context) {
                        App.guestView.remove(promise);
                    },

                    login: function (mgr, context) {
                        mgr.controller.doLogin(mgr, context);
                    }
                }),

                authorized: Em.State.create({
                    isAsyncExit: true,

                    enter: function (mgr) {
                        console.log("authorized");

                        App.userView = App.UserView.create({
                            templateName: "UserView",
                            controller: mgr.controller,
                        }).append();
                    },

                    beforeExit: function (promise) {
                        App.userView.remove(promise);
                    },

                    logout: function (mgr, context) {
                        mgr.controller.doLogout(mgr, context);
                    }
                })
            }
        });

        App.FadingView = Em.View.extend({
            //Does exists other "Ember" way to tell framework to add view to DOM in already hidden state?
            //I tried to set isVisible: false, but it not works
            classNames: ['initialHidden'],

            didInsertElement: function () {

                //If using this.$().hide().show('slow') without 'initialHidden' class then view is still visible when alert shown
                //alert("View visible");
                this.$().hide().fadeIn(500).removeClass("initialHidden");

            },

            remove: function (promise) {

                var originalView = this,
                    originalViewElement = originalView.$(),
                    clonedView = originalView.$().clone(),
                    originalRemove = this._super;

                $(clonedView).attr("id", $(clonedView).attr("id") + "_clone").removeClass('ember-view');
                $("*", clonedView).removeClass('ember-view').remove("script[id^=metamorph]");
                originalView.$().replaceWith(clonedView);

                //This line exists, only because of Metamorph error:
                //Cannot perform operations on a Metamorph that is not in the DOM.
                $(originalViewElement).css("display", "none").appendTo("body");

                clonedView.fadeOut(1000, function () {
                    $(this).remove();
                    originalRemove.apply(originalView);
                    promise.resolve();
                });

            }
        });

        App.GuestView = App.FadingView.extend();
        App.UserView = App.FadingView.extend();

        App.User = Em.Object.extend({
            username: "",
            isAuthorized: true
        });

        App.Guest = Em.Object.extend({
            isAuthorized: false,
            password: "",
            usernameHint: "Username...",
            passwordHint: "Password..."
        });
    </script>
4

0 回答 0