我是 Parse 的新手,我正在尝试做一个登录页面。我拥有的是用户名(不是电子邮件)和密码,这是可行的。我还为注册有一个用户名、密码和电子邮件字段。这也有效。我想要做的是添加一种方式,当用户输入他们的电子邮件地址时,解析将向他们发送电子邮件。我正在使用 JavaScript SDK。
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>Parse JavaScript Todo App</title>
<link href="css/todos.css" media="all" rel="stylesheet" type="text/css"/>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="js/underscore-1.1.6.js"></script>
<script src="//www.parsecdn.com/js/parse-1.1.15.min.js"></script>
<script src="js/todos.js"></script>
<body>
<!-- Todo App Interface -->
<div id="todoapp">
<div class="title">
<h1>Parse Todos</h1>
</div>
<div class="content">
</div>
</div>
<div id="credits">
Powered by <a href="https://www.parse.com">Parse</a> using the <a href="https://www.parse.com/docs/js_guide">JavaScript SDK</a>
</div>
<!-- Templates -->
<script type="text/template" id="login-template">
<header id="header"></header>
<div class="login">
<form class="login-form">
<h2>Log In</h2>
<div class="error" style="display:none"></div>
<input type="text" id="login-username" placeholder="Username" />
<input type="password" id="login-password" placeholder="Password" />
<button>Log In</button>
</form>
<form class="signup-form">
<h2>Sign Up</h2>
<div class="error" style="display:none"></div>
<input type="text" id="signup-username" placeholder="Username" />
<input type="password" id="signup-password" placeholder="Create a Password" />
<input type="email" id="signup-email" placeholder="Enter your Email" />
<button>Sign Up</button>
<a href="passReset.htm">Forgot Password?</a>
</div>
</script>
<script type="text/template" id="manage-todos-template">
<div id="user-info">
Signed in as <%= Parse.User.current().get("username") %> (<a href="#" class="log-out">Log out</a>)
</div>
<div class="section">
<header id="header">
<input id="new-todo" placeholder="What needs to be done?" type="text" />
</header>
<div id="main">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list">
<img src='images/spinner.gif' class='spinner' />
</ul>
</div>
<div id="todo-stats"></div>
</div>
</script>
<script type="text/template" id="item-template">
<li class="<%= done ? 'completed' : '' %>">
<div class="view">
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %>>
<label class="todo-content"><%= content %></label>
<button class="todo-destroy"></button>
</div>
<input class="edit" value="<%= content %>">
</li>
</script>
<script type="text/template" id="stats-template">
<footer id="footer">
<span id="todo-count"><strong><%= remaining %></strong> <%= remaining == 1 ? 'item' : 'items' %> left</span>
<ul id="filters">
<li>
<a href="javascript:void(0)" id="all" class="selected">All</a>
</li>
<li>
<a href="javascript:void(0)" id="active">Active</a>
</li>
<li>
<a href="javascript:void(0)" id="completed">Completed</a>
</li>
</ul>
<button id="clear-completed">Clear completed (<%= done %>)</button>
</footer>
</script>
接下来是实现上述工作的 JavaScript。
$(function() {
Parse.$ = jQuery;
// Initialize Parse with your Parse application javascript keys
Parse.initialize("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"XXXXXXXXXXXXXXXXXXXXXXX");
// Todo Model
// ----------
// Our basic Todo model has `content`, `order`, and `done` attributes.
var Todo = Parse.Object.extend("Todo", {
// Default attributes for the todo.
defaults: {
content: "empty todo...",
done: false
},
// Ensure that each todo created has `content`.
initialize: function() {
if (!this.get("content")) {
this.set({"content": this.defaults.content});
}
},
// Toggle the `done` state of this todo item.
toggle: function() {
this.save({done: !this.get("done")});
}
});
// This is the transient application state, not persisted on Parse
var AppState = Parse.Object.extend("AppState", {
defaults: {
filter: "all"
}
});
// Todo Collection
// ---------------
var TodoList = Parse.Collection.extend({
// Reference to this collection's model.
model: Todo,
// Filter down the list of all todo items that are finished.
done: function() {
return this.filter(function(todo){ return todo.get('done'); });
},
// Filter down the list to only todo items that are still not finished.
remaining: function() {
return this.without.apply(this, this.done());
},
// We keep the Todos in sequential order, despite being saved by unordered
// GUID in the database. This generates the next order number for new items.
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
// Todos are sorted by their original insertion order.
comparator: function(todo) {
return todo.get('order');
}
});
// Todo Item View
// --------------
// The DOM element for a todo item...
var TodoView = Parse.View.extend({
//... is a list tag.
tagName: "li",
// Cache the template function for a single item.
template: _.template($('#item-template').html()),
// The DOM events specific to an item.
events: {
"click .toggle" : "toggleDone",
"dblclick label.todo-content" : "edit",
"click .todo-destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
// The TodoView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between a Todo and a TodoView in this
// app, we set a direct reference on the model for convenience.
initialize: function() {
_.bindAll(this, 'render', 'close', 'remove');
this.model.bind('change', this.render);
this.model.bind('destroy', this.remove);
},
// Re-render the contents of the todo item.
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
this.input = this.$('.edit');
return this;
},
// Toggle the `"done"` state of the model.
toggleDone: function() {
this.model.toggle();
},
// Switch this view into `"editing"` mode, displaying the input field.
edit: function() {
$(this.el).addClass("editing");
this.input.focus();
},
// Close the `"editing"` mode, saving changes to the todo.
close: function() {
this.model.save({content: this.input.val()});
$(this.el).removeClass("editing");
},
// If you hit `enter`, we're through editing the item.
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
// Remove the item, destroy the model.
clear: function() {
this.model.destroy();
}
});
// The Application
// ---------------
// The main view that lets a user manage their todo items
var ManageTodosView = Parse.View.extend({
// Our template for the line of statistics at the bottom of the app.
statsTemplate: _.template($('#stats-template').html()),
// Delegated events for creating new items, and clearing completed ones.
events: {
"keypress #new-todo": "createOnEnter",
"click #clear-completed": "clearCompleted",
"click #toggle-all": "toggleAllComplete",
"click .log-out": "logOut",
"click ul#filters a": "selectFilter"
},
el: ".content",
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved to Parse.
initialize: function() {
var self = this;
_.bindAll(this, 'addOne', 'addAll', 'addSome', 'render', 'toggleAllComplete', 'logOut', 'createOnEnter');
// Main todo management template
this.$el.html(_.template($("#manage-todos-template").html()));
this.input = this.$("#new-todo");
this.allCheckbox = this.$("#toggle-all")[0];
// Create our collection of Todos
this.todos = new TodoList;
// Setup the query for the collection to look for todos from the current user
this.todos.query = new Parse.Query(Todo);
this.todos.query.equalTo("user", Parse.User.current());
this.todos.bind('add', this.addOne);
this.todos.bind('reset', this.addAll);
this.todos.bind('all', this.render);
// Fetch all the todo items for this user
this.todos.fetch();
state.on("change", this.filter, this);
},
// Logs out the user and shows the login view
logOut: function(e) {
Parse.User.logOut();
new LogInView();
this.undelegateEvents();
delete this;
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function() {
var done = this.todos.done().length;
var remaining = this.todos.remaining().length;
this.$('#todo-stats').html(this.statsTemplate({
total: this.todos.length,
done: done,
remaining: remaining
}));
this.delegateEvents();
this.allCheckbox.checked = !remaining;
},
// Filters the list based on which type of filter is selected
selectFilter: function(e) {
var el = $(e.target);
var filterValue = el.attr("id");
state.set({filter: filterValue});
Parse.history.navigate(filterValue);
},
filter: function() {
var filterValue = state.get("filter");
this.$("ul#filters a").removeClass("selected");
this.$("ul#filters a#" + filterValue).addClass("selected");
if (filterValue === "all") {
this.addAll();
} else if (filterValue === "completed") {
this.addSome(function(item) { return item.get('done') });
} else {
this.addSome(function(item) { return !item.get('done') });
}
},
// Resets the filters to display all todos
resetFilters: function() {
this.$("ul#filters a").removeClass("selected");
this.$("ul#filters a#all").addClass("selected");
this.addAll();
},
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
addOne: function(todo) {
var view = new TodoView({model: todo});
this.$("#todo-list").append(view.render().el);
},
// Add all items in the Todos collection at once.
addAll: function(collection, filter) {
this.$("#todo-list").html("");
this.todos.each(this.addOne);
},
// Only adds some todos, based on a filtering function that is passed in
addSome: function(filter) {
var self = this;
this.$("#todo-list").html("");
this.todos.chain().filter(filter).each(function(item) { self.addOne(item) });
},
// If you hit return in the main input field, create new Todo model
createOnEnter: function(e) {
var self = this;
if (e.keyCode != 13) return;
this.todos.create({
content: this.input.val(),
order: this.todos.nextOrder(),
done: false,
user: Parse.User.current(),
ACL: new Parse.ACL(Parse.User.current())
});
this.input.val('');
this.resetFilters();
},
// Clear all done todo items, destroying their models.
clearCompleted: function() {
_.each(this.todos.done(), function(todo){ todo.destroy(); });
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
this.todos.each(function (todo) { todo.save({'done': done}); });
}
});
var LogInView = Parse.View.extend({
events: {
"submit form.login-form": "logIn",
"submit form.signup-form": "signUp",
"submit form.reset-form": "reset"
},
el: ".content",
initialize: function() {
_.bindAll(this, "logIn", "signUp","reset");
this.render();
},
logIn: function(e) {
var self = this;
var username = this.$("#login-username").val();
var password = this.$("#login-password").val();
Parse.User.logIn(username, password, {
success: function(user) {
new ManageTodosView();
self.undelegateEvents();
delete self;
},
error: function(user, error) {
self.$(".login-form .error").html("Invalid username or password. Please try again.").show();
this.$(".login-form button").removeAttr("disabled");
}
});
this.$(".login-form button").attr("disabled", "disabled");
return false;
},
signUp: function(e) {
var self = this;
var username = this.$("#signup-username").val();
var password = this.$("#signup-password").val();
var email = this.$("#signup-email").val();
Parse.User.signUp(username, password,{email:email, ACL: new Parse.ACL() }, {
success: function(user) {
new ManageTodosView();
self.undelegateEvents();
delete self;
},
error: function(user, error) {
self.$(".signup-form .error").html(error.message).show();
this.$(".signup-form button").removeAttr("disabled");
}
});
this.$(".signup-form button").attr("disabled", "disabled");
return false;
},
render: function() {
this.$el.html(_.template($("#login-template").html()));
this.delegateEvents();
}
});
// The main view for the app
var AppView = Parse.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#todoapp"),
initialize: function() {
this.render();
},
render: function() {
if (Parse.User.current()) {
new ManageTodosView();
} else {
new LogInView();
}
}
});
var AppRouter = Parse.Router.extend({
routes: {
"all": "all",
"active": "active",
"completed": "completed"
},
initialize: function(options) {
},
all: function() {
state.set({ filter: "all" });
},
active: function() {
state.set({ filter: "active" });
},
completed: function() {
state.set({ filter: "completed" });
}
});
var state = new AppState;
new AppRouter;
new AppView;
Parse.history.start();
});
我知道
Parse.User.requestPasswordReset("email@example.com", {
success: function() {
// Password reset request was sent successfully
},
error: function(error) {
// Show the error message somewhere
alert("Error: " + error.code + " " + error.message);
}
});
但无法弄清楚如何让它工作。
提前致谢。
托马斯。
编辑:基本上我要问的是如何在 Parse 中设置重置/忘记密码链接/按钮。
我现在拥有的是一个电子邮件地址和一个按钮的输入。单击按钮时,我希望 Parse 向用户发送一封电子邮件,允许用户更改其密码。
我已经更改了上面的代码,并且有了更多的知识,我将问题缩小到我认为以下
reset: function (e) {
var self = this;
var email = this.$("reset-email").val();
Parse.User.requestPasswordReset({email:email}, {
success: function () {
new LogInView();
self.undelegateEvents();
delete self;
},
error: function(error) {
// Show the error message somewhere
self.$(".reset-form .error").html(error.message).show();
this.$(".reset-form button").removeAttr("disabled");
}
});
this.$(".reset-form button").attr("disabled", "disabled");
return false;
},
我得到的错误是:无效的电子邮件{}
对不起,这么草率的帖子我完全迷路了。
最终编辑:
我得到了上述工作问题在 Parse 论坛上得到了回答 https://www.parse.com/questions/parseuserrequestpasswordreset