Skip to content
This repository was archived by the owner on Oct 20, 2021. It is now read-only.

Vue #182

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Vue #182

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Feel free to submit a pull request to improve the content!
1. [Blaze tutorial](https://www.meteor.com/tutorials/blaze/creating-an-app): [`/content/blaze`](https://github.com/meteor/tutorials/tree/master/content/blaze)
2. [Angular tutorial](https://www.meteor.com/tutorials/angular/creating-an-app): [`/content/angular`](https://github.com/meteor/tutorials/tree/master/content/angular)
3. [React tutorial](https://www.meteor.com/tutorials/react/creating-an-app): [`/content/react`](https://github.com/meteor/tutorials/tree/master/content/react)
4. [Vue tutorial](https://www.meteor.com/tutorials/vue/creating-an-app): [`/content/vue`](https://github.com/meteor/tutorials/tree/master/content/vue)

### Tutorial step-by-step repositories

Expand All @@ -17,6 +18,7 @@ We also maintain all of the tutorials as step-by-step git repositories here:
1. [Blaze](https://github.com/meteor/simple-todos)
2. [Angular](https://github.com/meteor/simple-todos-angular)
3. [React](https://github.com/meteor/simple-todos-react)
4. [Vue](https://github.com/meteor/simple-todos-vue)

### Tutorial viewer

Expand Down
2 changes: 1 addition & 1 deletion content/react/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if (Meteor.isClient) {
TutorialRegistry.registerTutorial("react", {
title: "Simple Todos React",
subtitle: "Learn how to use Meteor and React together",
tutorialSourceLink: "github.com/meteor/tutorials/content/angular",
tutorialSourceLink: "github.com/meteor/tutorials/content/react",
steps: [
{
title: "Creating an app",
Expand Down
74 changes: 74 additions & 0 deletions content/vue/metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
if (Meteor.isClient) {
DiffBox.registerTutorial("simple-todos-vue", {
gitHubRepoName: "meteor/simple-todos-vue",
patchFilename: "generated/vue.multi.patch"
});
}

TutorialRegistry.registerTutorial("vue", {
title: "Simple Todos Vue",
subtitle: "Learn how to use Meteor and Vue together",
tutorialSourceLink: "github.com/meteor/tutorials/content/vue",
steps: [
{
title: "Creating an app",
slug: "creating-an-app",
template: "sharedStep01"
},
{
title: 'Components',
slug: "components",
template: 'vue-step02'
},
{
title: 'Collections',
slug: "collections",
template: 'vue-step03'
},
{
title: 'Forms and events',
slug: "forms-and-events",
template: 'vue-step04'
},
{
title: 'Update and remove',
slug: "update-and-remove",
template: 'vue-step05'
},
{
title: 'Running on mobile',
slug: "running-on-mobile",
template: "sharedStep06"
},
{
title: 'Temporary UI state',
slug: "temporary-ui-state",
template: 'vue-step07'
},
{
title: 'Adding user accounts',
slug: "adding-user-accounts",
template: 'vue-step08'
},
{
title: 'Security with methods',
slug: "security-with-methods",
template: 'vue-step09'
},
{
title: 'Publish and subscribe',
slug: "publish-and-subscribe",
template: 'vue-step10'
},
{
title: 'Testing',
slug: "testing",
template: 'vue-step11'
},
{
title: 'Next steps',
slug: "next-steps",
template: 'vue-step12'
}
]
});
77 changes: 77 additions & 0 deletions content/vue/step02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{{#template name="vue-step02"}}
# Defining views with React components

To start working with React as our view library, let's add some NPM packages which will allow us to get started with React.

Open a new terminal in the same directory as your running app, and type:

```sh
meteor npm install --save react react-dom
```

> Note: `meteor npm` supports the same features as `npm`, though the difference can be important. Consult the [`meteor npm` documentation](https://docs.meteor.com/commandline.html#meteornpm) for more information.

### Replace the starter code

To get started, let's replace the code of the default starter app. Then we'll talk about what it does.

First, replace the content of the initial HTML file:

{{> DiffBox tutorialName="simple-todos-react" step="2.2"}}

Second, replace the contents of **`client/main.js`** with:

{{> DiffBox tutorialName="simple-todos-react" step="2.3"}}

Now we need to create a new directory called `imports`, a specially-named directory which will behave differently than other directories in the project. Files outside the `imports` directory will be loaded automatically when the Meteor server starts, while files inside the `imports` directory will only load when an `import` statement is used to load them.

After creating the `imports` directory, we will create two new files inside it:

{{> DiffBox tutorialName="simple-todos-react" step="2.4"}}

{{> DiffBox tutorialName="simple-todos-react" step="2.5"}}

We just added three things to our app:

1. An `App` React component
2. A `Task` React component
3. Some initialization code (in our `client/main.js` client JavaScript entrypoint), in a `Meteor.startup` block, which knows how to call code when the page is loaded and ready. This code loads the other components and renders them into the `#render-target` html element.

You can read more about how imports work and how to structure your code in the [Application Structure article](http://guide.meteor.com/structure.html) of the Meteor Guide.

Later in the tutorial, we will refer to these components when adding or changing code.

### Check the result

In our browser, the app should **roughly** look like the following (though much less pretty):

> #### Todo List
> - This is task 1
> - This is task 2
> - This is task 3

If your app doesn't look like this, use the GitHub link at the top right corner of each code snippet to see the entire file, and make sure your code matches the example.

### HTML files define static content

Meteor parses all of the HTML files in your app folder and identifies three top-level tags: **<head>**, **<body>**, and **<template>**.

Everything inside any <head> tags is added to the `head` section of the HTML sent to the client, and everything inside <body> tags is added to the `body` section, just like in a regular HTML file.

Everything inside <template> tags is compiled into Meteor _templates_, which can be included inside HTML with `{{dstache}}> templateName}}` or referenced in your JavaScript with `Template.templateName`. In this tutorial, we won't be using this feature of Meteor because we will be defining all of our view components with React.

### Define view components with React

In React, view components are subclasses of `React.Component` (which we import with `import { Component } from 'react';`). Your component can have any methods you like, but there are several methods such as `render` that have special functions. Components can also receive data from their parents through attributes called `props`. We'll go over some of the more common features of React in this tutorial; you can also check out [Facebook's React tutorial](https://facebook.github.io/react/docs/tutorial.html).

### Return markup from the render method with JSX

The most important method in every React component is `render()`, which is called by React to get a description of the HTML that this component should display. The HTML content is written using a JavaScript extension called JSX, which kind of looks like writing HTML inside your JavaScript. You can see some obvious differences already: in JSX, you use the `className` attribute instead of `class`. An important thing to know about JSX is that it isn't a templating language like Spacebars or Angular - it actually compiles directly to regular JavaScript. Read more about JSX [in the React docs](https://facebook.github.io/react/docs/jsx-in-depth.html).

JSX is supported by the `ecmascript` Atmosphere package, which is included in all new Meteor apps by default.

{{> DiffBox tutorialName="simple-todos-react" step="2.6"}}

Now that you've added the CSS, the app should look a lot nicer. Check in your browser to see that the new styles have loaded.

{{/template}}
25 changes: 25 additions & 0 deletions content/vue/step03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{#template name="vue-step03"}}

# Storing tasks in a collection

{{> step03CollectionsIntro tutorialName="simple-todos-react"}}

### Using data from a collection inside a React component

To use data from a Meteor collection inside a React component, we can use an Atmosphere package `react-meteor-data` which allows us to create a "data container" to feed Meteor's reactive data into React's component hierarchy.

```bash
meteor add react-meteor-data
```

To use `react-meteor-data`, we need to wrap our component in a *container* using the `withTracker` Higher Order Component:

{{> DiffBox step="3.4" tutorialName="simple-todos-react"}}

The wrapped `App` component fetches tasks from the `Tasks` collection and supplies them to the underlying `App` component it wraps as the `tasks` prop. It does this in a reactive way, so that when the contents of the database change, the `App` re-renders, as we'll soon see!

When you make these changes to the code, you'll notice that the tasks that used to be in the todo list have disappeared. That's because our database is currently empty — we need to insert some tasks!

{{> step03InsertingTasksFromConsole}}

{{/template}}
42 changes: 42 additions & 0 deletions content/vue/step04.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{{#template name="vue-step04"}}

# Adding tasks with a form

In this step, we'll add an input field for users to add tasks to the list.

First, let's add a form to our `App` component:

{{> DiffBox step="4.1" tutorialName="simple-todos-react"}}

> Tip: You can add comments to your JSX code by wrapping them in `{/* ... */}`

You can see that the `form` element has an `onSubmit` attribute that references a method on the component called `handleSubmit`. In React, this is how you listen to browser events, like the submit event on the form. The `input` element has a `ref` property which will let us easily access this element later.

Let's add a `handleSubmit` method to our `App` component:

{{> DiffBox step="4.2" tutorialName="simple-todos-react"}}

Now your app has a new input field. To add a task, just type into the input field and hit enter. If you open a new browser window and open the app again, you'll see that the list is automatically synchronized between all clients.

### Listening for events in React

As you can see, in React you handle DOM events by directly referencing a method on the component. Inside the event handler, you can reference elements from the component by giving them a `ref` property and using `ReactDOM.findDOMNode`. Read more about the different kinds of events React supports, and how the event system works, in the [React docs](https://facebook.github.io/react/docs/events.html).

### Inserting into a collection

Inside the event handler, we are adding a task to the `tasks` collection by calling `Tasks.insert()`. We can assign any properties to the task object, such as the time created, since we don't ever have to define a schema for the collection.

Being able to insert anything into the database from the client isn't very secure, but it's okay for now. In step 10 we'll learn how we can make our app secure and restrict how data is inserted into the database.

### Sorting our tasks

Currently, our code displays all new tasks at the bottom of the list. That's not very good for a task list, because we want to see the newest tasks first.

We can solve this by sorting the results using the `createdAt` field that is automatically added by our new code. Just add a sort option to the `find` call inside the data container wrapping the `App` component:

{{> DiffBox step="4.3" tutorialName="simple-todos-react"}}

Let's go back to the browser and make sure this worked: any new tasks that you add should appear at the top of the list, rather than at the bottom.

In the next step, we'll add some very important todo list features: checking off and deleting tasks.
{{/template}}
23 changes: 23 additions & 0 deletions content/vue/step05.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{#template name="vue-step05"}}

# Checking off and deleting tasks

Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them.

Let's add two new elements to our `task` component, a checkbox and a delete button, with event handlers for both:

{{> DiffBox step="5.1" tutorialName="simple-todos-react"}}

### Update

In the code above, we call `Tasks.update` to check off a task.

The `update` function on a collection takes two arguments. The first is a selector that identifies a subset of the collection, and the second is an update parameter that specifies what should be done to the matched objects.

In this case, the selector is just the `_id` of the relevant task. The update parameter uses `$set` to toggle the `checked` field, which will represent whether the task has been completed.

### Remove

The code from above uses `Tasks.remove` to delete a task. The `remove` function takes one argument, a selector that determines which item to remove from the collection.

{{/template}}
33 changes: 33 additions & 0 deletions content/vue/step07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{{#template name="vue-step07"}}

# Storing temporary UI data in component state

In this step, we'll add a client-side data filtering feature to our app, so that users can check a box to only see incomplete tasks. We're going to learn how to use React's component state to store temporary information that is only used on the client.

First, we need to add a checkbox to our `App` component:

{{> DiffBox step="7.1" tutorialName="simple-todos-react"}}

You can see that it reads from `this.state.hideCompleted`. React components have a special field called `state` where you can store encapsulated component data. We'll need to initialize the value of `this.state.hideCompleted` in the component's constructor:

{{> DiffBox step="7.2" tutorialName="simple-todos-react"}}

We can update `this.state` from an event handler by calling `this.setState`, which will update the state property asynchronously and then cause the component to re-render:

{{> DiffBox step="7.3" tutorialName="simple-todos-react"}}

Now, we need to update our `renderTasks` function to filter out completed tasks when `this.state.hideCompleted` is true:

{{> DiffBox step="7.4" tutorialName="simple-todos-react"}}

Now if you check the box, the task list will only show tasks that haven't been completed.

### One more feature: Showing a count of incomplete tasks

Now that we have written a query that filters out completed tasks, we can use the same query to display a count of the tasks that haven't been checked off. To do this we need to fetch a count in our data container and add a line to our `render` method. Since we already have the data in the client-side collection, adding this extra count doesn't involve asking the server for anything.

{{> DiffBox step="7.5" tutorialName="simple-todos-react"}}

{{> DiffBox step="7.6" tutorialName="simple-todos-react"}}

{{/template}}
74 changes: 74 additions & 0 deletions content/vue/step08.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{{#template name="vue-step08"}}

# Adding user accounts

Meteor comes with an accounts system and a drop-in login user interface that lets you add multi-user functionality to your app in minutes.

> Currently, this UI component uses Blaze, Meteor's default UI engine. In the future, there might also be a React-specific component for this.

To enable the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:

```bash
meteor add accounts-ui accounts-password
```

### Wrapping a Blaze component in React

To use the Blaze UI component from the `accounts-ui` package, we need to wrap it in a React component. To do so, let's create a new component called `AccountsUIWrapper` in a new file:

{{> DiffBox step="8.2" tutorialName="simple-todos-react"}}

Let's include the component we just defined inside App:

{{> DiffBox step="8.3" tutorialName="simple-todos-react"}}

Then, add the following code to configure the accounts UI to use usernames instead of email addresses:

{{> DiffBox step="8.4" tutorialName="simple-todos-react"}}

We also need to import that configuration code in our client side entrypoint:

{{> DiffBox step="8.5" tutorialName="simple-todos-react"}}

### Adding user-related functionality

Now users can create accounts and log into your app! This is very nice, but logging in and out isn't very useful yet. Let's add two features:

1. Only display the new task input field to logged in users
2. Show which user created each task

To do this, we will add two new fields to the `tasks` collection:

1. `owner` - the `_id` of the user that created the task.
2. `username` - the `username` of the user that created the task. We will save the username directly in the task object so that we don't have to look up the user every time we display the task.

First, let's add some code to save these fields into the `handleSubmit` event handler:

{{> DiffBox step="8.6" tutorialName="simple-todos-react"}}

Modify the data container to get information about the currently logged in user:

{{> DiffBox step="8.7" tutorialName="simple-todos-react"}}

Then, in our render method, add a conditional statement to only show the form when there is a logged in user:

{{> DiffBox step="8.8" tutorialName="simple-todos-react"}}

Finally, add a statement to display the `username` field on each task right before the text:

{{> DiffBox step="8.9" tutorialName="simple-todos-react"}}

In your browser, add some tasks and notice that your username shows up. Old tasks that we added before this step won't have usernames attached; you can just delete them.

Now, users can log in and we can track which user each task belongs to. Let's look at some of the concepts we just discovered in more detail.

### Automatic accounts UI

If our app has the `accounts-ui` package, all we have to do to add a login dropdown is render the included UI component. This dropdown detects which login methods have been added to the app and displays the appropriate controls. In our case, the only enabled login method is `accounts-password`, so the dropdown displays a password field. If you are adventurous, you can add the `accounts-facebook` package to enable Facebook login in your app - the Facebook button will automatically appear in the dropdown.

### Getting information about the logged-in user

In your data container, you can use `Meteor.user()` to check if a user is logged in and get information about them. For example, `Meteor.user().username` contains the logged in user's username. You can also use `Meteor.userId()` to just get the current user's `_id`.

In the next step, we will learn how to make our app more secure by doing data validation on the server.
{{/template}}
Loading