Meteor 編輯帖子

2022-06-30 13:57 更新

編輯帖子

上一章,我們已經(jīng)學(xué)會(huì)了創(chuàng)建帖子,下面來學(xué)習(xí)編輯和刪除它們。頁面的代碼非常簡(jiǎn)單,讓我們?cè)谶@個(gè)時(shí)候來談?wù)撘幌?Meteor 是如何管理用戶權(quán)限。

讓我們先設(shè)置我們的路由器,添加一個(gè)可以訪問帖子編輯頁的路徑,并設(shè)置它的數(shù)據(jù)上下文:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.route('/', {name: 'postsList'});

Router.route('/posts/:_id', {
  name: 'postPage',
  data: function() { return Posts.findOne(this.params._id); }
});

Router.route('/posts/:_id/edit', {
  name: 'postEdit',
  data: function() { return Posts.findOne(this.params._id); }
});

Router.route('/submit', {name: 'postSubmit'});

var requireLogin = function() {
  if (! Meteor.user()) {
    if (Meteor.loggingIn()) {
      this.render(this.loadingTemplate);
    } else {
      this.render('accessDenied');
    }
  } else {
    this.next();
  }
}

Router.onBeforeAction('dataNotFound', {only: 'postPage'});
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});

帖子編輯模板

我們可以現(xiàn)在專注模板了。我們的 postEdit 模板就包含一個(gè)相當(dāng)標(biāo)準(zhǔn)的表單:

<template name="postEdit">
  <form class="main form">
    <div class="form-group">
      <label class="control-label" for="url">URL</label>
      <div class="controls">
          <input name="url" id="url" type="text" value="{{url}}" placeholder="Your URL" class="form-control"/>
      </div>
    </div>
    <div class="form-group">
      <label class="control-label" for="title">Title</label>
      <div class="controls">
          <input name="title" id="title" type="text" value="{{title}}" placeholder="Name your post" class="form-control"/>
      </div>
    </div>
    <input type="submit" value="Submit" class="btn btn-primary submit"/>
    <hr/>
    <a class="btn btn-danger delete" href="#">Delete post</a>
  </form>
</template>

post_edit.js 來配合這個(gè)的模板:

Template.postEdit.events({
  'submit form': function(e) {
    e.preventDefault();

    var currentPostId = this._id;

    var postProperties = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val()
    }

    Posts.update(currentPostId, {$set: postProperties}, function(error) {
      if (error) {
        // 向用戶顯示錯(cuò)誤信息
        alert(error.reason);
      } else {
        Router.go('postPage', {_id: currentPostId});
      }
    });
  },

  'click .delete': function(e) {
    e.preventDefault();

    if (confirm("Delete this post?")) {
      var currentPostId = this._id;
      Posts.remove(currentPostId);
      Router.go('postsList');
    }
  }
});

相信你現(xiàn)在已經(jīng)對(duì)這些代碼都相當(dāng)?shù)氖煜ち恕?/p>

我們有兩個(gè)事件回調(diào)函數(shù):一個(gè)用于表單的 submit 事件,一個(gè)用于刪除鏈接的 click 事件。

刪除鏈接的回調(diào)函數(shù)是非常簡(jiǎn)單的:先防止默認(rèn)點(diǎn)擊事件,然后提示確認(rèn)窗口。如果確認(rèn)刪除,它將從模板的數(shù)據(jù)上下文中獲得當(dāng)前帖子的 ID ,然后刪除它,最后把用戶重定向到主頁。

更新的回調(diào)函數(shù)需要長(zhǎng)一點(diǎn)時(shí)間,但并不復(fù)雜。在防止默認(rèn)提交事件然后獲取了當(dāng)前帖子之后,我們將從表單中獲取相關(guān)字段的值,并將它們存儲(chǔ)在一個(gè) postProperties 的對(duì)象中。

然后,我們把該對(duì)象通過 $set 操作符(只更新指定字段的值,保留其他字段的值)傳遞給 Meteor 的 Collection.update() 方法,并通過回調(diào)函數(shù)去判斷如果更新失敗就顯示錯(cuò)誤信息;如果更新成功了,將自動(dòng)返回到該帖子的頁面。

添加鏈接

我們還應(yīng)該添加一個(gè)編輯帖子的鏈接,以便用戶可以訪問到帖子編輯頁面:

<template name="postItem">
  <div class="post">
    <div class="post-content">
      <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
      <p>
        submitted by {{author}}
        {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}}
      </p>
    </div>
    <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss</a>
  </div>
</template>

