Meteor 編輯帖子

2022-06-30 13:57 更新

編輯帖子

上一章,我們已經(jīng)學會了創(chuàng)建帖子,下面來學習編輯和刪除它們。頁面的代碼非常簡單,讓我們在這個時候來談論一下 Meteor 是如何管理用戶權(quán)限。

讓我們先設置我們的路由器,添加一個可以訪問帖子編輯頁的路徑,并設置它的數(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 模板就包含一個相當標準的表單:

<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 來配合這個的模板:

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) {
        // 向用戶顯示錯誤信息
        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)對這些代碼都相當?shù)氖煜ち恕?/p>

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

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

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

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

添加鏈接

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

<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>

當然,我們不能讓你的帖子提供給其他用戶去編輯。這就要通過 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;
  }
});

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

設置權(quán)限

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

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

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

在創(chuàng)建帖子這個章節(jié),我們拋棄了 allow() 方法,因為我們只通過服務端方法去插入新的帖子(繞過了 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); }
});

//...

限制編輯

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

我們用 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) {
    // 只能更改如下兩個字段:
    return (_.without(fieldNames, 'url', 'title').length > 0);
  }
});

//...

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

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

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

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

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

我們該如何去選擇使用呢?

當操作相對比較直觀,你可以通過 allowdeny 去設置你的規(guī)則的時候,直接在客戶端進行操作通常會更簡單。

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

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

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

請閱讀我們的 blog 來深入了解這個話題。

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號