Dojo MVC 的简单登录实现
Posted
技术标签:
【中文标题】Dojo MVC 的简单登录实现【英文标题】:Simple Login implementation for Dojo MVC 【发布时间】:2012-06-11 17:35:42 【问题描述】:有没有关于如何实现一个简单的登录页面/对话框的例子?我一直在尝试使用 dojo 样板来做到这一点(检查我以前的问题:Layout implementation for Dojo MVC)。到目前为止,我已经能够显示我的对话框。但我希望能够获取我的数据,并且在点击事件时希望有一个警报框(例如他的内容)。
我的看法:
<form action="Login" method="post" validate="true" id="loginForm">
<table >
<tr>
<td><label>Login</label></td>
<td><input class="cell" type="text" trim="true" dojoType="dijit.form.TextBox" value="" name="login" id="userId"/></td>
</tr>
<tr>
<td><label>Password</label></td>
<td><input class="cell" type="password" trim="true" dojoType="dijit.form.TextBox" value="" name="password" id="password"/></td>
</tr>
<tr><td colspan="2"> </td></tr>
<tr>
<td colspan="2" align="center">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" valign="top"><button dojoType="dijit.form.Button" type="submit" id="LoginButton" onClick="connect">Ok</button></td>
<td align="left" valign="top"><button dojoType="dijit.form.Button" type="submit" onclick="document.Login.reset()" id="Cancel">Cancel</button></td>
<td><button dojoType="dijit.form.Button" type="submit" onclick="showDialog();" id="resetPassword"> Show Dialog </button></td>
</tr>
</table>
</td>
</tr>
</table>
</form>
我的小部件模块/类
define([
"dojo/_base/declare",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!app/views/Login/Login.html",
"dijit/Dialog",
"dijit/form/Button",
"dijit/form/TextBox"
], function(
declare,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
template,
Dialog
)
return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, Dialog],
templateString: template
);
);
现在,如果您检查 HTML 元素 id LoginButton 在这种情况下应该调用“连接”函数。哪个应该显示(在警报框中)用户名和密码当前输入。
我应该把我的函数放在哪里?有点……
connect : function()
alert("username :" + dom.byId("userId").value()
+ " Password: " + dom.byId("password").value());
编辑:新代码
define([
"dojo/_base/declare",
"dijit/_Widget",
"dojo/dom",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!app/views/Login/Login.html",
"dijit/Dialog",
"dijit/form/Button",
"dijit/form/TextBox"
], function(
declare,
dom,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
template,
Dialog
)
return declare("login", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, Dialog],
templateString: template,
postCreate: function()
this.inherited(arguments);
// make reference to widget from the node attachment
this.submitButton = dijit.getEnclosingWidget(this.submitButtonNode);
// override its onSubmit
this.submitButton.onClick = function()
alert("username :" + dom.byId("userId").value()
+ " Password: " + dom.byId("password").value());
;
,
// and a sample on how to implement widget-in-template stateful get/setter pattern
// e.g. if submitbutton label should change on some event, call as such:
// dijit.byId('loginForm').set("submitLabel", "Sendin login, please wait");
_setSubmitLabelAttr : function(value)
return this.submitButton.set("label", value);
,
_getSubmitLabelAttr : function()
return this.submitButton.get("label");
,
);
);
我的 main.js:
define([ 'dojo/has', 'require', 'dojo/_base/sniff'], function (has, require)
var app = ;
if (has('host-browser'))
require([ './views/Login', 'dojo/domReady!' ], function (Login)
app.login = new Login().placeAt(document.body);
app.login.startup();
// And now…
app.login.show();
);
else
console.log('Hello from the server!');
);
【问题讨论】:
查看我对Dojo Dialog with confirmation button 和示例code at jsFiddle 的回答。 【参考方案1】:我想添加我的解决方案:http://jsfiddle.net/phusick/tG8Sg/
由于 jsFiddle 的限制,它有点扭曲,但主要原则与我在编码中采用的相同。
首先,登录过程(或任何其他表单处理)被封装在对话框中,并通过dojo/Evented
与应用程序的其余部分进行通信,以为发出事件。它是这样工作的:
var loginDialog = new LoginDialog( controller: loginController);
loginDialog.startup();
loginDialog.show();
loginDialog.on("cancel", function()
console.log("Login cancelled.");
);
loginDialog.on("error", function()
console.log("Login error.");
);
loginDialog.on("success", function()
console.log("Login success.");
console.log(JSON.stringify(this.form.get("value")));
);
正如您在 jsFiddle 中看到的,我在 LoginDialog
构造函数中组装了两个模板 dialog-template 和 login-form-template。原因是我通常还会有一个包装 dijit/form/Form
的类来做一些超出标准 dijit/form/Form
验证和数据序列化的魔法,但是由于登录很简单而且在 jsFiddle 的单个文件中会很乱,所以我摆脱了其中。将表单与对话框分开的优点是您可以在其他地方使用相同的表单(即 View)以及所有表单即席代码,例如在ContentPane
。表单只是为了向用户显示和收集数据,它不应该直接与 Model 通信,即 web 服务,有一个 Controller 用于此目的:
var LoginController = declare(null,
constructor: function(kwArgs)
lang.mixin(this, kwArgs);
,
login: function(data)
// simulate calling web service for authentication
var def = new Deferred();
setTimeout(lang.hitch(this, function()
def.resolve(data.username == this.username && data.password == this.password);
), 1000);
return def;
);
创建LoginController
的实例:
// provide username & password in the constructor
// since we do not have web service here to authenticate against
var loginController = new LoginController(username: "user", password: "user");
并将其传递给LoginDialog
构造函数,如上所示:
var loginDialog = new LoginDialog( controller: loginController);
终于LoginDialog
类:
var LoginDialog = declare([Dialog, Evented],
READY: 0,
BUSY: 1,
title: "Login Dialog",
message: "",
busyLabel: "Working...",
// Binding property values to DOM nodes in templates
// see: http://www.enterprisedojo.com/2010/10/02/lessons-in-widgetry-binding-property-values-to-dom-nodes-in-templates/
attributeMap: lang.delegate(dijit._Widget.prototype.attributeMap,
message:
node: "messageNode",
type: "innerHTML"
),
constructor: function(/*Object*/ kwArgs)
lang.mixin(this, kwArgs);
var dialogTemplate = dom.byId("dialog-template").textContent;
var formTemplate = dom.byId("login-form-template").textContent;
var template = lang.replace(dialogTemplate,
form: formTemplate
);
var contentWidget = new (declare(
[_Widget, _TemplatedMixin, _WidgetsInTemplateMixin],
templateString: template
));
contentWidget.startup();
var content = this.content = contentWidget;
this.form = content.form;
// shortcuts
this.submitButton = content.submitButton;
this.cancelButton = content.cancelButton;
this.messageNode = content.messageNode;
,
postCreate: function()
this.inherited(arguments);
this.readyState= this.READY;
this.okLabel = this.submitButton.get("label");
this.connect(this.submitButton, "onClick", "onSubmit");
this.connect(this.cancelButton, "onClick", "onCancel");
this.watch("readyState", lang.hitch(this, "_onReadyStateChange"));
this.form.watch("state", lang.hitch(this, "_onValidStateChange"));
this._onValidStateChange();
,
onSubmit: function()
this.set("readyState", this.BUSY);
this.set("message", "");
var data = this.form.get("value");
// ask the controller to login
var auth = this.controller.login(data);
Deferred.when(auth, lang.hitch(this, function(loginSuccess)
if (loginSuccess === true)
this.onLoginSuccess();
return;
this.onLoginError();
));
,
onLoginSuccess: function()
this.set("readyState", this.READY);
this.emit("success");
,
onLoginError: function()
this.set("readyState", this.READY);
this.set("message", "Please try again.");
this.emit("error");
,
onCancel: function()
this.emit("cancel");
,
_onValidStateChange: function()
this.submitButton.set("disabled", !!this.form.get("state").length);
,
_onReadyStateChange: function()
var isBusy = this.get("readyState") == this.BUSY;
this.submitButton.set("label", isBusy ? this.busyLabel : this.okLabel);
this.submitButton.set("disabled", isBusy);
);
请参阅上述jsFiddle 的模板。它们通常应该通过dojo/text!
在单独的文件中required
。我将它们放入 <script type="text/template">
以适应 jsFiddle。
【讨论】:
谢谢。代码来自我的Dojo 1.7 based project 我想在 GitHub 上发布。它是 CMS + CMS Admin,即基本上是基于dojo/store/JsonRest
和 dgrid
构建的 CRUD 应用程序。它还包括一个用 Zend Framework 编写的 RESTful 后端,所以我认为它可能会有所帮助,因为它既不是太简单也不是太复杂的示例,而这在 Dojo 生态系统中是缺失的。如果您有兴趣,我会在发布时通知您。
你看到了我的其他帖子......我正在获取几乎所有关于道场的信息。所以是的,我很感兴趣。我有一个非常具体的问题要问你。您如何使用 dojo 构建您的网站?你如何分离你的文件?就像您的登录示例一样。我相信我会为以下内容创建一个文件:dialog-template.html、login-form-template.html(在 app/views/templates 之类的东西下),然后同样处理登录 js 逻辑(app/views/LoginDialog.js)一个用于控制器(app/controllers/LoginController.js).. 我的 index.html 调用这两个?有意义吗?
是的,我就是这样做的,但我喜欢以与 js 类相同的方式命名模板,因此 LoginDialog.js
的模板将是 templates/LoginDialog.html
。如果需要,每个 namespace 都有其 templates 目录,例如 dl.dropbox.com/u/2131149/dojo-directory-structure.png
最后一个问题。我看到你正在使用 dojo 样板。 “实例”如何工作?例如,如果我们继续您的登录示例,我如何在 main.js 中创建(使其显示)...
我不确定你的“实例”是什么意思。我认为 main.js 中不应该有更多的逻辑,它只是为了启动以EntryPoint.js
开头的应用程序。【参考方案2】:
当您模板化小部件时,您会想要使用模板化小部件解析。取自http://dojotoolkit.org/documentation/tutorials/1.6/templated/,下面描述了如何将事件附加到一个简单的domnode。
<div
data-dojo-attach-point="focusNode"
data-dojo-attach-event="ondijitclick:_onClick"
role="menuitem" tabindex="-1">
<span data-dojo-attach-point="containerNode"></span>
</div>
当您在模板中有小部件时,请引用您的子小部件并通过这些引用设置它们的属性。
<td align="center" valign="top">
<button
id="LoginButton" type="submit"
dojoType="dijit.form.Button"
dojoAttachPoint="submitButtonNode">
Ok
</button>
</td>
创建一个文件夹,使其成为 dijit/dojox/dojo 文件夹的“兄弟”文件夹,将其称为“app”。并在小部件声明之后,放入一个名为 app/widget/MyWidget.js 的文件;
define([
"dojo/_base/declare",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!app/views/Login/Login.html",
"dijit/Dialog",
"dijit/form/Button",
"dijit/form/TextBox"
], function(
declare,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
template,
Dialog
)
return declare("app.widget.MyWidget", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, Dialog],
templateString: template
postCreate: function()
this.inherited(arguments);
// make reference to widget from the node attachment
this.submitButton = dijit.getEnclosingWidget(dojo.query(".dijitButton")[0], this.domNode);
// or simply dijit.byId('LoginButton');
// override its onSubmit
this.submitButton.onClick = function()
alert("username :" + dom.byId("userId").value()
+ " Password: " + dom.byId("password").value());
;
,
// and a sample on how to implement widget-in-template stateful get/setter pattern
// e.g. if submitbutton label should change on some event, call as such:
// dijit.byId('loginForm').set("submitLabel", "Sendin login, please wait");
_setSubmitLabelAttr : function(value)
return this.submitButton.set("label", value);
,
_getSubmitLabelAttr : function()
return this.submitButton.get("label");
);
);
一旦文件就位,请像这样编写您的 html:
<head>
<script src=...dojo...></script>
<script>
dojo.require([
"dojo/parser",
"app/widget/MyWidget",
"dojo/domReady!" ] , function(parser)
parser.parse();
);
</script>
</head>
<body>
<form dojoType="app.widget.MyWidget"></div>
</body>
这将执行以下操作:
加载 dojotoolkit (dojo/dojo.js) 创建一个名为“app”的命名空间,其中包含一个子空间“widget” 渲染主体(简单表单 dom 节点) 调用脚本ondomready,需要app.widget.MyWidget
MyWidget 需要依赖项
parser.parse 实例化<form dojoType="mywidget">
【讨论】:
我在 firebug 中遇到错误:“声明登录:mixion #0 不是可调用的构造函数。 我希望你的 mixin#0 不是 '...' 对吗? :) 我在答案中填写了空白 这个 dijit.getEnclosureWidget(this.submitButtonNode) 给我返回 null?我不知道你是否看过 dojo 样板,但基本上有一个文件 main.js,我在其中调用我的对话框。检查我的编辑。 啊好吧,我猜<button dojoType...>
节点被销毁并替换为另一个domnode(来自dijit.Button.templateString 的一个)。基本上,调用 getEnclosureWidget 的目的是从其 domNode 引用中请求小部件 dijit.Button - 在解析期间已更改。所以行不通,我的错.. 尝试在 postCreate 中做 dijit.getEnclosingWidget(dojo.query(".dijitButton")[0], this.domNode)
或 this.getChildren()[ButtonIndex]
哈哈!都很好。不幸的是,这两行不起作用我仍然得到 this.submitBtton 是未定义的。为什么我不能创建一个函数并将其作为事件调用?【参考方案3】:
在 mschr 的帮助下。我遇到了这个特殊的解决方案。
define([
"dojo/_base/declare",
"dojo/dom",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!app/views/Login/Login.html",
"dijit/Dialog",
"dijit/form/Button",
"dijit/form/TextBox"
], function(
declare,
dom,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
template,
Dialog
)
return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, Dialog],
templateString: template,
postCreate: function()
this.inherited(arguments);
// make reference to widget from the node attachment
//this.submitButton = this.getChildren()[ButtonIndex];
// override its onSubmit
/*alert("username :" + dom.byId("userId").value()
+ " Password: " + dom.byId("password").value());*/
dojo.connect(this.loginButton, 'onclick', this._login);
,
// and a sample on how to implement widget-in-template stateful get/setter pattern
// e.g. if submitbutton label should change on some event, call as such:
// dijit.byId('loginForm').set("submitLabel", "Sendin login, please wait");
_login : function()
var value = dom.byId("userId").value;
if(value)
alert("username: " + value);
,
// and a sample on how to implement widget-in-template stateful get/setter pattern
// e.g. if submitbutton label should change on some event, call as such:
// dijit.byId('loginForm').set("submitLabel", "Sendin login, please wait");
_setSubmitLabelAttr : function(value)
return this.submitButton.set("label", value);
,
_getSubmitLabelAttr : function()
return this.submitButton.get("label");
,
);
);
【讨论】:
不要使用dojo.connect
,否则在您手动断开此处理程序之前,您的对话框不会被垃圾回收。 _login()
方法也不会在您的对话框范围内被调用,例如this.loginButton
在_login()
中不可用,您必须编写dojo.connect(this.loginButton, "onClick", lang.hitch(this, "_login"))
来实现这一点。改用this.connect(this.loginButton, "onClick", "_login")
,_login()
将在对话框范围内被调用,并且在对话框被销毁时连接将自动断开。
另外请注意,使用dom.byId()
会使您将对话框用作单例,这对于登录对话框可能没问题,但您将无法控制 DOM 节点的id
s和整个应用程序中的dijits。您不应在 dijit 模板中使用 id
s,而应使用 data-dojo-attach-point
。以上是关于Dojo MVC 的简单登录实现的主要内容,如果未能解决你的问题,请参考以下文章
基于 Dojo toolkit 实现 web2.0 的 MVC 模式