7:添加用户账户

7.1:密码认证

Meteor 自带基本的认证和账户管理系统,因此您只需要添加 accounts-password 即可启用用户名和密码认证。

meteor add accounts-password

还有许多其他认证方法受支持。您可以在这里了解更多关于账户系统的信息 here

我们还建议您安装 bcrypt 节点模块。否则,您将看到一条警告,提示您正在使用纯 Javascript 实现。

meteor npm install --save bcrypt

您应该始终使用 meteor npm 而不是 npm,以便始终使用 Meteor 固定版本的 npm。这有助于避免由于不同版本的 npm 安装不同的模块而导致的问题。

7.2:创建用户账户

现在您可以为您的应用创建一个默认用户。我们将使用 meteorite 作为用户名。如果我们没有在数据库中找到它,则在服务器启动时创建一个新用户。

server/main.js

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { TasksCollection } from '/imports/api/TasksCollection';

..

const SEED_USERNAME = 'meteorite';
const SEED_PASSWORD = 'password';

Meteor.startup(() => {
  if (!Accounts.findUserByUsername(SEED_USERNAME)) {
    Accounts.createUser({
      username: SEED_USERNAME,
      password: SEED_PASSWORD,
    });
  }
  ..
});

您在应用 UI 中应该还没有看到任何变化。

7.3:登录表单

您需要提供一种方法供用户输入凭据并进行身份验证。为此,我们需要一个表单。

我们的登录表单将很简单,只有两个字段(用户名和密码)和一个按钮。您应该使用 Meteor.loginWithPassword(username, password); 来使用提供的输入对用户进行身份验证。

imports/ui/Login.html

<template name="login">
    <form class="login-form">
        <div>
            <label htmlFor="username">Username</label>

            <input
                    type="text"
                    placeholder="Username"
                    name="username"
                    required
            />
        </div>

        <div>
            <label htmlFor="password">Password</label>

            <input
                    type="password"
                    placeholder="Password"
                    name="password"
                    required
            />
        </div>
        <div>
            <button type="submit">Log In</button>
        </div>
    </form>
</template>

imports/ui/Login.js

import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import './Login.html';

Template.login.events({
  'submit .login-form'(e) {
    e.preventDefault();

    const target = e.target;

    const username = target.username.value;
    const password = target.password.value;

    Meteor.loginWithPassword(username, password);
  }
});

确保还在 App.js 中导入登录表单。

/imports/ui/App.js

import { Template } from "meteor/templating";
import { TasksCollection } from "../api/TasksCollection";
import { ReactiveDict } from "meteor/reactive-dict";
import "./App.html";
import "./Task.js";
import "./Login.js";
...

现在您有了表单,是时候使用它了。

7.4:需要身份验证

我们的应用应该只允许已认证的用户访问其任务管理功能。

我们可以通过在没有已认证用户时从模板渲染 Login 来实现这一点。否则,我们将返回表单、过滤器和列表组件。

为了实现这一点,我们将在 App.html 上的主 div 中使用条件测试。

imports/ui/App.html

