Skip to content

Commit

Permalink
Step 15: Meteor methods
Browse files Browse the repository at this point in the history
  • Loading branch information
DAB0mB committed Dec 14, 2016
1 parent 9eeebfd commit 769c64f
Show file tree
Hide file tree
Showing 2 changed files with 773 additions and 0 deletions.
195 changes: 195 additions & 0 deletions manuals/templates/step15.md
@@ -0,0 +1,195 @@
In this step we will learn how to use Meteor Methods to
implement server side logic of the party invitation feature.

> A capital "M" will be used with Meteor "M"ethods to avoid confusion with Javascript function methods
Meteor Methods are a more secure and reliable way to
implement complex logic on the server side in comparison to the direct
manipulations of Mongo collections. Also, we'll touch briefly on
Meteor's UI latency compensation mechanism that comes with these Methods.
This is one of the great Meteor concepts that allows for rapid UI changes.

# Invitation Method

Let's create a new file `both/methods/parties.methods.ts`, and add the following `invite` Meteor Method:

{{{diff_step 15.1}}}

We used a special API method `Meteor.methods` to register
a new Meteor Method. Again, don't forget to import your created `parties.methods.ts` module
in the server's `main.ts` module to have the Methods defined properly:

{{{diff_step 15.2}}}

### Latency Compensation

UI Latency compensation is one of the features that makes Meteor stand out amongst most other Web frameworks, thanks again to the isomorphic environment and Meteor Methods.
In short, visual changes are applied immediately as a response to some user action,
even before the server responds to anything. If you want to read up more about how the view can securely be updated
even before the server is contacted proceed to an [Introduction to Latency Compensation](https://meteorhacks.com/introduction-to-latency-compensation) written by Arunoda.

But to make it happen, we need to define our Methods on the client side as well. Let's import our Methods in `client/main.ts`:

{{{diff_step 15.3}}}

### Validating Methods with Check

As you can see, we've also done a lot of checks to verify that
all arguments passed down to the method are valid.

First the validity of the arguments' types are checked, and then
the business logic associated with them is checked.

Type validation checks, which are essential for the JavaScript methods dealing with the storage's data,
are done with the help of a handy Meteor's package called ["check"](https://atmospherejs.com/meteor/check).

meteor add check

Then, if everything is valid, we send an invitation email.
Here we are using another handy Meteor's package titled ["email"](https://atmospherejs.com/meteor/email).

meteor add email

At this point, we are ready to add a call to the new Method from the client.

Let's add a new button right after each username or email in that
list of users to invite in the `PartyDetails`'s template:

{{{diff_step 15.5}}}

And then, change the component to handle the click event and invite a user:

{{{diff_step 15.6}}}

> We used `MeteorObservable.call` which triggers a Meteor server method, which triggers `next` callback when the server returns a response, and `error` when the server returns an error.
### Updating Invited Users Reactively

One more thing before we are done with the party owner's invitation
logic. We, of course, would like to make this list of users
change reactively, i.e. each user disappears from the list
when the invitation has been sent successfully.

It's worth mentioning that each party should change appropriately
when we invite a user — the party `invited` array should update
in the local Mongo storage. If we wrap the line where
we get the new party with the `MeteorObservable.autorun` method, this code should
re-run reactively:

{{{diff_step 15.7}}}

> Now each time the Party object changes, we will re-fetch it from the collection and assign it to the Component property. Our view known to update itself's because we used `zone()` operator in order to connect between Meteor data and Angular change detection.
Now its time to update our users list.
We'll move the line that gets the users list into a
separate method, provided with the list of IDs of already invited users;
and call it whenever we need: right in the above `MeteorObservable.autorun` method after the party assignment and in the subscription, like that:

{{{diff_step 15.8}}}

Here comes test time. Let's add a couple of new users.
Then login as an old user and add a new party.
Go to the party: you should see a list of all users including
newly created ones. Invite several of them — each item in the list
should disappear after a successful invitation.

What's important to notice here is that each user item in the users list
disappears right after the click, even before the message about
the invitation was successfully sent. That's the latency compensation at work!

# User Reply

Here we are going to implement the user reply to the party invitation request.

First of all, let's make parties list a bit more secure,
which means two things: showing private parties to those who have been invited
or to owners, and elaborate routing activation defense for the party details view:

{{{diff_step 15.9}}}

The next thing is a party invitee response to the invitation itself. Here, as usual,
we'll need to update the server side and UI. For the server,
let's add a new `reply` Meteor Method:

{{{diff_step 15.10}}}

As you can see, a new property, called "rsvp", was added
above to collect user responses of this particular party.
One more thing. Let's update the party declaration file to
make TypeScript resolve and compile with no warnings:

{{{diff_step 15.11}}}

For the UI, let's add three new buttons onto the party details view.
These will be "yes", "no", "maybe" buttons and users responses accordingly:

{{{diff_step 15.12}}}

Then, handle click events in the PartyDetails component:

{{{diff_step 15.13}}}

### Rsvp Pipe

Last, but not the least, let's show statistics of the invitation responses for the party owner.
Let's imagine that any party owner
would like to know the total number of those who declined, accepted, or remain tentative.
This is a perfect use case to add a new stateful pipe, which takes as
an input a party and a one of the RSVP responses, and calculates the total number of responses
associated with this, provided as a parameter we'll call "response".

Add a new pipe to the `client/imports/app/shared/rsvp.pipe.ts` as follows:

{{{diff_step 15.14}}}

The RSVP Pipe fetches the party and returns the count of `rsvps` Array, due the fact that we binded the change detection of Angular 2 and the Meteor data change, each time the data changes, Angular 2 renders the view again, and the RSVP Pipe will run again and update the view with the new number.

It's also worth mentioning that the arguments of a Pipe implementation inside a template are passed to the `transform` method in the same form. Only difference is that the first argument of `transform` is a value to be transformed. In our case, passed only the RSVP response, hence, we are taking the first
value in the list.

An example:

```js
// usage: text | subStr:20:50
@Pipe({name: 'subStr'})
class SubStrPipe implements PipeTransform {
transform(text: string, starts: number, ends: number) {
return text.substring(starts, ends);
}
}
```

Let's make use of this pipe in the `PartiesList` component:

{{{diff_step 15.15}}}

And let's add the new Pipe to the shared declarations file:

{{{diff_step 15.16}}}

Now it's testing time! Check that an invited user is able to reply to an
invitation, and also verify that the party's statistics update properly and reactively.
Login as an existing user. Add a new party, go to the party and
invite some other users. Then, open a new browser window in the anonymous mode along with the current window,
and login as the invited user there. Go to the party details page, and reply, say, "no";
the party's statistics on the first page with the parties list should duly update.

# Challenge

There is one important thing that we missed. Besides the party invitation
statistics, each user would like to know if she has already responded, in case she forgot,
to a particular invitation. This step's challenge will be to add this status
information onto the PartyDetails's view and make it update reactively.

> Hint: In order to make it reactive, you'll need to add one more handler into
> the party `MeteorObservable.autorun`, like the `getUsers` method in the this step above.
# Summary

We've just finished the invitation feature in this step, having added bunch of new stuff.
Socially is looking much more mature with Meteor Methods on board. We can give ourselves
a big thumbs-up for that!

Though, some places in the app can certainly be improved. For example,
we still show some private information to all invited users, which should be designated only for the party owner.
We'll fix this in the next step.

0 comments on commit 769c64f

Please sign in to comment.