在准备大型项目期间,我正在研究一些JS MV *框架,这些框架将由团队与大约10名开发人员一起使用。
首先我尝试了AngularJS,因为它的性能看起来是所有框架中最好的(SO topic)。它有一些很好的未来像DI,但我真的不喜欢用它,因为对我而言,它有点乱码,难以理解,我喜欢尽可能清洁的代码,结构良好,易于使用读。
目前我正在尝试使用EmberJS,它看起来像是ember,非常适合开发人员,并且很容易使用它。所以我写了一个小的“登录小部件”,看看应该如何构建ember应用程序。在开发过程中,我试图解决一些问题,如异步请求,动画等......
“小部件”已经工作并完成我希望他做的所有事情,但此时,我仍然有一些问题,我想听听有经验的ember开发人员关于当前代码结构,对象角色,以及使用ember后我可以遇到的问题。
所以我有一些问题:
1)Ember视图很酷,但是如果我想让ember处理现有的元素动作和动画呢?我的意思是,例如我在服务器上呈现链接(或使用AJAX接收的HTML),我如何绑定ember动作来处理它的单击操作并为它实例化我的自定义LinkView?我确信我可以通过jQuery或其他东西“手动”绑定到DOM事件来找到解决方案,但确实存在“Ember”方式吗?
2)是否可以在进入初始状态之前设置StateManager
属性,但不在类级别设置它?
3)有人能指出我如何使用Em.Deffered
的例子吗?我承诺使用jQuery $.Deferred()
,但我不希望在我的StateManager
中依赖jQuery。
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>