You are on page 1of 24

Tnh nng b phiu - Discover Meteor

1 of 24

http://vi.discovermeteor.com/chapters/voting/

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

2 of 24

http://vi.discovermeteor.com/chapters/voting/

upvoters

votes

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

3 of 24

http://vi.discovermeteor.com/chapters/voting/

// Fixture data
if (Posts.find().count() === 0) {
var now = new Date().getTime();
// create two users

var tomId = Meteor.users.insert({

profile: { name: 'Tom Coleman' }

});

var tom = Meteor.users.findOne(tomId);


var sachaId = Meteor.users.insert({
profile: { name: 'Sacha Greif' }

});

var sacha = Meteor.users.findOne(sachaId);


var telescopeId = Posts.insert({
title: 'Introducing Telescope',
userId: sacha._id,

author: sacha.profile.name,
url: 'http://sachagreif.com/introducing-telescope/',
submitted: new Date(now - 7 * 3600 * 1000),
commentsCount: 2,
upvoters: [],
votes: 0

});

Comments.insert({

postId: telescopeId,
userId: tom._id,

author: tom.profile.name,
submitted: new Date(now - 5 * 3600 * 1000),

body: 'Interesting project Sacha, can I get involved?'

});

Comments.insert({

postId: telescopeId,
userId: sacha._id,

author: sacha.profile.name,

submitted: new Date(now - 3 * 3600 * 1000),


1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

4 of 24

http://vi.discovermeteor.com/chapters/voting/

body: 'You sure can Tom!'

});

Posts.insert({

title: 'Meteor',

userId: tom._id,

author: tom.profile.name,
url: 'http://meteor.com',

submitted: new Date(now - 10 * 3600 * 1000),


commentsCount: 0,
upvoters: [],
votes: 0

});

Posts.insert({

title: 'The Meteor Book',


userId: tom._id,

author: tom.profile.name,

url: 'http://themeteorbook.com',
submitted: new Date(now - 12 * 3600 * 1000),
commentsCount: 0,
upvoters: [],
votes: 0

});

for (var i = 0; i < 10; i++) {


Posts.insert({

title: 'Test post #' + i,

author: sacha.profile.name,
userId: sacha._id,

url: 'http://google.com/?q=test-' + i,

submitted: new Date(now - i * 3600 * 1000 + 1),


commentsCount: 0,
upvoters: [],
votes: 0

});

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

5 of 24

http://vi.discovermeteor.com/chapters/voting/

meteor reset

//...
var postWithSameLink = Posts.findOne({url: postAttributes.url});
if (postWithSameLink) {
return {

postExists: true,
_id: postWithSameLink._id

var user = Meteor.user();

var post = _.extend(postAttributes, {


userId: user._id,
author: user.username,
submitted: new Date(),
commentsCount: 0,
upvoters: [],
votes: 0

});

var postId = Posts.insert(post);


return {
};

_id: postId

//...

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

6 of 24

http://vi.discovermeteor.com/chapters/voting/

<template name="postItem">
<div class="post">

<a href="#" class="upvote btn btn-default"></a>


<div class="post-content">

<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
<p>

{{votes}} Votes,

submitted by {{author}},

<a href="{{pathFor 'postPage'}}">{{commentsCount}} comments</a>


{{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}}

</p>
</div>

<a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss

</div>

</template>

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

7 of 24

http://vi.discovermeteor.com/chapters/voting/

//...
Template.postItem.events({

'click .upvote': function(e) {


e.preventDefault();

Meteor.call('upvote', this._id);

});

lib/collections/posts.js

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

8 of 24

http://vi.discovermeteor.com/chapters/voting/

//...
Meteor.methods({

post: function(postAttributes) {
},

//...

upvote: function(postId) {

check(this.userId, String);
check(postId, String);

var post = Posts.findOne(postId);


if (!post)

throw new Meteor.Error('invalid', 'Post not found');

if (_.include(post.upvoters, this.userId))

throw new Meteor.Error('invalid', 'Already upvoted this post');


Posts.update(post._id, {
$addToSet: {upvoters: this.userId},

$inc: {votes: 1}
});

});

//...

$addToSet

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

9 of 24

$inc

http://vi.discovermeteor.com/chapters/voting/

disabled

<template name="postItem">
<div class="post">

<a href="#" class="upvote btn btn-default {{upvotedClass}}"></a>


<div class="post-content">

//...
</div>

</template>

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

10 of 24

http://vi.discovermeteor.com/chapters/voting/

Template.postItem.helpers({
ownPost: function() {
},

//...

domain: function() {
},

//...

upvotedClass: function() {

var userId = Meteor.userId();

if (userId && !_.include(this.upvoters, userId)) {


return 'btn-primary upvotable';

} else {
}
});

return 'disabled';

Template.postItem.events({
'click .upvotable': function(e) {
e.preventDefault();

}
});

Meteor.call('upvote', this._id);

.upvote

.upvotable

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

11 of 24

http://vi.discovermeteor.com/chapters/voting/

UI.registerHelper('pluralize', function(n, thing) {


// fairly stupid pluralizer
if (n === 1) {

return '1 ' + thing;

} else {
}
});

return n + ' ' + thing + 's';

UI.registerHelper

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

12 of 24

http://vi.discovermeteor.com/chapters/voting/

<template name="postItem">
//...
<p>

{{pluralize votes "Vote"}},


submitted by {{author}},

<a href="{{pathFor 'postPage'}}">{{pluralize commentsCount "comment"}}

{{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}}

</p>

//...
</template>

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

13 of 24

http://vi.discovermeteor.com/chapters/voting/

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

14 of 24

http://vi.discovermeteor.com/chapters/voting/

//...
Meteor.methods({

post: function(postAttributes) {
},

//...

upvote: function(postId) {

check(this.userId, String);
check(postId, String);

var affected = Posts.update({


_id: postId,

upvoters: {$ne: this.userId}

}, {
$addToSet: {upvoters: this.userId},
$inc: {votes: 1}

});
if (! affected)

throw new Meteor.Error('invalid', "You weren't able to upvote that post

});

//...

id

id

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

15 of 24

http://vi.discovermeteor.com/chapters/voting/

> Posts.update(postId, {$set: {votes: 10000}});

postId

deny()

Posts

update

collections/posts.js

update

postList

posts

/new/5

newPosts

/best/5

NewPostsListController

bestPosts

PostsListController

BestPostsListController

/new

/best

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

16 of 24

this.sort

http://vi.discovermeteor.com/chapters/voting/

home

newPosts

BestPostsListController

{submitted: -1}

NewPostsListController

PostsListController

NewPostsListController

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

17 of 24

http://vi.discovermeteor.com/chapters/voting/

//...
PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,

postsLimit: function() {
},

return parseInt(this.params.postsLimit) || this.increment;

findOptions: function() {
},

return {sort: this.sort, limit: this.postsLimit()};

subscriptions: function() {
},

this.postsSub = Meteor.subscribe('posts', this.findOptions());

posts: function() {
return Posts.find({}, this.findOptions());
},

data: function() {
var hasMore = this.posts().count() === this.postsLimit();
return {

posts: this.posts(),

}
});

};

ready: this.postsSub.ready,
nextPath: hasMore ? this.nextPath() : null

NewPostsController = PostsListController.extend({
sort: {submitted: -1, _id: -1},

nextPath: function() {
return Router.routes.newPosts.path({postsLimit: this.postsLimit()
}

});

BestPostsController = PostsListController.extend({
sort: {votes: -1, submitted: -1, _id: -1},
nextPath: function() {
}

return Router.routes.bestPosts.path({postsLimit: this.postsLimit()

});

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

18 of 24

http://vi.discovermeteor.com/chapters/voting/

Router.route('/', {
name: 'home',

controller: NewPostsController

});

Router.route('/new/:postsLimit?', {name: 'newPosts'});

Router.route('/best/:postsLimit?', {name: 'bestPosts'});

PostsListController

_id

NewPostsController

votes

postsList

nextPath

BestPostsController

Router.route('/:postsLimit?', {
name: 'postsList'

})

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

19 of 24

http://vi.discovermeteor.com/chapters/voting/

<template name="header">
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">

<button type="button" class="navbar-toggle collapsed" data-toggle=


<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>

</button>

<a class="navbar-brand" href="{{pathFor 'home'}}">Microscope</a>

</div>

<div class="collapse navbar-collapse" id="navigation">


<ul class="nav navbar-nav">

<li>
<a href="{{pathFor 'newPosts'}}">New</a>
</li>

<li>
<a href="{{pathFor 'bestPosts'}}">Best</a>
</li>

{{#if currentUser}}

<li>
<a href="{{pathFor 'postSubmit'}}">Submit Post</a>
</li>

<li class="dropdown">
{{> notifications}}
</li>

{{/if}}

</ul>
<ul class="nav navbar-nav navbar-right">
{{> loginButtons}}

</ul>

</div>

</div>

</nav>

</template>

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

20 of 24

http://vi.discovermeteor.com/chapters/voting/

'click .delete': function(e) {


e.preventDefault();
if (confirm("Delete this post?")) {
var currentPostId = this._id;
Posts.remove(currentPostId);
}

Router.go('home');

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

21 of 24

<li>

http://vi.discovermeteor.com/chapters/voting/

home

newPosts

activeRouteClass

/new

header.js

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

22 of 24

http://vi.discovermeteor.com/chapters/voting/

<template name="header">
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">

<button type="button" class="navbar-toggle collapsed" data-toggle=


<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>

</button>

<a class="navbar-brand" href="{{pathFor 'home'}}">Microscope</a>

</div>

<div class="collapse navbar-collapse" id="navigation">


<ul class="nav navbar-nav">

<li class="{{activeRouteClass 'home' 'newPosts'}}">


<a href="{{pathFor 'newPosts'}}">New</a>
</li>

<li class="{{activeRouteClass 'bestPosts'}}">


<a href="{{pathFor 'bestPosts'}}">Best</a>
</li>

{{#if currentUser}}

<li class="{{activeRouteClass 'postSubmit'}}">


<a href="{{pathFor 'postSubmit'}}">Submit Post</a>
</li>

<li class="dropdown">
{{> notifications}}
</li>

{{/if}}

</ul>
<ul class="nav navbar-nav navbar-right">
{{> loginButtons}}

</ul>

</div>

</div>

</nav>

</template>

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

23 of 24

http://vi.discovermeteor.com/chapters/voting/

Template.header.helpers({

activeRouteClass: function(/* route names */) {


var args = Array.prototype.slice.call(arguments, 0);
args.pop();

var active = _.any(args, function(name) {

return Router.current() && Router.current().route.getName() ===

});
}

return active && 'active';

});

1/10/2016 9:37 AM

Tnh nng b phiu - Discover Meteor

24 of 24

http://vi.discovermeteor.com/chapters/voting/

arguments

false && myString

any() true

arguments

myString

activeRouteClass

boolean && string

&& myString

pop()

any()

false

true

1/10/2016 9:37 AM

You might also like