...
                </div>
            </div>
        </header>

        <div class="main">
            {{#if isUserLogged}}

                {{> form }}

                <div class="filter">
                    <button id="hide-completed-button">
                        {{#if hideCompleted}}
                                Show All
                        {{else}}
                                Hide Completed
                        {{/if}}
                    </button>
                </div>

                <ul class="tasks">
                    {{#each tasks}}
                        {{> task}}
                    {{/each}}
                </ul>
            {{else}}
                {{> login }}
            {{/if}}
        </div>
...

如您所见,如果用户已登录,我们将渲染整个应用(isUserLogged)。否则,我们将渲染 Login 模板。现在让我们创建我们的助手 isUserLogged

imports/ui/App.js

...

const getUser = () => Meteor.user();
const isUserLogged = () => !!getUser();
...

Template.mainContainer.helpers({
  ...,
  isUserLogged() {
    return isUserLogged();
  }
});

7.5:登录表单样式

好的,现在让我们为登录表单设置样式。

client/main.css

.login-form {
  display: flex;
  flex-direction: column;
  height: 100%;

  justify-content: center;
  align-items: center;
}

.login-form > div {
  margin: 8px;
}

.login-form > div > label {
  font-weight: bold;
}

.login-form > div  > input {
  flex-grow: 1;
  box-sizing: border-box;
  padding: 10px 6px;
  background: transparent;
  border: 1px solid #aaa;
  width: 100%;
  font-size: 1em;
  margin-right: 16px;
  margin-top: 4px;
}

.login-form > div > input:focus {
  outline: 0;
}

.login-form > div > button {
  background-color: #62807e;
}

现在您的登录表单应该看起来很漂亮且居中。

7.6:服务器启动

从现在开始,每个任务都应该有一个所有者。转到您的数据库,如您之前所学,并从那里删除所有任务。

db.tasks.remove({});

更改您的 server/main.js 以使用您的 meteorite 用户作为所有者添加种子任务。

更改后,请确保重新启动服务器,以便 Meteor.startup 块可以再次运行。当您在服务器端代码中进行更改时,这可能会自动发生。

server/main.js

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { TasksCollection } from '/imports/api/TasksCollection';

const insertTask = (taskText, user) =>
  TasksCollection.insert({
    text: taskText,
    userId: user._id,
    createdAt: new Date(),
  });

const SEED_USERNAME = 'meteorite';
const SEED_PASSWORD = 'password';

Meteor.startup(() => {
  if (!Accounts.findUserByUsername(SEED_USERNAME)) {
    Accounts.createUser({
      username: SEED_USERNAME,
      password: SEED_PASSWORD,
    });
  }

  const user = Accounts.findUserByUsername(SEED_USERNAME);

  if (TasksCollection.find().count() === 0) {
    [
      'First Task',
      'Second Task',
      'Third Task',
      'Fourth Task',
      'Fifth Task',
      'Sixth Task',
      'Seventh Task',
    ].forEach(taskText => insertTask(taskText, user));
  }
});

我们正在使用一个名为 userId 的新字段和我们的用户 _id 字段。我们还设置了 createdAt 字段。

7.7:任务所有者

现在您可以在 UI 中按已认证的用户过滤任务。在从 Mini Mongo 获取任务时,使用用户 _id 将字段 userId 添加到您的 Mongo 选择器。

imports/ui/App.js

...
const getTasksFilter = () => {
  const user = getUser();

  const hideCompletedFilter = { isChecked: { $ne: true } };

  const userFilter = user ? { userId: user._id } : {};

  const pendingOnlyFilter = { ...hideCompletedFilter, ...userFilter };

  return { userFilter, pendingOnlyFilter };
}

...

Template.mainContainer.helpers({
  tasks() {
    const instance = Template.instance();
    const hideCompleted = instance.state.get(HIDE_COMPLETED_STRING);

    const { pendingOnlyFilter, userFilter } = getTasksFilter();

    if (!isUserLogged()) {
      return [];
    }

    return TasksCollection.find(hideCompleted ? pendingOnlyFilter : userFilter, {
      sort: { createdAt: -1 },
    }).fetch();
  },
  ...,
  incompleteCount() {
    if (!isUserLogged()) {
      return '';
    }

    const { pendingOnlyFilter } = getTasksFilter();

    const incompleteTasksCount = TasksCollection.find(pendingOnlyFilter).count();
    return incompleteTasksCount ? `(${incompleteTasksCount})` : '';
  },
  ...
});

此外,在创建新任务时,更新 insert 调用以包含字段 userId

imports/ui/App.js

...
Template.form.events({
  "submit .task-form"(event) {
   ...
    TasksCollection.insert({
      text,
      userId: getUser()._id,
      createdAt: new Date(), // current time
    });
   ...
  }
});
...

7.8:注销

我们还可以通过在应用栏下方显示所有者的用户名来组织我们的任务。让我们添加一个新的 div,用户可以在其中点击并从应用中注销。

imports/ui/App.html

...
        <div class="main">
            {{#if isUserLogged}}
                <div class="user">
                    {{getUser.username}} 🚪
                </div>
                {{> form }}

...

现在,让我们创建 getUser 助手并实现当用户点击此 div 时将注销用户的事件。注销是通过调用函数 Meteor.logout() 来完成的。

imports/ui/App.js

...

Template.mainContainer.events({
  ...,
  'click .user'() {
    Meteor.logout();
  }
});

...

Template.mainContainer.helpers({
  ...,
  getUser() {
    return getUser();
  }
});

...

请记住也要设置用户名的样式。

client/main.css

.user {
  display: flex;

  align-self: flex-end;

  margin: 8px 16px 0;
  font-weight: bold;
}

呼!您在此步骤中做了很多工作。对用户进行了身份验证,在任务中设置了用户,并为用户提供了一种注销的方法。

您的应用现在应该如下所示

回顾:您可以查看您的代码应该是什么样子 here

在下一步中,我们将开始使用方法在检查某些条件后更改数据。

在 GitHub 上编辑
// 搜索框