當(dāng)然,我們不能讓你的帖子提供給其他用戶去編輯。這就要通過 ownPost helper 來幫忙:

Template.postItem.helpers({
  ownPost: function() {
    return this.userId === Meteor.userId();
  },
  domain: function() {
    var a = document.createElement('a');
    a.href = this.url;
    return a.hostname;
  }
});

我們的帖子編輯表單看起來很好,但是目前還不能夠進(jìn)行任何的編輯,這是為什么?

設(shè)置權(quán)限

自從我們移除了 insecure 包,現(xiàn)在所有客戶端的修改都會(huì)被拒絕。

為了解決這個(gè)問題,我們需要建立一些權(quán)限規(guī)則。首先,在 lib 目錄下創(chuàng)建一個(gè)新的 permissions.js 文件。這樣做將會(huì)首先加載我們權(quán)限文件(它在服務(wù)端和客戶端都可以被加載到):

// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
  return doc && doc.userId === userId;
}

在創(chuàng)建帖子這個(gè)章節(jié),我們拋棄了 allow() 方法,因?yàn)槲覀冎煌ㄟ^服務(wù)端方法去插入新的帖子(繞過了 allow() 方法)。

但是現(xiàn)在我們要在客戶端編輯和刪除帖子!我們回到 posts.js 文件并添加 allow()

Posts = new Mongo.Collection('posts');

Posts.allow({
  update: function(userId, post) { return ownsDocument(userId, post); },
  remove: function(userId, post) { return ownsDocument(userId, post); }
});

//...

限制編輯

盡管你可以編輯自己的帖子,但并不意味著你可以允許去編輯帖子的每個(gè)屬性。例如,我們不允許用戶創(chuàng)建一個(gè)帖子之后,再將其分配給其他用戶。

我們用 Meteor 的 deny() 方法,以確保用戶只能編輯特定的字段:

Posts = new Mongo.Collection('posts');

Posts.allow({
  update: function(userId, post) { return ownsDocument(userId, post); },
  remove: function(userId, post) { return ownsDocument(userId, post); }
});

Posts.deny({
  update: function(userId, post, fieldNames) {
    // 只能更改如下兩個(gè)字段:
    return (_.without(fieldNames, 'url', 'title').length > 0);
  }
});

//...

代碼中的 fieldNames 數(shù)組,它包含了需要被修改的字段,并使用 Underscorewithout() 方法返回一個(gè)不包含 urltitle 字段的子數(shù)組。

正常情況下,這個(gè)數(shù)組應(yīng)該是空的,它的長(zhǎng)度應(yīng)該是0。如果有人采取其他操作,這個(gè)數(shù)組的長(zhǎng)度將變?yōu)?或更多,回調(diào)函數(shù)將返回 true (因此禁止更新)。

你也許注意到了在我們的代碼中沒有檢查鏈接是否重復(fù)的代碼。這就意味著用戶成功添加一個(gè)鏈接后,再編輯時(shí)就會(huì)繞過檢查。這個(gè)問題同樣可以通過為編輯帖子表單使用 Meteor 內(nèi)置方法來解決,但是我們將它作為練習(xí)留給讀者。

內(nèi)置方法的回調(diào) vs 客戶端數(shù)據(jù)操作

創(chuàng)建帖子,我們使用的是 postInsert 的內(nèi)置方法,而編輯和刪除帖子,我們直接在客戶端調(diào)用 updateremove,并通過 allowdeny 去限制使用權(quán)限。

我們?cè)撊绾稳ミx擇使用呢?

當(dāng)操作相對(duì)比較直觀,你可以通過 allowdeny 去設(shè)置你的規(guī)則的時(shí)候,直接在客戶端進(jìn)行操作通常會(huì)更簡(jiǎn)單。

然而,一旦你需要做一些在用戶控制以外的事情(比如設(shè)置一個(gè)新帖子的時(shí)間戳,或者把帖子分配到正確的用戶),這種情況使用內(nèi)置方法會(huì)更好。

內(nèi)置方法也適用在其他的一些情景:

  • 當(dāng)你需要通過內(nèi)置方法的回調(diào)函數(shù)去獲得返回值的時(shí)候,而不是等待響應(yīng)和同步才傳遞的數(shù)據(jù)。
  • 對(duì)于一些繁重的數(shù)據(jù)庫操作,比如要提取大量的數(shù)據(jù)集合。
  • 計(jì)算或者合計(jì)數(shù)據(jù)的時(shí)候(比如:計(jì)數(shù)、平均值、求和)。

請(qǐng)閱讀我們的 blog 來深入了解這個(gè)話題。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)