├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── content
├── .DS_Store
├── angular
│ ├── metadata.js
│ ├── step02.md
│ ├── step03.md
│ ├── step04.md
│ ├── step05.md
│ ├── step06.md
│ ├── step07.md
│ ├── step08.md
│ ├── step09.md
│ ├── step10.md
│ ├── step11.md
│ └── step12.md
├── blaze
│ ├── metadata.js
│ ├── step02.md
│ ├── step03.md
│ ├── step04.md
│ ├── step05.md
│ ├── step07.md
│ ├── step08.md
│ ├── step09.md
│ ├── step10.md
│ ├── step11.md
│ └── step12.md
├── shared
│ ├── adding-css.js
│ ├── adding-css.md
│ ├── explanations.md
│ ├── nextSteps.md
│ ├── step01.md
│ └── step06.md
├── step00.html
├── svelte
│ ├── metadata.js
│ ├── step02.md
│ ├── step03.md
│ ├── step04.md
│ ├── step05.md
│ ├── step07.md
│ ├── step08.md
│ ├── step09.md
│ ├── step10.md
│ ├── step11.md
│ └── step12.md
└── vue
│ ├── metadata.js
│ ├── step02.md
│ ├── step03.md
│ ├── step04.md
│ ├── step05.md
│ ├── step07.md
│ ├── step08.md
│ ├── step09.md
│ ├── step10.md
│ ├── step11.md
│ └── step12.md
├── generated
├── angular.multi.patch
├── blaze.multi.patch
├── svelte.multi.patch
└── vue.multi.patch
├── package-lock.json
├── package.js
└── scripts
└── process-repo.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "repos/blaze"]
2 | path = repos/blaze
3 | url = git@github.com:meteor/simple-todos.git
4 | [submodule "repos/angular"]
5 | path = repos/angular
6 | url = git@github.com:meteor/simple-todos-angular.git
7 | [submodule "repos/vue"]
8 | path = repos/vue
9 | url = git@github.com:meteor/simple-todos-vue.git
10 | [submodule "repos/svelte"]
11 | path = repos/svelte
12 | url = git@github.com:meteor/simple-todos-svelte.git
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Meteor Development Group
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | We no longer use this repository, we have now different repos for each technology:
2 |
3 | - https://github.com/meteor/react-tutorial
4 | - https://github.com/meteor/blaze-tutorial
5 | - https://github.com/meteor/vue-tutorial
6 | - https://github.com/meteor/svelte-tutorial
7 | - https://github.com/meteor/angular-tutorial
8 |
9 | # Official Meteor tutorials
10 |
11 | This repository contains the content and view code for the official Meteor tutorials at [meteor.com](https://www.meteor.com/tutorials/blaze/creating-an-app).
12 |
13 | Feel free to submit a pull request to improve the content!
14 |
15 | ### Tutorial content
16 |
17 | 1. [Blaze tutorial](https://www.meteor.com/tutorials/blaze/creating-an-app): [`/content/blaze`](https://github.com/meteor/tutorials/tree/master/content/blaze)
18 | 2. [Angular tutorial](https://www.meteor.com/tutorials/angular/creating-an-app): [`/content/angular`](https://github.com/meteor/tutorials/tree/master/content/angular)
19 | 3. [React tutorial - NEW FORMAT](https://react-tutorial.meteor.com): [`/content/react`](https://github.com/meteor/react-tutorial/)
20 | 4. [Vue tutorial](https://www.meteor.com/tutorials/vue/creating-an-app): [`/content/vue`](https://github.com/meteor/tutorials/tree/master/content/vue)
21 | 5. [Svelte tutorial](https://www.meteor.com/tutorials/svelte/creating-an-app): [`/content/svelte`](https://github.com/meteor/tutorials/tree/master/content/svelte)
22 |
23 | ### Tutorial step-by-step repositories
24 |
25 | We also maintain all of the tutorials as step-by-step git repositories here:
26 |
27 | 1. [Blaze](https://github.com/meteor/simple-todos)
28 | 2. [Angular](https://github.com/meteor/simple-todos-angular)
29 | 3. [React - NEW FORMAT](https://github.com/meteor/react-tutorial/)
30 | 4. [Vue](https://github.com/meteor/simple-todos-vue)
31 | 5. [Svelte](https://github.com/meteor/simple-todos-svelte)
32 |
33 | ### Tutorial viewer
34 |
35 | If you are editing the tutorials, use this simple app to view them: https://github.com/meteor/tutorial-viewer
36 |
37 | ## Tutorial workflow
38 |
39 | ### Editing the prose
40 |
41 | Just edit the markdown files in `/content/`.
42 |
43 | ### Editing code snippets
44 |
45 | The code snippets are generated from the step-by-step git repositories which are git submodules in `/repos`. Each code snippet is its own commit. Commit messages follow the following format:
46 |
47 | ```
48 | Step 3.1: Add some feature
49 | ```
50 |
51 | You might also want to make sure that all of your files end with a newline so that you don't get an annoying "No newline at end of file" diff.
52 |
53 | After using `git rebase -i --root` to massage the repository into the desired state, run the script to update the generated files:
54 |
55 | ```sh
56 | ./scripts/process-repo.rb blaze
57 | ```
58 |
59 | The commit with this message can then be included in the content with the following code snippet:
60 |
61 | ```html
62 | {{> DiffBox step="3.1" tutorialName="simple-todos"}}
63 | ```
64 |
65 | You should replace `simple-todos` with the correct tutorial name (defined by calling `DiffBox.registerTutorial`).
66 |
67 | You're done! Make sure to commit the changes to all of the generated files.
68 |
69 | ## Repository layout
70 |
71 | This repository is a Meteor package; it's currently not published, but you can clone it and use it as a local package in an app.
72 |
73 | The different parts of the repository have quite different responsibilities, but they are somewhat tightly coupled so it doesn't make sense to split them into separate packages at this point.
74 |
75 | 3. `/content/` The actual tutorial prose content, in Markdown format.
76 | 4. `/generated/` (don't edit manually) This directory contains Git patch files generated from the step-by-step repos.
77 | 5. `/repos/` This directory contains git submodules of all three step-by-step tutorial repositories.
78 | 6. `/scripts/` This contains a script that is used to update `/generated/` from the repositories in `/repos/`.
79 |
80 |
81 | ## Updating Sub Modules
82 |
83 | 1. `git submodule update --init --recursive`
84 | 2. `meteor`
85 |
--------------------------------------------------------------------------------
/content/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meteor/tutorials/3fd1f6917fb5dcece6255517ecd1d0b29f12cc48/content/.DS_Store
--------------------------------------------------------------------------------
/content/angular/metadata.js:
--------------------------------------------------------------------------------
1 | if (Meteor.isClient) {
2 | DiffBox.registerTutorial("simple-todos-angular", {
3 | gitHubRepoName: "meteor/simple-todos-angular",
4 | patchFilename: "generated/angular.multi.patch"
5 | });
6 | }
7 |
8 | TutorialRegistry.registerTutorial("angular", {
9 | title: "Simple Todos Angular",
10 | subtitle: "Learn how to use Meteor and Angular together",
11 | tutorialSourceLink: "github.com/meteor/tutorials/content/angular",
12 | steps: [
13 | {
14 | title: 'Creating an app',
15 | slug: "creating-an-app",
16 | template: 'sharedStep01'
17 | },
18 | {
19 | title: 'Templates',
20 | slug: "templates",
21 | template: 'angular-step02'
22 | },
23 | {
24 | title: 'Collections',
25 | slug: "collections",
26 | template: 'angular-step03'
27 | },
28 | {
29 | title: 'Forms and events',
30 | slug: "forms-and-events",
31 | template: 'angular-step04'
32 | },
33 | {
34 | title: 'Update and remove',
35 | slug: "update-and-remove",
36 | template: 'angular-step05'
37 | },
38 | {
39 | title: 'Running on mobile',
40 | slug: "running-on-mobile",
41 | template: 'angular-step06'
42 | },
43 | {
44 | title: 'Filtering Collections',
45 | slug: "filtering-collections",
46 | template: 'angular-step07'
47 | },
48 | {
49 | title: 'Adding user accounts',
50 | slug: "adding-user-accounts",
51 | template: 'angular-step08'
52 | },
53 | {
54 | title: 'Security with methods',
55 | slug: "security-with-methods",
56 | template: 'angular-step09'
57 | },
58 | {
59 | title: 'Publish and subscribe',
60 | slug: "publish-and-subscribe",
61 | template: 'angular-step10'
62 | },
63 | {
64 | title: 'Testing',
65 | slug: "testing",
66 | template: 'angular-step11'
67 | },
68 | {
69 | title: 'Next steps',
70 | slug: "next-steps",
71 | template: 'angular-step12'
72 | }
73 | ]
74 | });
75 |
--------------------------------------------------------------------------------
/content/angular/step02.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step02"}}
2 | # Defining views with templates
3 |
4 | To use Angular in our app, we first need to remove the default UI package of Meteor, called `Blaze`.
5 |
6 | We remove it by running:
7 |
8 | meteor remove blaze-html-templates
9 |
10 | Now we need to replace it with UI package for angular:
11 |
12 | meteor add angular-templates
13 |
14 | To start working with [angular-meteor](http://angular-meteor.com/), let's add some NPM packages.
15 |
16 | meteor npm install --save angular angular-meteor
17 |
18 | > 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.
19 |
20 | To start working on our todos list app, let's replace the code of the default starter app with the code below. Then we'll talk about what it does.
21 |
22 | {{> DiffBox tutorialName="simple-todos-angular" step="2.2"}}
23 |
24 | {{> DiffBox tutorialName="simple-todos-angular" step="2.3"}}
25 |
26 | 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.
27 |
28 | After creating the `imports` directory, we will create two new files inside it.
29 |
30 | A template for the todosList component:
31 |
32 | {{> DiffBox tutorialName="simple-todos-angular" step="2.4"}}
33 |
34 | And some functionality:
35 |
36 | {{> DiffBox tutorialName="simple-todos-angular" step="2.5"}}
37 |
38 | We can now implement it into the application.
39 |
40 | First, we have to put component into a template:
41 |
42 | {{> DiffBox tutorialName="simple-todos-angular" step="2.6"}}
43 |
44 | Then add module to the application:
45 |
46 | {{> DiffBox tutorialName="simple-todos-angular" step="2.7"}}
47 |
48 | 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.
49 |
50 | In our browser, the app should look pretty much like this:
51 |
52 | > #### Todo List
53 | > - This is task 1
54 | > - This is task 2
55 | > - This is task 3
56 |
57 | Now let's find out what all these bits of code are doing!
58 |
59 | ### HTML files in Meteor define templates
60 |
61 | Meteor parses all of the regular .HTML files in your app folder and identifies three top-level tags: **<head>**, **<body>**, and **<template>**.
62 |
63 | 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.
64 |
65 | The [angular-meteor package](http://angular-meteor.com/) parses all of the `html` files in your app folder and puts them in Angular's template cache with the id of their full path.
66 |
67 | So, for example, when a file named `my-angular-template.html` is placed in the `client` folder, it will be available for `ng-include` or `ui-router` with the name `client/my-angular-template.html`.
68 |
69 | ### Adding logic and data to templates
70 |
71 | All of the code in your `html` files is compiled with Angular. Angular binds the data into our templates just like any other Angular app.
72 |
73 | In the next step, we will see how we can use the $meteor service to bind our scope data to a database collection.
74 |
75 | {{> DiffBox tutorialName="simple-todos-angular" step="2.8"}}
76 |
77 | {{/template}}
78 |
--------------------------------------------------------------------------------
/content/angular/step03.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step03"}}
2 |
3 | # Storing tasks in a collection
4 |
5 | {{> step03CollectionsIntro tutorialName="simple-todos"}}
6 |
7 | Let's update our client-side JavaScript code to get our tasks from a collection instead of a static array:
8 |
9 | {{> DiffBox tutorialName="simple-todos-angular" step="3.3"}}
10 |
11 | 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!
12 |
13 | {{> step03InsertingTasksFromConsole}}
14 |
15 | {{/template}}
16 |
--------------------------------------------------------------------------------
/content/angular/step04.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step04"}}
2 |
3 | # Adding tasks with a form
4 |
5 | In this step, we'll add an input field for users to add tasks to the list.
6 |
7 | First, let's add a form to our HTML:
8 |
9 | {{> DiffBox tutorialName="simple-todos-angular" step="4.1"}}
10 |
11 | Here's the JavaScript code we need to add to listen to the `submit` event on the form:
12 |
13 | {{> DiffBox tutorialName="simple-todos-angular" step="4.2"}}
14 |
15 | 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.
16 |
17 | ### Attaching events to templates
18 |
19 | As you can see, this is just a regular Angular application.
20 |
21 | In our case above, we are listening to the `submit` event on our form to call the `addTask` controller function.
22 |
23 | ### Inserting into a collection
24 |
25 | Inside our controller function, we are adding a task to the `tasks` collection by simply 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.
26 |
27 | Being able to insert anything into the database from the client isn't very secure, but it's okay for now. In step 9 we'll learn how we can make our app secure and restrict how data is inserted into the database.
28 |
29 | ### Sorting our tasks
30 |
31 | 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.
32 |
33 | We can solve this by sorting the results using the `createdAt` field that is automatically added by our new code.
34 | Until now you probably used Angular sort filter to do so. you can still use that here, but we are going to use a different method because it is better for real world use cases.
35 |
36 | Replace the `Tasks` collection variable with a function inside our `tasks` helper.
37 | The function will return the result of calling the `find` function with the `sort` parameter on our `Tasks` collection, like that:
38 |
39 | {{> DiffBox tutorialName="simple-todos-angular" step="4.3"}}
40 |
41 | To better understand the difference between using the sort filter and the collection options, check out the advanced tutorial about [search, sort and pagination](https://angular-meteor.com/tutorials/socially/angular1/search-sort-pagination-and-reactive-vars).
42 |
43 | In the next step, we'll add some very important todo list functions: checking off and deleting tasks.
44 | {{/template}}
45 |
--------------------------------------------------------------------------------
/content/angular/step05.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step05"}}
2 |
3 | # Checking off and deleting tasks
4 |
5 | Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them.
6 |
7 | Let's add two elements to our `todosList` component, a checkbox and a delete button:
8 |
9 | {{> DiffBox tutorialName="simple-todos-angular" step="5.1"}}
10 |
11 | {{> DiffBox tutorialName="simple-todos-angular" step="5.2"}}
12 |
13 | ### Update
14 |
15 | As you can see we added two directives.
16 |
17 | To watch `checked` state of each task.
18 |
19 | ```html
20 | ng-checked="task.checked"
21 | ```
22 |
23 | And to change current state by calling `setChecked` method of the controller.
24 |
25 | ```html
26 | ng-click="$ctrl.setChecked(task)"
27 | ```
28 |
29 | ### Delete
30 |
31 | We can delete task by clicking `removeTask` method.
32 |
33 | ### Classes
34 |
35 | If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them.
36 |
37 | Here we bind the checked state of a task to a class with `ng-class`:
38 |
39 | ```html
40 |
41 | ```
42 |
43 | With this code, if the `checked` property of a task is `true`, the `checked` class is added to our list item. Using this class, we can make checked-off tasks look different in our CSS.
44 | {{/template}}
45 |
--------------------------------------------------------------------------------
/content/angular/step06.md:
--------------------------------------------------------------------------------
1 | {{#template name="angularSpecialPart06"}}
2 | ### Setting up Angular for mobile
3 |
4 | Angular needs the main document to be ready so it can bootstrap, but different devices have different events for `ready`.
5 |
6 | To solve this, we need to change the way we bootstrap our Angular app.
7 | Remove the current bootstrap by removing `ng-app` from the `` tag:
8 |
9 | {{> DiffBox tutorialName="simple-todos-angular" step="6.1"}}
10 |
11 | Then add the following code right after `Meteor.isClient`:
12 |
13 | {{> DiffBox tutorialName="simple-todos-angular" step="6.2"}}
14 |
15 | {{/template}}
16 |
17 | {{#template name="angular-step06"}}
18 | {{> sharedStep06 specialContent="angularSpecialPart06"}}
19 | {{/template}}
20 |
--------------------------------------------------------------------------------
/content/angular/step07.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step07"}}
2 |
3 | # Filtering collections
4 |
5 | In this step, we'll add a client-side data filtering feature to our app, so that users can check a box to see only incomplete tasks.
6 |
7 | We're going to learn how to use Mongo's filtering API.
8 |
9 | First, we need to add a checkbox to our HTML:
10 |
11 | {{> DiffBox tutorialName="simple-todos-angular" step="7.1"}}
12 |
13 | This checkbox binds to the controller's `hideCompleted` variable.
14 |
15 | Now, we need to update our `tasks` query each time `hideCompleted` changes.
16 |
17 | ### Filtering collection syntax
18 |
19 | The query that returns all tasks (the current query looks like that:
20 |
21 | ```js
22 | Tasks.find({}, { sort: { createdAt: -1 } })
23 | ```
24 |
25 | and the query to return only the not completed todos looks like that:
26 |
27 | ```js
28 | Tasks.find({ checked: {$ne: true} }, { sort: { createdAt: -1 } })
29 | ```
30 |
31 | ### Add reactivity
32 |
33 | We somehow want to update the query every time `hideComplete` changes.
34 |
35 | Let's implement some reactivity into `tasks` helper:
36 |
37 | {{> DiffBox tutorialName="simple-todos-angular" step="7.2"}}
38 |
39 | As you can see, we used `getReactively()` method. You can read more about it in the following chapter.
40 |
41 | ### Connecting Angular bindings to Meteor's reactivity
42 |
43 | To make Meteor understand Angular bindings and the other way around, we use [$scope.getReactively](https://angular-meteor.com/api/angular-meteor/1.3.11/get-reactively) function that turns Angular
44 | scope variables into [Meteor reactive variables](http://docs.meteor.com/#/full/reactivevar_pkg).
45 |
46 | Now if you check the box, the task list will only show tasks that haven't been completed.
47 |
48 | > To learn more about the [getReactively](https://angular-meteor.com/api/angular-meteor/1.3.11/get-reactively) feature
49 | > you can try the [advanced tutorial](http://angular-meteor.com/tutorial/step_12).
50 |
51 | ### One more feature: Showing a count of incomplete tasks
52 |
53 | 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 add a scope function and change one line of the HTML.
54 |
55 | {{> DiffBox tutorialName="simple-todos-angular" step="7.3"}}
56 |
57 | {{> DiffBox tutorialName="simple-todos-angular" step="7.4"}}
58 |
59 | {{/template}}
60 |
--------------------------------------------------------------------------------
/content/angular/step08.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step08"}}
2 |
3 | # Adding user accounts
4 |
5 | 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.
6 |
7 | To enable the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:
8 |
9 | ```bash
10 | meteor add accounts-password dotansimha:accounts-ui-angular
11 | ```
12 |
13 | `accounts-password` is a package that includes all the logic for password based authentication.
14 |
15 | `dotansimha:accounts-ui-angular` includes the `` directive that contains all the HTML and CSS we need for user authentication forms.
16 |
17 | Now let's add dependency to `account.ui` module in our module definition:
18 |
19 | {{> DiffBox tutorialName="simple-todos-angular" step="8.2"}}
20 |
21 | In the HTML, right under the checkbox, include the following code to add a login dropdown:
22 |
23 | {{> DiffBox tutorialName="simple-todos-angular" step="8.3"}}
24 |
25 | Then, in your JavaScript, add the following code to configure the accounts UI to use usernames instead of email addresses:
26 |
27 | {{> DiffBox tutorialName="simple-todos-angular" step="8.4"}}
28 |
29 | We'll need to import that configuration from our *client-side JavaScript entrypoint* also:
30 |
31 | {{> DiffBox tutorialName="simple-todos-angular" step="8.5"}}
32 |
33 | 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 functions:
34 |
35 | 1. Only display the new task input field to logged in users
36 | 2. Show which user created each task
37 |
38 | To do this, we will add two new fields to the `tasks` collection:
39 |
40 | 1. `owner` - the `_id` of the user that created the task.
41 | 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.
42 |
43 | First, let's add some code to save these fields into the `addTask` function:
44 |
45 | {{> DiffBox tutorialName="simple-todos-angular" step="8.6"}}
46 |
47 | We want somehow to know about logged in user. We can use helper function! Let's call it `currentUser`:
48 |
49 | {{> DiffBox tutorialName="simple-todos-angular" step="8.7"}}
50 |
51 | Then, in our HTML, add an `ng-show` directive to only show the form when there is a logged in user:
52 |
53 | {{> DiffBox tutorialName="simple-todos-angular" step="8.8"}}
54 |
55 | Finally, add a statement to display the `username` field on each task right before the text:
56 |
57 | {{> DiffBox tutorialName="simple-todos-angular" step="8.9"}}
58 |
59 | 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.
60 |
61 | ### Automatic accounts UI
62 |
63 | If our app has the `accounts-ui` package, all we have to do to add a login dropdown is include the `loginButtons` template with [meteor-include](http://angular-meteor.com/api/meteor-include) directive.
64 | 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.
65 |
66 | ### Getting information about the logged-in user
67 |
68 | You can use `Meteor.userId()` to get the current user's `_id`, or `Meteor.user()` to get the whole user document.
69 |
70 | ### Custom templates
71 |
72 | You can choose not to use the `accounts-ui` package template and create your own Angular login templates.
73 | You can read more about it in the [chapter about angular-material](http://angular-meteor.com/tutorial/step_18) in the advanced tutorial.
74 |
75 | In the next step, we will learn how to make our app more secure by doing all of our data validation on the server instead of the client.
76 | {{/template}}
77 |
--------------------------------------------------------------------------------
/content/angular/step09.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step09"}}
2 |
3 | # Security with methods
4 |
5 | Before this step, any user of the app could edit any part of the database. This might be okay for very small internal apps or demos, but any real application needs to control permissions for its data. In Meteor, the best way to do this is by declaring _methods_. Instead of the client code directly calling `insert`, `update`, and `remove`, it will instead call methods that will check if the user is authorized to complete the action and then make any changes to the database on the client's behalf.
6 |
7 | ### Removing `insecure`
8 |
9 | Every newly created Meteor project has the `insecure` package added by default. This is the package that allows us to edit the database from the client. It's useful when prototyping, but now we are taking off the training wheels. To remove this package, go to your app directory and run:
10 |
11 | ```bash
12 | meteor remove insecure
13 | ```
14 |
15 | If you try to use the app after removing this package, you will notice that none of the inputs or buttons work anymore. This is because all client-side database permissions have been revoked. Now we need to rewrite some parts of our app to use methods.
16 |
17 | ### Defining methods
18 |
19 | First, we need to define some methods. We need one method for each database operation we want to perform on the client. Methods should be defined in code that is executed on the client and the server - we will discuss this a bit later in the section titled _Optimistic UI_.
20 |
21 | {{> DiffBox tutorialName="simple-todos-angular" step="9.2"}}
22 |
23 | Now that we have defined our methods, we need to update the places we were operating on the collection to use the methods instead.
24 |
25 | {{> DiffBox tutorialName="simple-todos-angular" step="9.3"}}
26 |
27 | {{> DiffBox tutorialName="simple-todos-angular" step="9.4"}}
28 |
29 | Now all of our inputs and buttons will start working again. What did we gain from all of this work?
30 |
31 | 1. When we insert tasks into the database, we can now securely verify that the user is logged in, that the `createdAt` field is correct, and that the `owner` and `username` fields are correct and the user isn't impersonating anyone.
32 | 2. We can add extra validation logic to `setChecked` and `removeTask` in later steps when users can make tasks private.
33 | 3. Our client code is now more separated from our database logic. Instead of a lot of stuff happening inside our event handlers, we now have methods that can be called from anywhere.
34 |
35 | {{> step09OptimisticUI}}
36 |
37 | {{/template}}
38 |
--------------------------------------------------------------------------------
/content/angular/step10.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step10"}}
2 |
3 | # Filtering data with publish and subscribe
4 |
5 | Now that we have moved all of our app's sensitive code into methods, we need to learn about the other half of Meteor's security story. Until now, we have worked assuming the entire database is present on the client, meaning if we call `Tasks.find()` we will get every task in the collection. That's not good if users of our application want to store privacy-sensitive data. We need a way of controlling which data Meteor sends to the client-side database.
6 |
7 | Just like with `insecure` in the last step, all new Meteor apps start with the `autopublish` package. Let's remove it and see what happens:
8 |
9 | ```bash
10 | meteor remove autopublish
11 | ```
12 |
13 | When the app refreshes, the task list will be empty. Without the `autopublish` package, we will have to specify explicitly what the server sends to the client. The functions in Meteor that do this are `Meteor.publish` and [$scope.subscribe](http://angular-meteor.com/api/subscribe).
14 |
15 | Let's add them now.
16 |
17 | {{> DiffBox tutorialName="simple-todos-angular" step="10.2"}}
18 |
19 | {{> DiffBox tutorialName="simple-todos-angular" step="10.3"}}
20 |
21 | Once you have added this code, all of the tasks will reappear.
22 |
23 | Calling `Meteor.publish` on the server registers a _publication_ named `"tasks"`. When [$scope.subscribe](http://angular-meteor.com/api/subscribe) is called on the client with the publication name, the client _subscribes_ to all the data from that publication, which in this case is all of the tasks in the database. To truly see the power of the publish/subscribe model, let's implement a feature that allows users to mark tasks as "private" so that no other users can see them.
24 |
25 | ### Implementing private tasks
26 |
27 | First, let's add another property to tasks called "private" and a button for users to mark a task as private. This button should only show up for the owner of a task. It will display the current state of the item.
28 |
29 | {{> DiffBox tutorialName="simple-todos-angular" step="10.4"}}
30 |
31 | {{> DiffBox tutorialName="simple-todos-angular" step="10.5"}}
32 |
33 | We need to modify our JavaScript code in two places:
34 |
35 | {{> DiffBox tutorialName="simple-todos-angular" step="10.6"}}
36 |
37 | {{> DiffBox tutorialName="simple-todos-angular" step="10.7"}}
38 |
39 | ### Selectively publishing tasks based on privacy status
40 |
41 | Now that we have a way of setting which tasks are private, we should modify our
42 | publication function to only send the tasks that a user is authorized to see:
43 |
44 | {{> DiffBox tutorialName="simple-todos-angular" step="10.8"}}
45 |
46 | To test that this functionality works, you can use your browser's private browsing mode to log in as a different user. Put the two windows side by side and mark a task private to confirm that the other user can't see it. Now make it public again and it will reappear!
47 |
48 | ### Extra method security
49 |
50 | In order to finish up our private task feature, we need to add checks to our `deleteTask` and `setChecked` methods to make sure only the task owner can delete or check off a private task:
51 |
52 | {{> DiffBox tutorialName="simple-todos-angular" step="10.9"}}
53 |
54 | > Notice that with this code anyone can delete any public task. With some small modifications to the code, you should be able to make it so that only the owner can delete their tasks.
55 |
56 | We're done with our private task feature! Now our app is secure from attackers trying to view or modify someone's private tasks.
57 |
58 | {{/template}}
59 |
--------------------------------------------------------------------------------
/content/angular/step11.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step11"}}
2 |
3 | # Testing
4 |
5 | Now we've created a few features for our application, let's add a test to ensure that we don't regress and that it works the way we expect.
6 |
7 | We'll write a test that exercises one of our Methods (which form the "write" part of our app's API), and verifies it works correctly.
8 |
9 | To do so, we'll add a [test driver](http://guide.meteor.com/testing.html#test-driver) for the [Mocha](https://mochajs.org) JavaScript test framework:
10 |
11 | ```bash
12 | meteor add practicalmeteor:mocha
13 | ```
14 |
15 | > **New in Meteor 1.7+**: While the `meteor test ...` command does still work as specified below, the release of Meteor 1.7 includes a new feature which allows you to specify the location of the test module in `package.json` with a property named `"testModule"` within the `"meteor"` object, which you can read more about [in the changelog](https://docs.meteor.com/changelog.html#v1720180528). In order to get the expected behavior, such that the `meteor test ...` command only uses files whose names match the format `*.test[s].*` or `*.spec[s].*`, you should either remove the line `"testModule": "tests/main.js"` from your `package.json` file, or change it to an appropriate value, before running `meteor test ...`.
16 |
17 | We can now run our app in "test mode" by calling out a special command and specifying to use the driver (you'll need to stop the regular app from running, or specify an alternate port with `--port XYZ`):
18 |
19 | ```bash
20 | meteor test --driver-package practicalmeteor:mocha
21 | ```
22 |
23 | If you do so, you should see an empty test results page in your browser window.
24 |
25 | Let's add a simple test (that doesn't do anything yet):
26 |
27 | {{> DiffBox tutorialName="simple-todos-angular" step="11.2"}}
28 |
29 | In any test we need to ensure the database is in the state we expect before beginning. We can use Mocha's `beforeEach` construct to do that easily:
30 |
31 | {{> DiffBox tutorialName="simple-todos-angular" step="11.3"}}
32 |
33 | Here we create a single task that's associated with a random `userId` that'll be different for each test run.
34 |
35 | Now we can write the test to call the `task.remove` method "as" that user and verify the task is deleted:
36 |
37 | {{> DiffBox tutorialName="simple-todos-angular" step="11.4"}}
38 |
39 | There's a lot more you can do in a Meteor test! You can read more about it in the Meteor Guide [article on testing](http://guide.meteor.com/testing.html).
40 |
41 | # Testing the component
42 |
43 | We need one thing to start writing tests of our angular component.
44 |
45 | ```bash
46 | meteor npm install --save-dev angular-mocks
47 | ```
48 |
49 | Let's prepare some background for tests:
50 |
51 | {{> DiffBox tutorialName="simple-todos-angular" step="11.6"}}
52 |
53 | Add test to check incomplete tasks count:
54 |
55 | {{> DiffBox tutorialName="simple-todos-angular" step="11.7"}}
56 |
57 | Now we can move to testing our controller:
58 |
59 | {{> DiffBox tutorialName="simple-todos-angular" step="11.8"}}
60 |
61 | As you remember, we use meteor method to insert new tasks. Let's check if it works:
62 |
63 | {{> DiffBox tutorialName="simple-todos-angular" step="11.9"}}
64 |
65 | It should also reseting form:
66 |
67 | {{> DiffBox tutorialName="simple-todos-angular" step="11.10"}}
68 |
69 | {{/template}}
70 |
--------------------------------------------------------------------------------
/content/angular/step12.md:
--------------------------------------------------------------------------------
1 | {{#template name="angular-step12"}}
2 |
3 | # What's next?
4 |
5 | Congratulations on your newly built Meteor app!
6 |
7 | Your app currently supports collaborating on a single todo list. To see how you
8 | could add more functionality, check out the [full angular-meteor tutorial](http://angular-meteor.com/).
9 |
10 | {{> step12NextSteps}}
11 |
12 | {{/template}}
13 |
--------------------------------------------------------------------------------
/content/blaze/metadata.js:
--------------------------------------------------------------------------------
1 | if (Meteor.isClient) {
2 | DiffBox.registerTutorial("simple-todos", {
3 | gitHubRepoName: "meteor/simple-todos",
4 | patchFilename: "generated/blaze.multi.patch"
5 | });
6 | }
7 |
8 | TutorialRegistry.registerTutorial("blaze", {
9 | title: "Simple Todos",
10 | subtitle: "Build a simple todo list app with Meteor",
11 | tutorialSourceLink: "github.com/meteor/tutorials/content/blaze",
12 | steps: [
13 | {
14 | title: 'Creating an app',
15 | slug: "creating-an-app",
16 | template: 'sharedStep01'
17 | },
18 | {
19 | title: 'Templates',
20 | slug: "templates",
21 | template: 'blaze-step02'
22 | },
23 | {
24 | title: 'Collections',
25 | slug: "collections",
26 | template: 'blaze-step03'
27 | },
28 | {
29 | title: 'Forms and events',
30 | slug: "forms-and-events",
31 | template: 'blaze-step04'
32 | },
33 | {
34 | title: 'Update and remove',
35 | slug: "update-and-remove",
36 | template: 'blaze-step05'
37 | },
38 | {
39 | title: 'Running on mobile',
40 | slug: "running-on-mobile",
41 | template: 'sharedStep06'
42 | },
43 | {
44 | title: 'Temporary UI state',
45 | slug: "temporary-ui-state",
46 | template: 'blaze-step07'
47 | },
48 | {
49 | title: 'Adding user accounts',
50 | slug: "adding-user-accounts",
51 | template: 'blaze-step08'
52 | },
53 | {
54 | title: 'Security with methods',
55 | slug: "security-with-methods",
56 | template: 'blaze-step09'
57 | },
58 | {
59 | title: 'Publish and subscribe',
60 | slug: "publish-and-subscribe",
61 | template: 'blaze-step10'
62 | },
63 | {
64 | title: 'Testing',
65 | slug: "testing",
66 | template: 'blaze-step11'
67 | },
68 | {
69 | title: 'Next steps',
70 | slug: "next-steps",
71 | template: 'blaze-step12'
72 | }
73 | ]
74 | });
75 |
--------------------------------------------------------------------------------
/content/blaze/step02.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step02"}}
2 | # Defining views with templates
3 |
4 | To start working on our todo list app, let's replace the code of the default starter app with the code below. Then we'll talk about what it does.
5 |
6 | First, let's remove the body from our HTML entry-point (leaving just the `` tag):
7 |
8 | {{> DiffBox tutorialName="simple-todos" step="2.1"}}
9 |
10 | Create a new directory with the name `imports` inside `simple-todos` folder. Then we create some new files in the `imports/` directory:
11 |
12 | {{> DiffBox tutorialName="simple-todos" step="2.2"}}
13 |
14 | {{> DiffBox tutorialName="simple-todos" step="2.3"}}
15 |
16 | Inside our front-end JavaScript entry-point file, `client/main.js`, we'll _remove_ the rest of the code and import `imports/ui/body.js`:
17 |
18 | {{> DiffBox tutorialName="simple-todos" step="2.4"}}
19 |
20 | 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.
21 |
22 | In our browser, the app will now look much like this:
23 |
24 | > #### Todo List
25 | > - This is task 1
26 | > - This is task 2
27 | > - This is task 3
28 |
29 | Now let's find out what all these bits of code are doing!
30 |
31 | ### HTML files in Meteor define templates
32 |
33 | Meteor parses HTML files and identifies three top-level tags: **<head>**, **<body>**, and **<template>**.
34 |
35 | 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.
36 |
37 | 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`.
38 |
39 | Also, the `body` section can be referenced in your JavaScript with `Template.body`. Think of it as a special "parent" template, that can include the other child templates.
40 |
41 | ### Adding logic and data to templates
42 |
43 | All of the code in your HTML files is compiled with [Meteor's Spacebars compiler](http://blazejs.org/api/spacebars.html). Spacebars uses statements surrounded by double curly braces such as `{{dstache}}#each}}` and `{{dstache}}#if}}` to let you add logic and data to your views.
44 |
45 | You can pass data into templates from your JavaScript code by defining _helpers_. In the code above, we defined a helper called `tasks` on `Template.body` that returns an array. Inside the body tag of the HTML, we can use `{{dstache}}#each tasks}}` to iterate over the array and insert a `task` template for each value. Inside the `#each` block, we can display the `text` property of each array item using `{{dstache}}text}}`.
46 |
47 | In the next step, we will see how we can use helpers to make our templates display dynamic data from a database collection.
48 |
49 | ### Styling with CSS
50 |
51 | To have a better experience while following the tutorial we suggest you copy-paste the following CSS code into your app:
52 |
53 | {{> DiffBox tutorialName="simple-todos" step="2.5"}}
54 |
55 | {{/template}}
56 |
--------------------------------------------------------------------------------
/content/blaze/step03.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step03"}}
2 |
3 | # Storing tasks in a collection
4 |
5 | {{> step03CollectionsIntro tutorialName="simple-todos"}}
6 |
7 | Let's update our client-side JavaScript code to get our tasks from a collection instead of a static array:
8 |
9 | {{> DiffBox tutorialName="simple-todos" step="3.3"}}
10 |
11 | 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!
12 |
13 | {{> step03InsertingTasksFromConsole}}
14 |
15 | {{/template}}
16 |
--------------------------------------------------------------------------------
/content/blaze/step04.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step04"}}
2 |
3 | # Adding tasks with a form
4 |
5 | In this step, we'll add an input field for users to add tasks to the list.
6 |
7 | First, let's add a form to our HTML:
8 |
9 | {{> DiffBox tutorialName="simple-todos" step="4.1"}}
10 |
11 | Here's the JavaScript code we need to add to listen to the `submit` event on the form:
12 |
13 | {{> DiffBox tutorialName="simple-todos" step="4.2"}}
14 |
15 | 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.
16 |
17 | ### Attaching events to templates
18 |
19 | Event listeners are added to templates in much the same way as helpers are: by calling `Template.templateName.events(...)` with a dictionary. The keys describe the event to listen for, and the values are _event handlers_ that are called when the event happens.
20 |
21 | In our case above, we are listening to the `submit` event on any element that matches the CSS selector `.new-task`. When this event is triggered by the user pressing enter inside the input field, our event handler function is called.
22 |
23 | The event handler gets an argument called `event` that has some information about the event that was triggered. In this case `event.target` is our form element, and we can get the value of our input with `event.target.text.value`. You can see all of the other properties of the `event` object by adding a `console.log(event)` and inspecting the object in your browser console.
24 |
25 | Finally, in the last line of the event handler, we clear the input to prepare for another new task.
26 |
27 | ### Inserting into a collection
28 |
29 | 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.
30 |
31 | 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.
32 |
33 | ### Sorting our tasks
34 |
35 | 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.
36 |
37 | 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 `tasks` helper:
38 |
39 | {{> DiffBox tutorialName="simple-todos" step="4.3"}}
40 |
41 | In the next step, we'll add some very important todo list functions: checking off and deleting tasks.
42 | {{/template}}
43 |
--------------------------------------------------------------------------------
/content/blaze/step05.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step05"}}
2 |
3 | # Checking off and deleting tasks
4 |
5 | Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them.
6 |
7 | Let's work on our `task` template---starting by moving it to its own file, with some new features, a checkbox and a delete button:
8 |
9 | {{> DiffBox tutorialName="simple-todos" step="5.1"}}
10 |
11 | **5.2** Remove the previous `task` template
12 |
13 | Since we've added a new `task` template here, we need to remove the old `task` template we had before. Open the `imports/ui/body.html` file and remove the entire `...` section from the end.
14 |
15 | Now we've added UI elements, but they don't do anything yet! We should add some event handlers:
16 |
17 | {{> DiffBox tutorialName="simple-todos" step="5.3"}}
18 |
19 | The `body` template uses the `task` template, so we need to import it as well:
20 |
21 | {{> DiffBox tutorialName="simple-todos" step="5.4"}}
22 |
23 |
24 | ### Getting data in event handlers
25 |
26 | Inside the event handlers, `this` refers to an individual task object. In a collection, every inserted document has a unique `_id` field that can be used to refer to that specific document. We can get the `_id` of the current task with `this._id`. Once we have the `_id`, we can use `update` and `remove` to modify the relevant task.
27 |
28 | ### Update
29 |
30 | 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.
31 |
32 | 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.
33 |
34 | ### Remove
35 |
36 | The `remove` function takes one argument, a selector that determines which item to remove from the collection.
37 |
38 | ### Using object properties or helpers to add/remove classes
39 |
40 | If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them. This is enabled by the following snippet:
41 |
42 | ```html
43 |
44 | ```
45 |
46 | With this code, if the `checked` property of a task is `true`, the `checked` class is added to our list item. Using this class, we can make checked-off tasks look different in our CSS.
47 | {{/template}}
48 |
--------------------------------------------------------------------------------
/content/blaze/step07.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step07"}}
2 |
3 | # Storing temporary UI state in a Reactive Dictionary
4 |
5 | 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 a [`ReactiveDict`](https://atmospherejs.com/meteor/reactive-dict) to store temporary reactive state on the client. A `ReactiveDict` is like a normal JS object with keys and values, but with built-in reactivity.
6 |
7 | First, we need to add a checkbox to our HTML:
8 |
9 | {{> DiffBox tutorialName="simple-todos" step="7.1"}}
10 |
11 | Now we need to add the `reactive-dict` package:
12 |
13 | ```bash
14 | meteor add reactive-dict
15 | ```
16 |
17 | Then we need to set up a new `ReactiveDict` and attach it to the body template instance (as this is where we'll store the checkbox's state) when it is first created:
18 |
19 | {{> DiffBox tutorialName="simple-todos" step="7.3"}}
20 |
21 | Then, we need an event handler to update the `ReactiveDict` variable when the checkbox
22 | is checked or unchecked. An event handler takes two arguments, the second of which is the same template instance which was `this` in the `onCreated` callback:
23 |
24 | {{> DiffBox tutorialName="simple-todos" step="7.4"}}
25 |
26 | Now, we need to update `Template.body.helpers`. The code below has a new if
27 | block to filter the tasks if the checkbox is checked:
28 |
29 | {{> DiffBox tutorialName="simple-todos" step="7.5"}}
30 |
31 | Now if you check the box, the task list will only show tasks that haven't been completed.
32 |
33 | ### ReactiveDicts are reactive data stores for the client
34 |
35 | Until now, we have stored all of our state in collections, and the view updated automatically when we modified the data inside these collections. This is because Mongo.Collection is recognized by Meteor as a _reactive data source_, meaning Meteor knows when the data inside has changed. `ReactiveDict` is the same way, but is not synced with the server like collections are. This makes a `ReactiveDict` a convenient place to store temporary UI state like the checkbox above. Just like with collections, we don't have to write any extra code for the template to update when the `ReactiveDict` variable changes — just calling `instance.state.get(...)` inside the helper is enough.
36 |
37 | You can read more about patterns for writing components in the [Blaze article](http://guide.meteor.com/blaze.html) of the Meteor Guide.
38 |
39 |
40 | ### One more feature: Showing a count of incomplete tasks
41 |
42 | 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 add a helper and change one line of the HTML.
43 |
44 | {{> DiffBox tutorialName="simple-todos" step="7.6"}}
45 |
46 | {{> DiffBox tutorialName="simple-todos" step="7.7"}}
47 |
48 | {{/template}}
49 |
--------------------------------------------------------------------------------
/content/blaze/step08.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step08"}}
2 |
3 | # Adding user accounts
4 |
5 | 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.
6 |
7 | To enable the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:
8 |
9 | ```bash
10 | meteor add accounts-ui accounts-password
11 | ```
12 |
13 | In the HTML, right under the checkbox, include the following code to add a login dropdown:
14 |
15 | {{> DiffBox tutorialName="simple-todos" step="8.2"}}
16 |
17 | Then, in your JavaScript, add the following code to configure the accounts UI to use usernames instead of email addresses:
18 |
19 | {{> DiffBox tutorialName="simple-todos" step="8.3"}}
20 |
21 | We'll need to import that configuration from our *client-side JavaScript entrypoint* also:
22 |
23 | {{> DiffBox tutorialName="simple-todos" step="8.4"}}
24 |
25 | 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 functions:
26 |
27 | 1. Only display the new task input field to logged in users
28 | 2. Show which user created each task
29 |
30 | To do this, we will add two new fields to the `tasks` collection:
31 |
32 | 1. `owner` - the `_id` of the user that created the task.
33 | 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.
34 |
35 | First, let's add some code to save these fields into the `submit .new-task` event handler:
36 |
37 | {{> DiffBox tutorialName="simple-todos" step="8.5"}}
38 |
39 | Then, in our HTML, add an `#if` block helper to only show the form when there is a logged in user:
40 |
41 | {{> DiffBox tutorialName="simple-todos" step="8.6"}}
42 |
43 | Finally, add a Spacebars statement to display the `username` field on each task right before the text:
44 |
45 | {{> DiffBox tutorialName="simple-todos" step="8.7"}}
46 |
47 | 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.
48 |
49 | ### Automatic accounts UI
50 |
51 | If our app has the `accounts-ui` package, all we have to do to add a login dropdown is include the `loginButtons` template with `{{dstache}}> loginButtons}}`. 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.
52 |
53 | ### Getting information about the logged-in user
54 |
55 | In your HTML, you can use the built-in `{{dstache}}currentUser}}` helper to check if a user is logged in and get information about them. For example, `{{dstache}}currentUser.username}}` will display the logged in user's username.
56 |
57 | In your JavaScript code, you can use `Meteor.userId()` to get the current user's `_id`, or `Meteor.user()` to get the whole user document.
58 |
59 | In the next step, we will learn how to make our app more secure by doing all of our data validation on the server instead of the client.
60 | {{/template}}
61 |
62 | You can read more about using accounts in Meteor in the [Accounts article](http://guide.meteor.com/accounts.html) of the Meteor Guide.
63 |
64 |
--------------------------------------------------------------------------------
/content/blaze/step09.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step09"}}
2 |
3 | # Security with methods
4 |
5 | Before this step, any user of the app could edit any part of the database. This might be okay for very small internal apps or demos, but any real application needs to control permissions for its data. In Meteor, the best way to do this is by declaring _methods_. Instead of the client code directly calling `insert`, `update`, and `remove`, it will instead call methods that will check if the user is authorized to complete the action and then make any changes to the database on the client's behalf.
6 |
7 | ### Removing `insecure`
8 |
9 | Every newly created Meteor project has the `insecure` package added by default. This is the package that allows us to edit the database from the client. It's useful when prototyping, but now we are taking off the training wheels. To remove this package, go to your app directory and run:
10 |
11 | ```bash
12 | meteor remove insecure
13 | ```
14 |
15 | If you try to use the app after removing this package, you will notice that none of the inputs or buttons work anymore. This is because all client-side database permissions have been revoked. Now we need to rewrite some parts of our app to use methods.
16 |
17 | ### Defining methods
18 |
19 | First, we need to define some methods. We need one method for each database operation we want to perform on the client. Methods should be defined in code that is executed on the client and the server - we will discuss this a bit later in the section titled _Optimistic UI_.
20 |
21 | {{> DiffBox tutorialName="simple-todos" step="9.2"}}
22 |
23 | Now that we have defined our methods, we need to update the places we were operating on the collection to use the methods instead:
24 |
25 | {{> DiffBox tutorialName="simple-todos" step="9.3"}}
26 |
27 | We do the same on this file, but also remove the `import` of the `Tasks` collection since it's no longer necessary:
28 |
29 | {{> DiffBox tutorialName="simple-todos" step="9.4"}}
30 |
31 | Now all of our inputs and buttons will start working again. What did we gain from all of this work?
32 |
33 | 1. When we insert tasks into the database, we can now securely verify that the user is logged in, that the `createdAt` field is correct, and that the `owner` and `username` fields are correct and the user isn't impersonating anyone.
34 | 2. We can add extra validation logic to `setChecked` and `remove` in later steps when users can make tasks private.
35 | 3. Our client code is now more separated from our database logic. Instead of a lot of stuff happening inside our event handlers, we now have methods that can be called from anywhere.
36 |
37 | {{> step09OptimisticUI}}
38 |
39 |
40 | {{/template}}
41 |
--------------------------------------------------------------------------------
/content/blaze/step10.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step10"}}
2 |
3 | # Filtering data with publish and subscribe
4 |
5 | Now that we have moved all of our app's sensitive code into methods, we need to learn about the other half of Meteor's security story. Until now, we have worked assuming the entire database is present on the client, meaning if we call `Tasks.find()` we will get every task in the collection. That's not good if users of our application want to store privacy-sensitive data. We need a way of controlling which data Meteor sends to the client-side database.
6 |
7 | Just like with `insecure` in the last step, all new Meteor apps start with the `autopublish` package. Let's remove it and see what happens:
8 |
9 | ```bash
10 | meteor remove autopublish
11 | ```
12 |
13 | When the app refreshes, the task list will be empty. Without the `autopublish` package, we will have to specify explicitly what the server sends to the client. The functions in Meteor that do this are `Meteor.publish` and `Meteor.subscribe`.
14 |
15 | First lets add a publication for all tasks:
16 |
17 | {{> DiffBox tutorialName="simple-todos" step="10.2"}}
18 |
19 | And then let's subscribe to that publication when the `body` template is created:
20 |
21 | {{> DiffBox tutorialName="simple-todos" step="10.3"}}
22 |
23 | Once you have added this code, all of the tasks will reappear.
24 |
25 | Calling `Meteor.publish` on the server registers a _publication_ named `"tasks"`. When `Meteor.subscribe` is called on the client with the publication name, the client _subscribes_ to all the data from that publication, which in this case is all of the tasks in the database. To truly see the power of the publish/subscribe model, let's implement a feature that allows users to mark tasks as "private" so that no other users can see them.
26 |
27 | You can read more about publications in the [Data Loading article](http://guide.meteor.com/data-loading.html) of the Meteor Guide.
28 |
29 | ### Implementing private tasks
30 |
31 | First, let's add another property to tasks called "private" and a button for users to mark a task as private. This button should only show up for the owner of a task. It will display the current state of the item.
32 |
33 | {{> DiffBox tutorialName="simple-todos" step="10.4"}}
34 |
35 | Let's make sure our task has a special class if it is marked private:
36 |
37 | {{> DiffBox tutorialName="simple-todos" step="10.5"}}
38 |
39 | We need to modify our JavaScript code in three places:
40 |
41 | {{> DiffBox tutorialName="simple-todos" step="10.6"}}
42 |
43 | {{> DiffBox tutorialName="simple-todos" step="10.7"}}
44 |
45 | {{> DiffBox tutorialName="simple-todos" step="10.8"}}
46 |
47 | ### Selectively publishing tasks based on privacy status
48 |
49 | Now that we have a way of setting which tasks are private, we should modify our
50 | publication function to only send the tasks that a user is authorized to see:
51 |
52 | {{> DiffBox tutorialName="simple-todos" step="10.9"}}
53 |
54 | To test that this functionality works, you can use your browser's private browsing mode to log in as a different user. Put the two windows side by side and mark a task private to confirm that the other user can't see it. Now make it public again and it will reappear!
55 |
56 | ### Extra method security
57 |
58 | In order to finish up our private task feature, we need to add checks to our `deleteTask` and `setChecked` methods to make sure only the task owner can delete or check off a private task:
59 |
60 | {{> DiffBox tutorialName="simple-todos" step="10.10"}}
61 |
62 | > Notice that with this code anyone can delete any public task. With some small modifications to the code, you should be able to make it so that only the owner can delete their tasks.
63 |
64 | We're done with our private task feature! Now our app is secure from attackers trying to view or modify someone's private tasks.
65 |
66 | {{/template}}
67 |
--------------------------------------------------------------------------------
/content/blaze/step11.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step11"}}
2 |
3 | # Testing
4 |
5 | Now that we've created a few features for our application, let's add a test to ensure that we don't regress and that it works the way we expect.
6 |
7 | We'll write a test that exercises one of our Methods (which form the "write" part of our app's API), and verifies it works correctly.
8 |
9 | To do so, we'll add a [test driver](http://guide.meteor.com/testing.html#test-driver) for the [Mocha](https://mochajs.org) JavaScript test framework, along with a test assertion library:
10 |
11 | ```bash
12 | meteor add meteortesting:mocha
13 | meteor npm install --save-dev chai
14 | ```
15 |
16 | We can now run our app in "test mode" by calling out a special command and specifying to use the driver (you'll need to stop the regular app from running, or specify an alternate port with `--port XYZ`):
17 |
18 | ```bash
19 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
20 | ```
21 |
22 | If you do so, you should see test results for the two tests included by default in the `tests/main.js` file
23 |
24 | Let's add a new file in the `imports/api` folder named `tasks.test.js`. This file will be the home for all tests we make related to testing the applications's `tasks` api.
25 |
26 | Once the file is created we can then add a new test case to the file.
27 |
28 | {{> DiffBox tutorialName="simple-todos" step="11.2"}}
29 |
30 | The `imports/api/tasks.test.js` file will need to be imported in the `tests/main.js` file because the `tests/main.js` file serves as the entry point for the meteor test command
31 |
32 | {{> DiffBox tutorialName="simple-todos" step="11.3"}}
33 |
34 | Now that we are able to run all three of our test cases, we need to continue building the test case in the `imports/api/tasks.test.js` file.
35 |
36 | We first need to ensure the database is in the state we expect before our test case starts to run. To do so we can use Mocha's `beforeEach` construct.
37 |
38 | {{> DiffBox tutorialName="simple-todos" step="11.4"}}
39 |
40 | Here we create a single task that's associated with a random `userId` that'll be different for each test run.
41 |
42 | Now we can have our test case call the `tasks.remove` method "as" that user and verify the task is deleted:
43 |
44 | {{> DiffBox tutorialName="simple-todos" step="11.5"}}
45 |
46 | Running the test command again will allw you to see all three test cases are now passing.
47 |
48 | ```bash
49 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
50 | ```
51 |
52 | There's a lot more you can do in a Meteor test! You can read more about it in the Meteor Guide [article on testing](http://guide.meteor.com/testing.html).
53 |
54 | {{/template}}
55 |
--------------------------------------------------------------------------------
/content/blaze/step12.md:
--------------------------------------------------------------------------------
1 | {{#template name="blaze-step12"}}
2 |
3 | # What's next?
4 |
5 | Congratulations on your newly built Meteor app!
6 |
7 | Your app currently supports collaborating on a single todo list. To see how you
8 | could add more functionality, check out the Todos example — a more
9 | complete app that can handle sharing multiple lists. Also, try Local Market, a
10 | cross-platform customer engagement app that shows off native hardware
11 | functionality and social features.
12 |
13 | ```bash
14 | meteor create --example todos
15 | meteor create --example localmarket
16 | ```
17 |
18 | {{> step12NextSteps}}
19 |
20 | {{/template}}
21 |
--------------------------------------------------------------------------------
/content/shared/adding-css.js:
--------------------------------------------------------------------------------
1 | Template.addingCSS.helpers({
2 | cssHeading (cssFileName) {
3 | return `Replace ${cssFileName} with this code`;
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/content/shared/adding-css.md:
--------------------------------------------------------------------------------
1 | {{#template name="addingCSS"}}
2 |
3 | ### Adding CSS
4 |
5 | Before we go any further, let's make our app look nice by adding some CSS.
6 |
7 | Since this tutorial is focused on working with HTML and JavaScript, just copy all the CSS code below into `{{cssFileName}}`. This is all the CSS code you will need until the end of the tutorial. The app will still work without the CSS, but it will look much nicer if you add it.
8 |
9 | {{#codeBox cssHeading cssFileName}}
10 | ```css
11 | /* CSS declarations go here */
12 | body {
13 | font-family: sans-serif;
14 | background-color: #315481;
15 | background-image: linear-gradient(to bottom, #315481, #918e82 100%);
16 | background-attachment: fixed;
17 |
18 | position: absolute;
19 | top: 0;
20 | bottom: 0;
21 | left: 0;
22 | right: 0;
23 |
24 | padding: 0;
25 | margin: 0;
26 |
27 | font-size: 14px;
28 | }
29 |
30 | .container {
31 | max-width: 600px;
32 | margin: 0 auto;
33 | min-height: 100%;
34 | background: white;
35 | }
36 |
37 | header {
38 | background: #d2edf4;
39 | background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
40 | padding: 20px 15px 15px 15px;
41 | position: relative;
42 | }
43 |
44 | #login-buttons {
45 | display: block;
46 | }
47 |
48 | h1 {
49 | font-size: 1.5em;
50 | margin: 0;
51 | margin-bottom: 10px;
52 | display: inline-block;
53 | margin-right: 1em;
54 | }
55 |
56 | form {
57 | margin-top: 10px;
58 | margin-bottom: -10px;
59 | position: relative;
60 | }
61 |
62 | .new-task input {
63 | box-sizing: border-box;
64 | padding: 10px 0;
65 | background: transparent;
66 | border: none;
67 | width: 100%;
68 | padding-right: 80px;
69 | font-size: 1em;
70 | }
71 |
72 | .new-task input:focus{
73 | outline: 0;
74 | }
75 |
76 | ul {
77 | margin: 0;
78 | padding: 0;
79 | background: white;
80 | }
81 |
82 | .delete {
83 | float: right;
84 | font-weight: bold;
85 | background: none;
86 | font-size: 1em;
87 | border: none;
88 | position: relative;
89 | }
90 |
91 | li {
92 | position: relative;
93 | list-style: none;
94 | padding: 15px;
95 | border-bottom: #eee solid 1px;
96 | }
97 |
98 | li .text {
99 | margin-left: 10px;
100 | }
101 |
102 | li.checked {
103 | color: #888;
104 | }
105 |
106 | li.checked .text {
107 | text-decoration: line-through;
108 | }
109 |
110 | li.private {
111 | background: #eee;
112 | border-color: #ddd;
113 | }
114 |
115 | header .hide-completed {
116 | float: right;
117 | }
118 |
119 | .toggle-private {
120 | margin-left: 5px;
121 | }
122 |
123 | @media (max-width: 600px) {
124 | li {
125 | padding: 12px 15px;
126 | }
127 |
128 | .search {
129 | width: 150px;
130 | clear: both;
131 | }
132 |
133 | .new-task input {
134 | padding-bottom: 5px;
135 | }
136 | }
137 | ```
138 | {{/codeBox}}
139 |
140 | {{/template}}
141 |
--------------------------------------------------------------------------------
/content/shared/explanations.md:
--------------------------------------------------------------------------------
1 | {{#template name="step03CollectionsIntro"}}
2 |
3 | Collections are Meteor's way of storing persistent data. The special thing about collections in Meteor is that they can be accessed from both the server and the client, making it easy to write view logic without having to write a lot of server code. They also update themselves automatically, so a view component backed by a collection will automatically display the most up-to-date data.
4 |
5 | You can read more about collections in the [Collections article](http://guide.meteor.com/collections.html) of the Meteor Guide.
6 |
7 | Creating a new collection is as easy as calling `new Mongo.Collection("my-collection")` in your JavaScript. On the server, this sets up a MongoDB collection called `my-collection`; on the client, it creates a cache connected to the server collection. We'll learn more about the client/server divide in step 12, but for now we can write our code with the assumption that the entire database is present on the client.
8 |
9 | To create the collection, we define a new **`imports/api/tasks.js`** module that creates a Mongo collection and exports it:
10 |
11 | {{> DiffBox tutorialName=tutorialName step="3.1"}}
12 |
13 | Notice that we place this file in a new `imports/api` directory. This is a sensible place to store API-related files for the application. We will start by putting "collections" here and later we will add "publications" that read from them and "methods" that write to them. You can read more about how to structure your code in the [Application Structure article](http://guide.meteor.com/structure.html) of the Meteor Guide.
14 |
15 | We now need to import the `imports/api/tasks.js` module into `server/main.js`:
16 |
17 | {{> DiffBox tutorialName=tutorialName step="3.2"}}
18 |
19 | Importing `imports/api/tasks.js` on the server creates the MongoDB collection and sets up the plumbing to get the data to the client.
20 |
21 | {{/template}}
22 |
23 | {{#template name="step03InsertingTasksFromConsole"}}
24 |
25 | ### Inserting tasks from the server-side database console
26 |
27 | Items inside collections are called _documents_. Let's use the server database console to insert some documents into our collection. While your app is running, in a new terminal tab, go to your app directory and type:
28 |
29 | ```bash
30 | meteor mongo
31 | ```
32 |
33 | This opens a console into your app's local development database. Into the prompt, type:
34 |
35 | ```js
36 | db.tasks.insert({ text: "Hello world!", createdAt: new Date() });
37 | ```
38 |
39 | In your web browser, you will see the UI of your app immediately update to show the new task. You can see that we didn't have to write any code to connect the server-side database to our front-end code—it just happened automatically.
40 |
41 | Insert a few more tasks from the database console with different text. In the next step, we'll see how to add functionality to our app's UI so that we can add tasks without using the database console.
42 |
43 | {{/template}}
44 |
45 | {{#template name="step09OptimisticUI"}}
46 |
47 | ### Optimistic UI
48 |
49 | So why do we want to define our methods on the client and on the server? We do this to enable a feature we call _optimistic UI_.
50 |
51 | When you call a method on the client using `Meteor.call`, two things happen in parallel:
52 |
53 | 1. The client sends a request to the server to run the method in a secure environment, just like an AJAX request would work
54 | 2. A simulation of the method runs directly on the client to attempt to predict the outcome of the server call using the available information
55 |
56 | What this means is that a newly created task actually appears on the screen _before_ the result comes back from the server.
57 |
58 | If the result from the server comes back and is consistent with the simulation on the client, everything remains as is. If the result on the server is different from the result of the simulation on the client, the UI is patched to reflect the actual state of the server.
59 |
60 | You can read more about methods and optimistic UI in the [Methods article](http://guide.meteor.com/methods.html) of the Meteor Guide, and our [blog post about optimistic UI](https://blog.meteor.com/optimistic-ui-with-meteor-67b5a78c3fcf).
61 |
62 | {{/template}}
63 |
--------------------------------------------------------------------------------
/content/shared/nextSteps.md:
--------------------------------------------------------------------------------
1 | {{#template name="step12NextSteps"}}
2 |
3 | Here are some options for where you can go next:
4 |
5 | 1. Read the [Meteor Guide](http://guide.meteor.com) to learn about best practices and useful community packages
6 |
7 | 2. Check out the [complete documentation](https://docs.meteor.com)
8 |
9 | 3. Explore additional Meteor content such as the [Intermediate Meteor](https://www.youtube.com/watch?v=BI8IslJHSag&list=PLLnpHn493BHFYZUSK62aVycgcAouqBt7V) video series
10 |
11 | 4. Run your app on the cloud [today](https://www.meteor.com/hosting). Try Galaxy (Meteor Hosting) for Free! [Sign up](https://www.meteor.com/hosting) for Galaxy Hosting today and get 4 GB free trial for the first 30 days!
12 |
13 | {{/template}}
14 |
--------------------------------------------------------------------------------
/content/shared/step01.md:
--------------------------------------------------------------------------------
1 | {{#template name="sharedStep01"}}
2 |
3 | # Creating your first app
4 |
5 | In this tutorial, we are going to create a simple app to manage a 'to do' list and collaborate with others on those tasks. By the end, you should have a basic understanding of Meteor and its project structure.
6 |
7 | To create the app, open your terminal and type:
8 |
9 | ```bash
10 | meteor create simple-todos --blaze
11 | ```
12 |
13 | This will create a new folder called `simple-todos` with all of the files that a Meteor app needs:
14 |
15 | ```bash
16 | client/main.js # a JavaScript entry point loaded on the client
17 | client/main.html # an HTML file that defines view templates
18 | client/main.css # a CSS file to define your app's styles
19 | server/main.js # a JavaScript entry point loaded on the server
20 | test/main.js # a JavaScript entry point when running tests
21 | package.json # a control file for installing npm packages
22 | package-lock.json # describes the npm dependency tree
23 | node_modules/ # packages installed by npm
24 | .meteor/ # internal Meteor files
25 | .gitignore # a control file for git
26 | ```
27 |
28 | To run the newly created app:
29 |
30 | ```bash
31 | cd simple-todos
32 | meteor
33 | ```
34 |
35 | Open your web browser and go to `http://localhost:3000` to see the app running.
36 |
37 | You can play around with this default app for a bit before we continue. For example, try editing the text in `
` inside `client/main.html` using your favorite text editor. When you save the file, the page in your browser will automatically update with the new content. We call this _hot code push_.
38 |
39 | Now that you have some experience editing the files in your Meteor app, let's start working on a simple todo list application. If you find a bug or error in the tutorial, please file an issue or submit a pull request [on GitHub](https://github.com/meteor/tutorials).
40 | {{/template}}
41 |
--------------------------------------------------------------------------------
/content/shared/step06.md:
--------------------------------------------------------------------------------
1 | {{#template name="sharedStep06"}}
2 |
3 | # Running your app on Android or iOS
4 |
5 | > Currently, Meteor on Windows does not support mobile builds. If you are using Meteor on Windows, you should skip this step.
6 |
7 | So far, we've been building our app and testing only in a web browser, but Meteor has been designed to work across different platforms - your simple todo list website can become an iOS or Android app in just a few commands.
8 |
9 | Meteor makes it easy to set up all of the tools required to build mobile apps, but downloading all of the programs can take a while - for Android the download is about 300MB and for iOS you need to install Xcode which is about 2GB. If you don't want to wait to download these tools, feel free to skip to the next step.
10 |
11 | {{#if specialContent}}
12 | {{> Template.dynamic template=specialContent}}
13 | {{/if}}
14 |
15 | ### Running on an iOS simulator (Mac Only)
16 |
17 | If you have a Mac, you can run your app inside the iOS simulator.
18 |
19 | Go to your app folder and type:
20 |
21 | ```bash
22 | meteor install-sdk ios
23 | ```
24 |
25 | This will run you through the setup necessary to build an iOS app from your project. When you're done, type:
26 |
27 | ```bash
28 | meteor add-platform ios
29 | meteor run ios
30 | ```
31 |
32 | You will see the iOS simulator pop up with your app running inside.
33 |
34 | ### Running on an Android emulator
35 |
36 | In the terminal, go to your app folder and type:
37 |
38 | ```bash
39 | meteor install-sdk android
40 | ```
41 |
42 | This will help you install all of the necessary tools to build an Android app from your project. When you are done installing everything, type:
43 |
44 | ```bash
45 | meteor add-platform android
46 | ```
47 |
48 | After you agree to the license terms, type:
49 |
50 | ```bash
51 | meteor run android
52 | ```
53 |
54 | After some initialization, you will see an Android emulator pop up, running your app inside a native Android wrapper. The emulator can be somewhat slow, so if you want to see what it's really like using your app, you should run it on an actual device.
55 |
56 | ### Running on an Android device
57 |
58 | First, complete all of the steps above to set up the Android tools on your system. Then, make sure you have [USB Debugging enabled on your phone](http://developer.android.com/tools/device.html#developer-device-options) and the phone is plugged into your computer with a USB cable. Also, you must quit the Android emulator before running on a device.
59 |
60 | Then, run the following command:
61 |
62 | ```bash
63 | meteor run android-device
64 | ```
65 |
66 | The app will be built and installed on your device.
67 |
68 | ### Running on an iPhone or iPad (Mac Only; requires Apple developer account)
69 |
70 | If you have an Apple developer account, you can also run your app on an iOS device. Run the following command:
71 |
72 | ```bash
73 | meteor run ios-device
74 | ```
75 |
76 | This will open Xcode with a project for your iOS app. You can use Xcode to then launch the app on any device or simulator that Xcode supports.
77 |
78 | Now that we have seen how easy it is to run our app on mobile, let's get to adding some more features.
79 |
80 | {{/template}}
81 |
--------------------------------------------------------------------------------
/content/step00.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Installing Meteor on OS X, Linux and Windows
4 |
Current version: {{meteorVersion}}
5 |
6 |
7 | {{#markdown}}
8 |
9 | Meteor supports **OS X, Windows, and Linux**, and is simple to install. The command line installer supports Mac OS X
10 | 10.7 (Lion) and above, and Linux on x86 and x86_64 architectures. The Windows installer supports Windows 7, Windows 8.1, Windows 10, Windows Server 2008, and Windows Server 2012. iOS development requires the latest Xcode.
11 |
12 | ### On OS X or Linux?
13 |
14 | Install the latest official Meteor release from
15 | your terminal:
16 |
17 |
22 |
23 | ### On Windows?
24 | Download the official Meteor installer
25 |
26 | ###What Next?
27 |
28 | Now that you've installed Meteor, check out [the tutorial](/tutorials/blaze) that
29 | teaches you how to build a collaborative todo list app while showing you
30 | Meteor's most exciting and useful features. You can also read about [the
31 | design of the Meteor platform](/projects) or check out the
32 | [complete documentation](https://docs.meteor.com).
33 |
34 | {{/markdown}}
35 |
36 |
37 |
38 |
39 |
Get on the mailing list - the latest Meteor news, new releases, critical security alerts, and nothing else.
40 |
41 |
42 | {{> emailForm}}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/content/svelte/metadata.js:
--------------------------------------------------------------------------------
1 | if (Meteor.isClient) {
2 | DiffBox.registerTutorial("simple-todos-svelte", {
3 | gitHubRepoName: "meteor/simple-todos-svelte",
4 | patchFilename: "generated/svelte.multi.patch"
5 | });
6 | }
7 |
8 | TutorialRegistry.registerTutorial("svelte", {
9 | title: "Simple Todos Svelte",
10 | subtitle: "Learn how to use Meteor and Svelte together",
11 | tutorialSourceLink: "github.com/meteor/tutorials/content/svelte",
12 | steps: [
13 | {
14 | title: 'Creating an app',
15 | slug: "creating-an-app",
16 | template: 'sharedStep01'
17 | },
18 | {
19 | title: 'Templates',
20 | slug: "templates",
21 | template: 'svelte-step02'
22 | },
23 | {
24 | title: 'Collections',
25 | slug: "collections",
26 | template: 'svelte-step03'
27 | },
28 | {
29 | title: 'Forms and events',
30 | slug: "forms-and-events",
31 | template: 'svelte-step04'
32 | },
33 | {
34 | title: 'Update and remove',
35 | slug: "update-and-remove",
36 | template: 'svelte-step05'
37 | },
38 | {
39 | title: 'Running on mobile',
40 | slug: "running-on-mobile",
41 | template: 'sharedStep06'
42 | },
43 | {
44 | title: 'Filtering Collections',
45 | slug: "filtering-collections",
46 | template: 'svelte-step07'
47 | },
48 | {
49 | title: 'Adding user accounts',
50 | slug: "adding-user-accounts",
51 | template: 'svelte-step08'
52 | },
53 | {
54 | title: 'Security with methods',
55 | slug: "security-with-methods",
56 | template: 'svelte-step09'
57 | },
58 | {
59 | title: 'Publish and subscribe',
60 | slug: "publish-and-subscribe",
61 | template: 'svelte-step10'
62 | },
63 | {
64 | title: 'Testing',
65 | slug: "testing",
66 | template: 'svelte-step11'
67 | },
68 | {
69 | title: 'Next steps',
70 | slug: "next-steps",
71 | template: 'svelte-step12'
72 | }
73 | ]
74 | });
75 |
--------------------------------------------------------------------------------
/content/svelte/step02.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step02"}}
2 | # In a hurry?
3 |
4 | If you don't have time to follow this detailed tutorial right now, you have two options.
5 |
6 | To skip to the finished application explained by this tutorial, run the following commands:
7 |
8 | ```sh
9 | git clone git@github.com:meteor/simple-todos-svelte.git
10 | cd simple-todos-svelte
11 | meteor npm install
12 | meteor
13 | ```
14 |
15 | If you are an experienced Svelte developer, most of this application will be self-explanatory, though you can always refer back to this tutorial to understand the details.
16 |
17 | # Working with Svelte
18 |
19 | The rest of this tutorial will take you on a step-by-step journey through the details of using Svelte with Meteor.
20 |
21 | ### Install Svelte dependencies
22 |
23 | To start working with Svelte as our view library, we first need to add the relevant npm package.
24 |
25 | Open a new terminal in the same directory as your running app, and run the following commands:
26 |
27 | ```sh
28 | meteor npm install --save svelte
29 | ```
30 |
31 | > 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.
32 |
33 | We also need to add the svelte:compiler Meteor package to allow us to create files with the .svelte extension, which makes it possible to create Svelte components in the single file format, and the rdb:svelte-meteor-data Meteor package which allows us to consume Meteor's reactive data sources inside of our Svelte components.
34 |
35 | ```sh
36 | meteor add svelte:compiler rdb:svelte-meteor-data
37 | ```
38 |
39 | ### Replace `blaze-html-templates` with `static-html`
40 |
41 | By default, new Meteor applications use Blaze as their templating engine, but the application we're building in this tutorial does not need the `blaze-html-templates` Meteor package.
42 |
43 | First remove the `blaze-html-templates` package:
44 |
45 | ```sh
46 | meteor remove blaze-html-templates
47 | ```
48 |
49 | Now, to ensure `.html` files are processed as static HTML, add the `static-html` package:
50 |
51 | ```sh
52 | meteor add static-html
53 | ```
54 |
55 | The `static-html` package is not specifically for building a Svelte application. It simply turns `` and `` fragments found in `.html` files into raw HTML that will be served from the Meteor web server. Later, your Svelte application will render its components into this HTML.
56 |
57 | Note that both `blaze-html-templates` and `static-html` are _Meteor packages_, rather than npm packages, because they need to register _compiler plugins_ that determine how `.html` files are processed. Controlling compilation is one of several key features that make Meteor packages more powerful than npm packages.
58 |
59 | ### Replace the starter code
60 |
61 | To get started, let's replace the code of the default starter app. Then we'll talk about what it does.
62 |
63 | First, replace the content of the **`client/main.html`** file:
64 |
65 | {{> DiffBox tutorialName="simple-todos-svelte" step="2.2"}}
66 |
67 | Second, replace the contents of the **`client/main.js`** module:
68 |
69 | {{> DiffBox tutorialName="simple-todos-svelte" step="2.3"}}
70 |
71 | Now we need to create a new directory called `imports`, with a directory called `ui` inside of it. There will soon be other directories besides `ui` within `imports`, but we'll start with `ui`.
72 |
73 | > Note: in previous versions of Meteor, the `imports` directory was special because files outside the `imports` directory were loaded automatically when the application started, whereas files inside the `imports` directory were only loaded when imported using an `import` declaration or a `require` statement. As of Meteor 1.7, the entry point for both client and server JavaScript is determined by the `meteor.mainModule` section in `package.json`. In other words, as far as JavaScript code is concerned, the entire application now behaves as if it was inside an `imports` directory, so you don't need to worry as much about the `imports` directory now.
74 |
75 | 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.
76 |
77 | To continue converting your app to use Svelte, copy the following code into **`imports/ui/App.svelte`**:
78 |
79 | {{> DiffBox tutorialName="simple-todos-svelte" step="2.4"}}
80 |
81 | Now copy the following code into **`imports/ui/Task.svelte`**:
82 |
83 | {{> DiffBox tutorialName="simple-todos-svelte" step="2.5"}}
84 |
85 | We just added three things to our app:
86 |
87 | 1. An `App` Svelte component in `imports/ui/App.svelte`
88 | 2. A `Task` Svelte component in `imports/ui/Task.svelte`
89 | 3. Some initialization code (in our `client/main.js` client JavaScript entry point), in a `Meteor.startup` block, which knows how to call code when the page is loaded and ready. This code creates a Svelte component that will be mounted using the `#app` html element.
90 |
91 | ### Define view components with Svelte
92 |
93 | In Svelte, single file components are created with the .svelte file extension and are comprised of three sections, the script section, the markup section and the style section. Within the script section you will write Javascript that runs when the component instance is created. The Svelte component format is fully explained in the [Svelte Guide](https://svelte.dev/docs#Component_format)
94 |
95 | ### Check the result
96 |
97 | In our browser, the app should **roughly** look like the following (though much less pretty):
98 |
99 | > #### Todo List
100 | >
101 | > - This is task 1
102 | > - This is task 2
103 | > - This is task 3
104 |
105 | 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.
106 |
107 | ### Add CSS styles to your app
108 |
109 | In order to make your todo list more visually appealing, copy the following code into **`client/main.css`**:
110 |
111 | {{> DiffBox tutorialName="simple-todos-svelte" step="2.6"}}
112 |
113 | Now that you've added the CSS, the app should look a lot nicer. Check your browser to see that the new styles have loaded.
114 |
115 | {{/template}}
116 |
--------------------------------------------------------------------------------
/content/svelte/step03.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step03"}}
2 |
3 | # Storing tasks in a collection
4 |
5 | {{> step03CollectionsIntro tutorialName="simple-todos-svelte"}}
6 |
7 | Let's update our client-side JavaScript code to get our tasks from a collection instead of a static array:
8 |
9 | {{> DiffBox tutorialName="simple-todos-svelte" step="3.3"}}
10 |
11 | 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!
12 |
13 | {{> step03InsertingTasksFromConsole}}
14 |
15 | {{/template}}
16 |
--------------------------------------------------------------------------------
/content/svelte/step04.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step04"}}
2 |
3 | # Adding tasks with a form
4 |
5 | In this step, we'll add an input field for users to add new tasks to the list.
6 |
7 | First, let's add a form and a form input to our `App` component's markup section. Then we will add a `newTask` property to the `App` component's script section:
8 |
9 | {{> DiffBox step="4.1" tutorialName="simple-todos-svelte"}}
10 |
11 | This form's input tag will have a `bind:value` attribute added to it and this will bind the input's value to the `newTask` property.
12 |
13 | Next, the `handleSubmit` method will be added to the `App` component's script section. In order to execute the `handleSubmit` function on our form's submit event we will add the `on:submit|preventDefault` attribute to the form tag:
14 |
15 | {{> DiffBox step="4.2" tutorialName="simple-todos-svelte"}}
16 |
17 | To add a new 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.
18 |
19 | ### Inserting into a collection
20 |
21 | 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.
22 |
23 | 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.
24 |
25 | ### Sorting our tasks
26 |
27 | 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.
28 |
29 | 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 `Tasks.find` call inside the `App` component:
30 |
31 | {{> DiffBox step="4.3" tutorialName="simple-todos-svelte"}}
32 |
33 | 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.
34 |
35 | In the next step, we'll add some very important todo list features: checking off and deleting tasks.
36 | {{/template}}
37 |
--------------------------------------------------------------------------------
/content/svelte/step05.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step05"}}
2 |
3 | # Checking off and deleting tasks
4 |
5 | Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them.
6 |
7 | Let's add two new elements to our `Task` component, a checkbox and a delete button, with event handlers for both:
8 |
9 | {{> DiffBox step="5.1" tutorialName="simple-todos-svelte"}}
10 |
11 | ### Update
12 |
13 | In the code above, we call `Tasks.update` to check off a task.
14 |
15 | 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.
16 |
17 | 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.
18 |
19 | ### Remove
20 |
21 | 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.
22 |
23 | {{/template}}
--------------------------------------------------------------------------------
/content/svelte/step07.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step07"}}
2 |
3 | # Storing temporary UI data in Svelte components data field
4 |
5 | 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 Svelte's component state to store temporary information that is only used on the client.
6 |
7 | First, we need to add a checkbox to our `App` component:
8 |
9 | {{> DiffBox step="7.1" tutorialName="simple-todos-svelte"}}
10 |
11 | You can see that it reads from `hideCompleted`. We'll need to initialize the value of `hideCompleted` to false:
12 |
13 | {{> DiffBox step="7.2" tutorialName="simple-todos-svelte"}}
14 |
15 | The `hideCompleted` component propert will reactively update the contents of the `task` array and filter out tasks that have been checked if neeeded:
16 |
17 | {{> DiffBox step="7.3" tutorialName="simple-todos-svelte"}}
18 |
19 | Now if you check the box, the task list will only show tasks that haven't been completed.
20 |
21 |
22 | ### One more feature: Showing a count of incomplete tasks
23 |
24 | 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. Since we already have the data in the client-side collection, adding this extra count doesn't involve asking the server for anything.
25 |
26 | {{> DiffBox step="7.4" tutorialName="simple-todos-svelte"}}
27 |
28 | {{> DiffBox step="7.5" tutorialName="simple-todos-svelte"}}
29 |
30 | {{/template}}
31 |
--------------------------------------------------------------------------------
/content/svelte/step08.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step08"}}
2 |
3 | # Adding user accounts
4 |
5 | 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.
6 |
7 | > Currently, this UI component uses Blaze, Meteor's default UI engine.
8 |
9 | To make use of the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:
10 |
11 | ```bash
12 | meteor add accounts-ui accounts-password
13 | ```
14 |
15 | ### Wrapping a Blaze component in Svelte
16 |
17 | To use the login Blaze template from the `accounts-ui` package inside of a Svelte component, we need to first add the `svelte:blaze-integration` Meteor package:
18 |
19 | ```sh
20 | meteor add svelte:blaze-integration
21 | ```
22 |
23 | Now we can include the `loginButtons` template in the `App` component:
24 |
25 | {{> DiffBox step="8.3" tutorialName="simple-todos-svelte"}}
26 |
27 | We will then need to add a new file to configure the accounts package `imports/startup/accounts-config.js`.
28 |
29 | Then, add the following code to configure the accounts UI to use usernames instead of email addresses:
30 |
31 | {{> DiffBox step="8.4" tutorialName="simple-todos-svelte"}}
32 |
33 | We also need to import that configuration file into our client side entrypoint:
34 |
35 | {{> DiffBox step="8.5" tutorialName="simple-todos-svelte"}}
36 |
37 | ### Adding user-related functionality
38 |
39 | 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:
40 |
41 | 1. Only display the new task input field to logged in users
42 | 2. Show which user created each task
43 |
44 | To do this, we will add two new fields to the `tasks` collection:
45 |
46 | 1. `owner` - the `_id` of the user that created the task.
47 | 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.
48 |
49 | First, let's add some code to save these fields into the `handleSubmit` event handler:
50 |
51 | {{> DiffBox step="8.6" tutorialName="simple-todos-svelte"}}
52 |
53 | Modify the `App` component to get the information about the currently logged in user:
54 |
55 | {{> DiffBox step="8.7" tutorialName="simple-todos-svelte"}}
56 |
57 | Then, we can wrap our form in a `{#if expression}{/if}` directive to conditionally render our form only when there is a logged in user:
58 |
59 | {{> DiffBox step="8.8" tutorialName="simple-todos-svelte"}}
60 |
61 | Finally, add a statement to display the `username` field on each task right before the text:
62 |
63 | {{> DiffBox step="8.9" tutorialName="simple-todos-svelte"}}
64 |
65 | 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.
66 |
67 | 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.
68 |
69 | ### Automatic accounts UI
70 |
71 | 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.
72 |
73 | ### Getting information about the logged-in user
74 |
75 | 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`.
76 |
77 | In the next step, we will learn how to make our app more secure by doing data validation on the server.
78 | {{/template}}
79 |
--------------------------------------------------------------------------------
/content/svelte/step09.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step09"}}
2 |
3 | # Security with methods
4 |
5 | Before this step, any user of the app could edit any part of the database. This might be okay for very small internal apps or demos, but any real application needs to control permissions for its data. In Meteor, the best way to do this is by declaring _methods_. Instead of the client code directly calling `insert`, `update`, and `remove`, it will instead call methods that will check if the user is authorized to complete the action and then make any changes to the database on the client's behalf.
6 |
7 | ### Removing `insecure`
8 |
9 | Every newly created Meteor project has the `insecure` package added by default. This is the package that allows us to edit the database from the client. It's useful when prototyping, but now we are taking off the training wheels. To remove this package, go to your app directory and run:
10 |
11 | ```bash
12 | meteor remove insecure
13 | ```
14 |
15 | If you try to use the app after removing this package, you will notice that none of the inputs or buttons work anymore. This is because all client-side database permissions have been revoked. Now we need to rewrite some parts of our app to use methods.
16 |
17 | ### Defining methods
18 |
19 | First, we need to define some methods. We need one method for each database operation we want to perform on the client. Methods should be defined in code that is executed on the client and the server - we will discuss this a bit later in the section titled _Optimistic UI_.
20 |
21 | {{> DiffBox step="9.2" tutorialName="simple-todos-svelte"}}
22 |
23 | Now that we have defined our methods, we need to update the places we were operating on the collection to use the methods instead:
24 |
25 | {{> DiffBox step="9.3" tutorialName="simple-todos-svelte"}}
26 |
27 | {{> DiffBox step="9.4" tutorialName="simple-todos-svelte"}}
28 |
29 | Now all of our inputs and buttons will start working again. What did we gain from all of this work?
30 |
31 | 1. When we insert tasks into the database, we can now securely verify that the user is logged in, that the `createdAt` field is correct, and that the `owner` and `username` fields are correct and the user isn't impersonating anyone.
32 | 2. We can add extra validation logic to `setChecked` and `deleteTask` in later steps when users can make tasks private.
33 | 3. Our client code is now more separated from our database logic. Instead of a lot of stuff happening inside our event handlers, we now have methods that can be called from anywhere.
34 |
35 | {{> step09OptimisticUI}}
36 |
37 | {{/template}}
38 |
--------------------------------------------------------------------------------
/content/svelte/step10.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step10"}}
2 |
3 | # Filtering data with publish and subscribe
4 |
5 | Now that we have moved all of our app's sensitive code into methods, we need to learn about the other half of Meteor's security story. Until now, we have worked assuming the entire database is present on the client, meaning if we call `Tasks.find()` we will get every task in the collection. That's not good if users of our application want to store privacy-sensitive data. We need a way of controlling which data Meteor sends to the client-side database.
6 |
7 | Just like with `insecure` in the last step, all new Meteor apps start with the `autopublish` package, which automatically synchronizes all of the database contents to the client. Let's remove it and see what happens:
8 |
9 | ```bash
10 | meteor remove autopublish
11 | ```
12 |
13 | When the app refreshes, the task list will be empty. Without the `autopublish` package, we will have to specify explicitly what the server sends to the client. The functions in Meteor that do this are `Meteor.publish` and `Meteor.subscribe`.
14 |
15 | First lets add a publication for all the tasks we have in our Mongo collection:
16 |
17 | {{> DiffBox step="10.2" tutorialName="simple-todos-svelte"}}
18 |
19 | Calling `Meteor.publish` on the server registers a _publication_ named `"tasks"`.
20 |
21 | And then let's subscribe to that publication in the `App` component's `onMount` event handler. This event handler is called when the component has been added to the DOM.
22 |
23 | {{> DiffBox step="10.3" tutorialName="simple-todos-svelte"}}
24 |
25 | Once you have added this code, all of the tasks will reappear.
26 |
27 | To truly see the power of the publish/subscribe model, let's implement a feature that allows users to mark tasks as "private" so that no other users can see them.
28 |
29 | ### Adding a button to make tasks private
30 |
31 | Let's add another property to `Task` component called `showPrivateButton` and a button for users to mark a task as private. This button should only show up for the owner of a task. We want the label to indicate the current status: public or private.
32 |
33 | First, we need to add a new method that we can call to set a task's private status:
34 |
35 | {{> DiffBox step="10.4" tutorialName="simple-todos-svelte"}}
36 |
37 | Now, we need to update the `Task` component to include a new `showPrivateButton` field:
38 |
39 | {{> DiffBox step="10.5" tutorialName="simple-todos-svelte"}}
40 |
41 | The `Task` component will need to determine whether to show the button when a user logs in and logs out of
42 | the application:
43 |
44 | {{> DiffBox step="10.6" tutorialName="simple-todos-svelte"}}
45 |
46 | Now let's add the button:
47 |
48 | {{> DiffBox step="10.7" tutorialName="simple-todos-svelte"}}
49 |
50 | We need to define the event handler called by the button:
51 |
52 | {{> DiffBox step="10.8" tutorialName="simple-todos-svelte"}}
53 |
54 | One last thing, let's update the class of the `
` element in the `Task` component to reflect it's privacy status.
55 |
56 | {{> DiffBox step="10.9" tutorialName="simple-todos-svelte"}}
57 |
58 | ### Selectively publishing tasks based on privacy status
59 |
60 | Now that we have a way of setting which tasks are private, we should modify our
61 | publication function to only send the tasks that a user is authorized to see:
62 |
63 | {{> DiffBox step="10.10" tutorialName="simple-todos-svelte"}}
64 |
65 | To test that this functionality works, you can use your browser's private browsing mode to log in as a different user. Put the two windows side by side and mark a task private to confirm that the other user can't see it. Now make it public again and it will reappear!
66 |
67 | ### Extra method security
68 |
69 | In order to finish up our private task feature, we need to add checks to our `deleteTask` and `setChecked` methods to make sure only the task owner can delete or check off a private task:
70 |
71 | {{> DiffBox step="10.11" tutorialName="simple-todos-svelte"}}
72 |
73 | > Notice that with this code anyone can delete any public task. With some small modifications to the code, you should be able to make it so that only the owner can delete their tasks.
74 |
75 | We're done with our private task feature! Now our app is secure from attackers trying to view or modify someone's private tasks.
76 |
77 | {{/template}}
78 |
--------------------------------------------------------------------------------
/content/svelte/step11.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step11"}}
2 |
3 | # Testing
4 |
5 | Now that we've created a few features for our application, let's run a few tests to ensure that everything works the way we expect.
6 |
7 | ### Install Meteor and npm dependencies
8 |
9 | First, we will add a [test driver](http://guide.meteor.com/testing.html#test-driver) for the [Mocha](https://mochajs.org) JavaScript test framework, along with a test assertion library:
10 |
11 | ```bash
12 | meteor add meteortesting:mocha
13 | meteor npm install --save-dev chai
14 | ```
15 | ### Run `meteor test` with a driver package
16 |
17 | With these installed we can now run our app in "test mode" by running `meteor test` and specifying a test driver package (first you'll need to stop the regular app from running):
18 |
19 | ```bash
20 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
21 | ```
22 |
23 | After running that command, you should see the test results for the two tests included by default in the `tests/main.js` file from when we ran the `meteor create` command
24 |
25 | ```bash
26 | --------------------------------
27 | --- RUNNING APP SERVER TESTS ---
28 | --------------------------------
29 | ```
30 |
31 | followed by
32 |
33 | ```bash
34 | simple-todos-svelte
35 | ✓ package.json has correct name
36 | ✓ server is not client
37 |
38 | 2 passing (10ms)
39 | ```
40 |
41 | ### Understand existing tests
42 |
43 | Every new Meteor application includes a **`tests/main.js`** module containing several example tests using the `describe`, `it`, and `assert` style popularized by testing frameworks like [Mocha](https://mochajs.org/#getting-started):
44 |
45 | ```js
46 | import assert from "assert";
47 |
48 | describe("simple-todos-svelte", function() {
49 | it("package.json has correct name", async function() {
50 | const { name } = await import("../package.json");
51 | assert.strictEqual(name, "simple-todos-svelte");
52 | });
53 |
54 | if (Meteor.isClient) {
55 | it("client is not server", function() {
56 | assert.strictEqual(Meteor.isServer, false);
57 | });
58 | }
59 |
60 | if (Meteor.isServer) {
61 | it("server is not client", function() {
62 | assert.strictEqual(Meteor.isClient, false);
63 | });
64 | }
65 | });
66 | ```
67 |
68 | This module serves as the entry point for all your application tests, and it is defined as such in the `package.json` file. Under the `meteor` field in the `package.json` file is the `testModule` field and it is this field that refers to the entry point for the `meteor test` command. If you would like to, you can continue adding new tests to this module, using `Meteor.isServer` and `Meteor.isClient` to determine which tests run will run in which environment.
69 |
70 | ### Import additional test modules
71 |
72 | However, if you would prefer to split your tests across multiple modules, you can do that too. We will demonstrate this approach now. Let's add a new test module called **`imports/api/tasks.tests.js`**:
73 |
74 | {{> DiffBox tutorialName="simple-todos-svelte" step="11.2"}}
75 |
76 | In any test we make we need to ensure the database is in the state we expect before beginning. We can use Mocha's `beforeEach` construct to do test setup tasks:
77 |
78 | {{> DiffBox tutorialName="simple-todos-svelte" step="11.3"}}
79 |
80 | Here we create a single task that's associated with a random `userId` that'll be different for each test run.
81 |
82 | Now we can write the test to call the `tasks.remove` method "as" that user and verify the task is deleted:
83 |
84 | {{> DiffBox tutorialName="simple-todos-svelte" step="11.4"}}
85 |
86 | The only remaining step is to import this new test module into the main `tests/main.js` module:
87 |
88 | {{> DiffBox tutorialName="simple-todos-svelte" step="11.5"}}
89 |
90 | ### Run `meteor test` again
91 |
92 | If you run the test command again (or if you left it running from before)
93 |
94 | ```bash
95 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
96 | ```
97 |
98 | you should now see the output from the new test module we just added:
99 |
100 | ```bash
101 | Tasks
102 | methods
103 | ✓ can delete owned task
104 |
105 | simple-todos-svelte
106 | ✓ package.json has correct name
107 | ✓ server is not client
108 |
109 | 3 passing (120ms)
110 | ```
111 |
112 | ### Other useful testing commands
113 |
114 | To make it easier to type this command, you may want to add a shorthand to the [`"scripts"` section](https://docs.npmjs.com/misc/scripts) of your `package.json` file.
115 |
116 | In fact, new Meteor apps come with a few preconfigured npm scripts, which you are welcome to use or modify.
117 |
118 | The standard `npm test` (or `meteor npm test`) command runs the following command:
119 |
120 | ```bash
121 | meteor test --once --driver-package meteortesting:mocha
122 | ```
123 |
124 | This command is suitable for running in a continuous integration (CI) environment such as [Travis CI](https://travis-ci.org) or [CircleCI](https://circleci.com), since it runs only your server-side tests and then exits with 0 if all the tests passed.
125 |
126 | If you would like to run your tests while developing your application (and re-run them whenever the development server restarts), consider using `meteor npm run test-app`, which is equivalent to
127 |
128 | ```bash
129 | TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha
130 | ```
131 |
132 | This is almost the same as the earlier command, except that it also loads your application code as normal (due to `--full-app`), allowing you to interact with your app in the browser while running both client and server tests.
133 |
134 | ### Further reading
135 |
136 | There's a lot more you can do with Meteor tests! You can read more about it in the Meteor Guide [article on testing](http://guide.meteor.com/testing.html).
137 |
138 | {{/template}}
139 |
--------------------------------------------------------------------------------
/content/svelte/step12.md:
--------------------------------------------------------------------------------
1 | {{#template name="svelte-step12"}}
2 |
3 | # What's next?
4 |
5 | Congratulations on your newly built Meteor app!
6 |
7 | {{> step12NextSteps}}
8 |
9 | {{/template}}
10 |
--------------------------------------------------------------------------------
/content/vue/metadata.js:
--------------------------------------------------------------------------------
1 | if (Meteor.isClient) {
2 | DiffBox.registerTutorial("simple-todos-vue", {
3 | gitHubRepoName: "meteor/simple-todos-vue",
4 | patchFilename: "generated/vue.multi.patch"
5 | });
6 | }
7 |
8 | TutorialRegistry.registerTutorial("vue", {
9 | title: "Simple Todos Vue",
10 | subtitle: "Learn how to use Meteor and Vue together",
11 | tutorialSourceLink: "github.com/meteor/tutorials/content/vue",
12 | steps: [
13 | {
14 | title: "Creating an app",
15 | slug: "creating-an-app",
16 | template: "sharedStep01"
17 | },
18 | {
19 | title: 'Components',
20 | slug: "components",
21 | template: 'vue-step02'
22 | },
23 | {
24 | title: 'Collections',
25 | slug: "collections",
26 | template: 'vue-step03'
27 | },
28 | {
29 | title: 'Forms and events',
30 | slug: "forms-and-events",
31 | template: 'vue-step04'
32 | },
33 | {
34 | title: 'Update and remove',
35 | slug: "update-and-remove",
36 | template: 'vue-step05'
37 | },
38 | {
39 | title: 'Running on mobile',
40 | slug: "running-on-mobile",
41 | template: "sharedStep06"
42 | },
43 | {
44 | title: 'Temporary UI state',
45 | slug: "temporary-ui-state",
46 | template: 'vue-step07'
47 | },
48 | {
49 | title: 'Adding user accounts',
50 | slug: "adding-user-accounts",
51 | template: 'vue-step08'
52 | },
53 | {
54 | title: 'Security with methods',
55 | slug: "security-with-methods",
56 | template: 'vue-step09'
57 | },
58 | {
59 | title: 'Publish and subscribe',
60 | slug: "publish-and-subscribe",
61 | template: 'vue-step10'
62 | },
63 | {
64 | title: 'Testing',
65 | slug: "testing",
66 | template: 'vue-step11'
67 | },
68 | {
69 | title: 'Next steps',
70 | slug: "next-steps",
71 | template: 'vue-step12'
72 | }
73 | ]
74 | });
75 |
--------------------------------------------------------------------------------
/content/vue/step02.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step02"}}
2 |
3 | # In a hurry?
4 |
5 | If you don't have time to follow this detailed tutorial right now, you have two options.
6 |
7 | You can skip to the finished application explained by this tutorial, by running the following commands:
8 |
9 | ```sh
10 | git clone git@github.com:meteor/simple-todos-vue.git
11 | cd simple-todos-vue
12 | meteor npm install
13 | meteor
14 | ```
15 |
16 | If you are an experienced Vue developer, most of this application will be self-explanatory, though you can always refer back to this tutorial to understand the details.
17 |
18 | # Working with Vue
19 |
20 | The rest of this tutorial will take you on a step-by-step journey through the details of using Vue with Meteor.
21 |
22 | ### Install Vue dependencies
23 |
24 | To start working with Vue as our view library, we first need to add the relevant npm package.
25 |
26 | Open a new terminal in the same directory as your running app, and run the following command:
27 |
28 | ```sh
29 | meteor npm install --save vue
30 | ```
31 |
32 | > 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.
33 |
34 | We also need to add the akryum:vue-component Meteor package to allow us to create files with the .vue extension, which makes it possible to create Vue components in the single file format.
35 |
36 | ```sh
37 | meteor add akryum:vue-component
38 | ```
39 |
40 | ### Replace `blaze-html-templates` with `static-html`
41 |
42 | By default, new Meteor applications use Blaze as their templating engine and because we created our Meteor application with the `meteor create` command the `blaze-html-templates` package was included by default. While it's possible for a Meteor application to use Blaze and Vue simultaneously and we will demonstrate how to do this later, the application we're building in this tutorial does not need the `blaze-html-templates` Meteor package.
43 |
44 | First we will remove the `blaze-html-templates` Meteor package:
45 |
46 | ```sh
47 | meteor remove blaze-html-templates
48 | ```
49 |
50 | Now, to ensure `.html` files are processed as static HTML, we will add the `static-html` Meteor package:
51 |
52 | ```sh
53 | meteor add static-html
54 | ```
55 |
56 | The `static-html` package is not specific to Vue. It simply turns `` and `` fragments found in `.html` files into raw HTML that will be served from the Meteor web server. Later, your Vue application will render its components into this HTML.
57 |
58 | Note that both `blaze-html-templates` and `static-html` are _Meteor packages_, rather than npm packages, because they need to register _compiler plugins_ that determine how `.html` files are processed. Controlling compilation is one of several key features that make Meteor packages more powerful than npm packages.
59 |
60 | ### Replace the starter code
61 |
62 | To get started, let's replace the code of the default starter app. Then we'll talk about what it does.
63 |
64 | First, replace the content of the **`client/main.html`** file:
65 |
66 | {{> DiffBox tutorialName="simple-todos-vue" step="2.2"}}
67 |
68 | Second, replace the contents of the **`client/main.js`** module:
69 |
70 | {{> DiffBox tutorialName="simple-todos-vue" step="2.3"}}
71 |
72 | Now we need to create a new directory called `imports`, with a directory called `ui` inside of it. There will soon be other directories besides `ui` within `imports`, but we'll start with `ui`.
73 |
74 | > Note: in previous versions of Meteor, the `imports` directory was special because files outside the `imports` directory were loaded automatically when the application started, whereas files inside the `imports` directory were only loaded when imported using an `import` declaration or a `require` statement. As of Meteor 1.7, the entry point for both client and server JavaScript is determined by the `meteor.mainModule` section in `package.json`. In other words, as far as JavaScript code is concerned, the entire application now behaves as if it was inside an `imports` directory, so you don't need to worry as much about the `imports` directory now.
75 |
76 | 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.
77 |
78 | To continue converting your app to use Vue, copy the following code into **`imports/ui/App.vue`**:
79 |
80 | {{> DiffBox tutorialName="simple-todos-vue" step="2.4"}}
81 |
82 | Now copy the following code into **`imports/ui/Task.vue`**:
83 |
84 | {{> DiffBox tutorialName="simple-todos-vue" step="2.5"}}
85 |
86 | We just added three things to our app:
87 |
88 | 1. An `App` Vue component in `imports/ui/App.vue`
89 | 2. A `Task` Vue component in `imports/ui/Task.vue`
90 | 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 creates the root Vue instance and passes in the options object to it's contructor. This root Vue instance will be mounted using the `#app` html element.
91 |
92 | ### Define view components with Vue
93 |
94 | In Vue, single file components are created with the .vue file extension and are comprised of three sections, the script section, the template section and the style section. Within the script section you will define a Vue instance that supports the template section. This Vue instace, like the root Vue instance created in `main.js`, is fully documented in the [Vue.js Guide](https://vuejs.org/v2/guide/instance.html)
95 |
96 | ### Check the result
97 |
98 | In our browser, the app should **roughly** look like the following (though much less pretty):
99 |
100 | > #### Todo List
101 | >
102 | > - This is task 1
103 | > - This is task 2
104 | > - This is task 3
105 |
106 | 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.
107 |
108 | ### Add CSS styles to your app
109 |
110 | In order to make your todo list more visually appealing, copy the following code into **`client/main.css`**:
111 |
112 | {{> DiffBox tutorialName="simple-todos-vue" step="2.6"}}
113 |
114 | Now that you've added the CSS, the app should look a lot nicer. Check your browser to see that the new styles have loaded.
115 |
116 | {{/template}}
117 |
--------------------------------------------------------------------------------
/content/vue/step03.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step03"}}
2 |
3 | # Storing tasks in a collection
4 |
5 | {{> step03CollectionsIntro tutorialName="simple-todos-vue"}}
6 |
7 | ### 3.3 Using data from a collection inside a Vue component
8 |
9 | To use data from a Meteor collection inside a Vue component, we can use a package called `vue-meteor-tracker`, which allows us to create a "data container" to feed Meteor's reactive data into Vue's component hierarchy.
10 |
11 | ```bash
12 | meteor npm install --save vue-meteor-tracker
13 | ```
14 | In your Vue component, we can now add a `meteor` object:
15 |
16 | ```
17 | export default {
18 | meteor: {
19 | // Meteor specific options
20 | }
21 | }
22 | ```
23 |
24 | {{> DiffBox step="3.4" tutorialName="simple-todos-vue"}}
25 |
26 | {{> DiffBox step="3.5" tutorialName="simple-todos-vue"}}
27 |
28 | Add a field for each reactive Meteor data source that you need access to, in this case we want access to the Tasks Mongo Collection.
29 |
30 | ```
31 | export default {
32 | meteor: {
33 | tasks() {
34 | return Tasks.find({}).fetch();
35 | }
36 | }
37 | }
38 | ```
39 |
40 | 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—so we need to insert some tasks!
41 |
42 | {{> step03InsertingTasksFromConsole}}
43 |
44 | {{/template}}
45 |
--------------------------------------------------------------------------------
/content/vue/step04.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step04"}}
2 |
3 | # Adding tasks with a form
4 |
5 | In this step, we'll add an input field for users to add new tasks to the list.
6 |
7 | First, let's add a form to our `App` component's template section and a `newTask` field to the `data` object in our Vue instance:
8 |
9 | {{> DiffBox step="4.1" tutorialName="simple-todos-vue"}}
10 |
11 | This form will have an input element added to it that has a `v-model` attribute. The `newTask` data field will now be bound via two-way binding to the input element's value.
12 |
13 | You can also see that the `form` element has a `@submit.prevent` attribute that references a method called `handleSubmit` that we will later be defined in the component's `methods` object. In Vue, this is how you listen to browser events, like the submit event on the form.
14 |
15 | We can now add that `handleSubmit` method to our `App` component's `methods` object:
16 |
17 | {{> DiffBox step="4.2" tutorialName="simple-todos-vue"}}
18 |
19 | Now your app has a new input field. To add a new 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.
20 |
21 | ### Listening for events in Vue
22 |
23 | As you can see, in Vue 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 `v-model` property and adding a field to the data property. Read more about the different kinds of events Vue supports, and how the event system works, in the [Vue.js Guide](https://vuejs.org/v2/guide/#Handling-User-Input).
24 |
25 | ### Inserting into a collection
26 |
27 | 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.
28 |
29 | 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.
30 |
31 | ### Sorting our tasks
32 |
33 | 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.
34 |
35 | We can solve this by sorting the tasks using the `createdAt` field that is automatically added by our new code. Just add a sort option to the `Tasks.find` call inside the `App` component:
36 |
37 | {{> DiffBox step="4.3" tutorialName="simple-todos-vue"}}
38 |
39 | 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.
40 |
41 | In the next step, we'll add some very important todo list features: checking off and deleting tasks.
42 | {{/template}}
43 |
--------------------------------------------------------------------------------
/content/vue/step05.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step05"}}
2 |
3 | # Checking off and deleting tasks
4 |
5 | Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them.
6 |
7 | Let's add two new elements to our `task` component, a checkbox and a delete button, with event handlers for both:
8 |
9 | {{> DiffBox step="5.1" tutorialName="simple-todos-vue"}}
10 |
11 | ### Update
12 |
13 | In the code above, we call `Tasks.update` to check off a task.
14 |
15 | 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.
16 |
17 | 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.
18 |
19 | ### Remove
20 |
21 | 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.
22 |
23 | {{/template}}
24 |
--------------------------------------------------------------------------------
/content/vue/step07.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step07"}}
2 |
3 | # Storing temporary UI data in Vue components data field
4 |
5 | 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 Vue's component state to store temporary information that is only used on the client.
6 |
7 | First, we need to add a checkbox to our `App` component:
8 |
9 | {{> DiffBox step="7.1" tutorialName="simple-todos-vue"}}
10 |
11 | You can see that it reads from `this.hideCompleted`. We'll need to initialize the value of `this.hideCompleted` in the component's data object:
12 |
13 | {{> DiffBox step="7.2" tutorialName="simple-todos-vue"}}
14 |
15 | We can update `this.hideCompleted` from an event handler directly, which will then cause the component to re-render:
16 |
17 | {{> DiffBox step="7.3" tutorialName="simple-todos-vue"}}
18 |
19 | Now, we need to update the list of tasks to filter out completed tasks when `this.hideCompleted` is true:
20 |
21 | {{> DiffBox step="7.4" tutorialName="simple-todos-vue"}}
22 |
23 | Now if you check the box, the task list will only show tasks that haven't been completed.
24 |
25 | ### One more feature: Showing a count of incomplete tasks
26 |
27 | 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 the meteor object of the Vue instace. Since we already have the data in the client-side collection, adding this extra count doesn't involve asking the server for anything.
28 |
29 | {{> DiffBox step="7.5" tutorialName="simple-todos-vue"}}
30 |
31 | {{> DiffBox step="7.6" tutorialName="simple-todos-vue"}}
32 |
33 | {{/template}}
34 |
--------------------------------------------------------------------------------
/content/vue/step08.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step08"}}
2 |
3 | # Adding user accounts
4 |
5 | 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.
6 |
7 | > Currently, this UI component uses Blaze, Meteor's default UI engine.
8 |
9 | To make use of the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command:
10 |
11 | ```bash
12 | meteor add accounts-ui accounts-password
13 | ```
14 |
15 | ### Wrapping a Blaze component in Vue
16 |
17 | To use the Blaze UI component from the `accounts-ui` package inside of a Vue component, we need to make use of the Vue and Blaze Integration package. So we will first need to install this package.
18 |
19 | ```sh
20 | meteor add vuejs:blaze-integration
21 | ```
22 |
23 | Let's include the `loginButtons` template in the App component by adding the following markup ``:
24 |
25 | {{> DiffBox step="8.3" tutorialName="simple-todos-vue"}}
26 |
27 | We will then need to add a new file to configure the accounts package `imports/startup/accounts-config.js`.
28 |
29 | Then, add the following code to configure the accounts UI to use usernames instead of email addresses:
30 |
31 | {{> DiffBox step="8.4" tutorialName="simple-todos-vue"}}
32 |
33 | We also need to import that configuration file into our client side entrypoint:
34 |
35 | {{> DiffBox step="8.5" tutorialName="simple-todos-vue"}}
36 |
37 | ### Adding user-related functionality
38 |
39 | 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:
40 |
41 | 1. Only display the new task input field to logged in users
42 | 2. Show which user created each task
43 |
44 | To do this, we will add two new fields to the `tasks` collection:
45 |
46 | 1. `owner` - the `_id` of the user that created the task.
47 | 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.
48 |
49 | First, let's add some code to save these fields into the `handleSubmit` event handler:
50 |
51 | {{> DiffBox step="8.6" tutorialName="simple-todos-vue"}}
52 |
53 | Modify the data container to get information about the currently logged in user:
54 |
55 | {{> DiffBox step="8.7" tutorialName="simple-todos-vue"}}
56 |
57 | Then, we can wrap our form in a `` tag and add in the `v-if` directive to conditionally render our form only when there is a logged in user:
58 |
59 | {{> DiffBox step="8.8" tutorialName="simple-todos-vue"}}
60 |
61 | Finally, add a statement to display the `username` field on each task right before the text:
62 |
63 | {{> DiffBox step="8.9" tutorialName="simple-todos-vue"}}
64 |
65 | 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.
66 |
67 | 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.
68 |
69 | ### Automatic accounts UI
70 |
71 | 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.
72 |
73 | ### Getting information about the logged-in user
74 |
75 | 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`.
76 |
77 | In the next step, we will learn how to make our app more secure by doing data validation on the server.
78 | {{/template}}
79 |
--------------------------------------------------------------------------------
/content/vue/step09.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step09"}}
2 |
3 | # Security with methods
4 |
5 | Before this step, any user of the app could edit any part of the database. This might be okay for very small internal apps or demos, but any real application needs to control permissions for its data. In Meteor, the best way to do this is by declaring _methods_. Instead of the client code directly calling `insert`, `update`, and `remove`, it will instead call methods that will check if the user is authorized to complete the action and then make any changes to the database on the client's behalf.
6 |
7 | ### Removing `insecure`
8 |
9 | Every newly created Meteor project has the `insecure` package added by default. This is the package that allows us to edit the database from the client. It's useful when prototyping, but now we are taking off the training wheels. To remove this package, go to your app directory and run:
10 |
11 | ```bash
12 | meteor remove insecure
13 | ```
14 |
15 | If you try to use the app after removing this package, you will notice that none of the inputs or buttons work anymore. This is because all client-side database permissions have been revoked. Now we need to rewrite some parts of our app to use methods.
16 |
17 | ### Defining methods
18 |
19 | First, we need to define some methods. We need one method for each database operation we want to perform on the client. Methods should be defined in code that is executed on the client and the server - we will discuss this a bit later in the section titled _Optimistic UI_.
20 |
21 | {{> DiffBox step="9.2" tutorialName="simple-todos-vue"}}
22 |
23 | Now that we have defined our methods, we need to update the places we were operating on the collection to use the methods instead:
24 |
25 | {{> DiffBox step="9.3" tutorialName="simple-todos-vue"}}
26 |
27 | {{> DiffBox step="9.4" tutorialName="simple-todos-vue"}}
28 |
29 | Now all of our inputs and buttons will start working again. What did we gain from all of this work?
30 |
31 | 1. When we insert tasks into the database, we can now securely verify that the user is logged in, that the `createdAt` field is correct, and that the `owner` and `username` fields are correct and the user isn't impersonating anyone.
32 | 2. We can add extra validation logic to `setChecked` and `deleteTask` in later steps when users can make tasks private.
33 | 3. Our client code is now more separated from our database logic. Instead of a lot of stuff happening inside our event handlers, we now have methods that can be called from anywhere.
34 |
35 | {{> step09OptimisticUI}}
36 |
37 | {{/template}}
38 |
--------------------------------------------------------------------------------
/content/vue/step10.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step10"}}
2 |
3 | # Filtering data with publish and subscribe
4 |
5 | Now that we have moved all of our app's sensitive code into methods, we need to learn about the other half of Meteor's security story. Until now, we have worked assuming the entire database is present on the client, meaning if we call `Tasks.find()` we will get every task in the collection. That's not good if users of our application want to store privacy-sensitive data. We need a way of controlling which data Meteor sends to the client-side database.
6 |
7 | Just like with `insecure` in the last step, all new Meteor apps start with the `autopublish` package, which automatically synchronizes all of the database contents to the client. Let's remove it and see what happens:
8 |
9 | ```bash
10 | meteor remove autopublish
11 | ```
12 |
13 | When the app refreshes, the task list will be empty. Without the `autopublish` package, we will have to specify explicitly what the server sends to the client. The functions in Meteor that do this are `Meteor.publish` and `Meteor.subscribe`.
14 |
15 | First lets add a publication for all the tasks we have in our Mongo collection:
16 |
17 | {{> DiffBox step="10.2" tutorialName="simple-todos-vue"}}
18 |
19 | And then let's subscribe to that publication in the `App` component.
20 |
21 | {{> DiffBox step="10.3" tutorialName="simple-todos-vue"}}
22 |
23 | Once you have added this code, all of the tasks will reappear.
24 |
25 | Calling `Meteor.publish` on the server registers a _publication_ named `"tasks"`.
26 |
27 | On the client we will add an object for each subscription we want to subscribe to in the `$subscribe` object. The object key is the name of the publication and the value is either an array of parameters or a function returning an array of parameters.
28 |
29 | To truly see the power of the publish/subscribe model, let's implement a feature that allows users to mark tasks as "private" so that no other users can see them.
30 |
31 | ### Adding a button to make tasks private
32 |
33 | Let's add another property to tasks called "private" and a button for users to mark a task as private. This button should only show up for the owner of a task. We want the label to indicate the current status: public or private.
34 |
35 | First, we need to add a new method that we can call to set a task's private status:
36 |
37 | {{> DiffBox step="10.4" tutorialName="simple-todos-vue"}}
38 |
39 | Now, we need to be able to pass a new property to the `Task` component to determine whether we will
40 | show the private button; the button should show up only if the currently
41 | logged in user owns this task:
42 |
43 | {{> DiffBox step="10.5" tutorialName="simple-todos-vue"}}
44 |
45 | The `App` component will be updated next to pass in the component property we just created in the previous step:
46 |
47 | {{> DiffBox step="10.6" tutorialName="simple-todos-vue"}}
48 |
49 | Let's add the button, using this new prop to decide whether it should be displayed:
50 |
51 | {{> DiffBox step="10.7" tutorialName="simple-todos-vue"}}
52 |
53 | We need to define the event handler called by the button:
54 |
55 | {{> DiffBox step="10.8" tutorialName="simple-todos-vue"}}
56 |
57 | One last thing, let's update the class of the `
` element in the `Task` component to reflect it's privacy status. We'll use the `classnames` package for this:
58 |
59 | ```bash
60 | meteor npm install --save classnames
61 | ```
62 |
63 | Then we'll use that package to choose a class based on the task we are rendering:
64 |
65 | {{> DiffBox step="10.10" tutorialName="simple-todos-vue"}}
66 |
67 | ### Selectively publishing tasks based on privacy status
68 |
69 | Now that we have a way of setting which tasks are private, we should modify our
70 | publication function to only send the tasks that a user is authorized to see:
71 |
72 | {{> DiffBox step="10.11" tutorialName="simple-todos-vue"}}
73 |
74 | To test that this functionality works, you can use your browser's private browsing mode to log in as a different user. Put the two windows side by side and mark a task private to confirm that the other user can't see it. Now make it public again and it will reappear!
75 |
76 | ### Extra method security
77 |
78 | In order to finish up our private task feature, we need to add checks to our `deleteTask` and `setChecked` methods to make sure only the task owner can delete or check off a private task:
79 |
80 | {{> DiffBox step="10.12" tutorialName="simple-todos-vue"}}
81 |
82 | > Notice that with this code anyone can delete any public task. With some small modifications to the code, you should be able to make it so that only the owner can delete their tasks.
83 |
84 | We're done with our private task feature! Now our app is secure from attackers trying to view or modify someone's private tasks.
85 |
86 | {{/template}}
87 |
--------------------------------------------------------------------------------
/content/vue/step11.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step11"}}
2 |
3 | # Testing
4 |
5 | Now that we've added a few features to our application, let's run some tests to make sure that everything works the way we expect.
6 |
7 | ### Install Meteor and npm dependencies
8 |
9 | To do so, we'll add a [test driver](http://guide.meteor.com/testing.html#test-driver) for the [Mocha](https://mochajs.org) JavaScript test framework, along with a test assertion pacakge:
10 |
11 | ```bash
12 | meteor add meteortesting:mocha
13 | meteor npm install --save-dev chai
14 | ```
15 |
16 | ### Run `meteor test` with a driver package
17 |
18 | Next we will run our app in "test mode" by running the `meteor test` command and specifying a test driver package (first you'll need to stop the regular app from running):
19 |
20 | ```bash
21 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
22 | ```
23 |
24 | The first time you run this command, it should output
25 |
26 | ```bash
27 | --------------------------------
28 | --- RUNNING APP SERVER TESTS ---
29 | --------------------------------
30 | ```
31 |
32 | followed by
33 |
34 | ```bash
35 | simple-todos-vue
36 | ✓ package.json has correct name
37 | ✓ server is not client
38 |
39 | 2 passing (10ms)
40 | ```
41 |
42 | ### Understand existing tests
43 |
44 | But where are these two tests coming from? Every new Meteor application includes a **`tests/main.js`** module containing several example tests using the `describe`, `it`, and `assert` style popularized by testing frameworks like [Mocha](https://mochajs.org/#getting-started):
45 |
46 | ```js
47 | import assert from "assert";
48 |
49 | describe("simple-todos-vue", function() {
50 | it("package.json has correct name", async function() {
51 | const { name } = await import("../package.json");
52 | assert.strictEqual(name, "simple-todos-vue");
53 | });
54 |
55 | if (Meteor.isClient) {
56 | it("client is not server", function() {
57 | assert.strictEqual(Meteor.isServer, false);
58 | });
59 | }
60 |
61 | if (Meteor.isServer) {
62 | it("server is not client", function() {
63 | assert.strictEqual(Meteor.isClient, false);
64 | });
65 | }
66 | });
67 | ```
68 |
69 | This module serves as the entry point for all your application tests. If you like, you can continue adding new tests to this module, using `Meteor.isServer` and `Meteor.isClient` to determine which tests will run in which environment.
70 |
71 | ### Import additional test modules
72 |
73 | However, if you would prefer to split your tests across multiple modules, you can do that too. Let's add a new test module called **`imports/api/tasks.tests.js`**:
74 |
75 | {{> DiffBox tutorialName="simple-todos-vue" step="11.2"}}
76 |
77 | In any test we make we need to ensure the database is in the state we expect before beginning. We can use Mocha's `beforeEach` construct to do setup tasks:
78 |
79 | {{> DiffBox tutorialName="simple-todos-vue" step="11.3"}}
80 |
81 | Here we create a single task that's associated with a random `userId` that'll be different for each test run.
82 |
83 | Now we can write the test to call the `tasks.remove` method "as" that user and verify the task is deleted:
84 |
85 | {{> DiffBox tutorialName="simple-todos-vue" step="11.4"}}
86 |
87 | The only remaining step is to import this new test module into the main `tests/main.js` module:
88 |
89 | {{> DiffBox tutorialName="simple-todos-vue" step="11.5"}}
90 |
91 | ### Run `meteor test` again
92 |
93 | If you run the test command again (or if you left it running from before)
94 |
95 | ```bash
96 | TEST_WATCH=1 meteor test --driver-package meteortesting:mocha
97 | ```
98 |
99 | you should now see the output from the new test module we just added:
100 |
101 | ```bash
102 | Tasks
103 | methods
104 | ✓ can delete owned task
105 |
106 | simple-todos-vue
107 | ✓ package.json has correct name
108 | ✓ server is not client
109 |
110 | 3 passing (120ms)
111 | ```
112 |
113 | ### Other useful testing commands
114 |
115 | To make it easier to type this command, you may want to add a shortcut to the [`"scripts"` section](https://docs.npmjs.com/misc/scripts) of your `package.json` file.
116 |
117 | In fact, new Meteor apps come with a few preconfigured npm scripts, which you are welcome to use or modify.
118 |
119 | The standard `npm test` (or `meteor npm test`) command runs the following command:
120 |
121 | ```bash
122 | meteor test --once --driver-package meteortesting:mocha
123 | ```
124 |
125 | This command is suitable for running in a continuous integration (CI) environment such as [Travis CI](https://travis-ci.org) or [CircleCI](https://circleci.com), since it runs only your server-side tests and then exits with 0 if all the tests passed.
126 |
127 | If you would like to run your tests while developing your application (and re-run them whenever the development server restarts), consider using `meteor npm run test-app`, which is equivalent to
128 |
129 | ```bash
130 | TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha
131 | ```
132 |
133 | This is almost the same as the earlier command, except that it also loads your application code as normal (due to `--full-app`), allowing you to interact with your app in the browser while running both client and server tests.
134 |
135 | ### Further reading
136 |
137 | There's a lot more you can do with Meteor tests! You can read more about it in the Meteor Guide [article on testing](http://guide.meteor.com/testing.html).
138 |
139 | {{/template}}
140 |
--------------------------------------------------------------------------------
/content/vue/step12.md:
--------------------------------------------------------------------------------
1 | {{#template name="vue-step12"}}
2 |
3 | # What's next?
4 |
5 | Congratulations on your newly built Meteor app!
6 |
7 | {{> step12NextSteps}}
8 |
9 | {{/template}}
10 |
--------------------------------------------------------------------------------
/generated/angular.multi.patch:
--------------------------------------------------------------------------------
1 | From 81f5bb9f9167d989358f5a79dc2a62d750f100d7 Mon Sep 17 00:00:00 2001
2 | From: Uri Goldshtein
3 | Date: Thu, 24 Mar 2016 17:53:46 +0200
4 | Subject: [PATCH 01/55] Step 1: Run `meteor create`
5 |
6 | ---
7 | .gitignore | 1 +
8 | .meteor/.finished-upgraders | 13 +++++++++
9 | .meteor/.gitignore | 1 +
10 | .meteor/.id | 7 +++++
11 | .meteor/packages | 21 ++++++++++++++
12 | .meteor/platforms | 2 ++
13 | .meteor/release | 1 +
14 | .meteor/versions | 69 +++++++++++++++++++++++++++++++++++++++++++++
15 | client/main.css | 1 +
16 | client/main.html | 25 ++++++++++++++++
17 | client/main.js | 22 +++++++++++++++
18 | package.json | 10 +++++++
19 | server/main.js | 5 ++++
20 | 13 files changed, 178 insertions(+)
21 | create mode 100644 .gitignore
22 | create mode 100644 .meteor/.finished-upgraders
23 | create mode 100644 .meteor/.gitignore
24 | create mode 100644 .meteor/.id
25 | create mode 100644 .meteor/packages
26 | create mode 100644 .meteor/platforms
27 | create mode 100644 .meteor/release
28 | create mode 100644 .meteor/versions
29 | create mode 100644 client/main.css
30 | create mode 100644 client/main.html
31 | create mode 100644 client/main.js
32 | create mode 100644 package.json
33 | create mode 100644 server/main.js
34 |
35 | diff --git a/.gitignore b/.gitignore
36 | new file mode 100644
37 | index 0000000..40b878d
38 | --- /dev/null
39 | +++ b/.gitignore
40 | @@ -0,0 +1 @@
41 | +node_modules/
42 | \ No newline at end of file
43 | diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders
44 | new file mode 100644
45 | index 0000000..dacc2c0
46 | --- /dev/null
47 | +++ b/.meteor/.finished-upgraders
48 | @@ -0,0 +1,13 @@
49 | +# This file contains information which helps Meteor properly upgrade your
50 | +# app when you run 'meteor update'. You should check it into version control
51 | +# with your project.
52 | +
53 | +notices-for-0.9.0
54 | +notices-for-0.9.1
55 | +0.9.4-platform-file
56 | +notices-for-facebook-graph-api-2
57 | +1.2.0-standard-minifiers-package
58 | +1.2.0-meteor-platform-split
59 | +1.2.0-cordova-changes
60 | +1.2.0-breaking-changes
61 | +1.3.0-split-minifiers-package
62 | diff --git a/.meteor/.gitignore b/.meteor/.gitignore
63 | new file mode 100644
64 | index 0000000..4083037
65 | --- /dev/null
66 | +++ b/.meteor/.gitignore
67 | @@ -0,0 +1 @@
68 | +local
69 | diff --git a/.meteor/.id b/.meteor/.id
70 | new file mode 100644
71 | index 0000000..bc09f93
72 | --- /dev/null
73 | +++ b/.meteor/.id
74 | @@ -0,0 +1,7 @@
75 | +# This file contains a token that is unique to your project.
76 | +# Check it into your repository along with the rest of this directory.
77 | +# It can be used for purposes such as:
78 | +# - ensuring you don't accidentally deploy one app on top of another
79 | +# - providing package authors with aggregated statistics
80 | +
81 | +1vxxaq5ipkkmy1fjan5v
82 | diff --git a/.meteor/packages b/.meteor/packages
83 | new file mode 100644
84 | index 0000000..d4c8001
85 | --- /dev/null
86 | +++ b/.meteor/packages
87 | @@ -0,0 +1,21 @@
88 | +# Meteor packages used by this project, one per line.
89 | +# Check this file (and the other files in this directory) into your repository.
90 | +#
91 | +# 'meteor add' and 'meteor remove' will edit this file for you,
92 | +# but you can also edit it by hand.
93 | +
94 | +meteor-base # Packages every Meteor app needs to have
95 | +mobile-experience # Packages for a great mobile UX
96 | +mongo # The database Meteor supports right now
97 | +blaze-html-templates # Compile .html files into Meteor Blaze views
98 | +reactive-var # Reactive variable for tracker
99 | +jquery # Helpful client-side library
100 | +tracker # Meteor's client-side reactive programming library
101 | +
102 | +standard-minifier-css # CSS minifier run for production mode
103 | +standard-minifier-js # JS minifier run for production mode
104 | +es5-shim # ECMAScript 5 compatibility for older browsers.
105 | +ecmascript # Enable ECMAScript2015+ syntax in app code
106 | +
107 | +autopublish # Publish all data to the clients (for prototyping)
108 | +insecure # Allow all DB writes from clients (for prototyping)
109 | diff --git a/.meteor/platforms b/.meteor/platforms
110 | new file mode 100644
111 | index 0000000..efeba1b
112 | --- /dev/null
113 | +++ b/.meteor/platforms
114 | @@ -0,0 +1,2 @@
115 | +server
116 | +browser
117 | diff --git a/.meteor/release b/.meteor/release
118 | new file mode 100644
119 | index 0000000..621e94f
120 | --- /dev/null
121 | +++ b/.meteor/release
122 | @@ -0,0 +1 @@
123 | +none
124 | diff --git a/.meteor/versions b/.meteor/versions
125 | new file mode 100644
126 | index 0000000..0ee9e95
127 | --- /dev/null
128 | +++ b/.meteor/versions
129 | @@ -0,0 +1,69 @@
130 | +allow-deny@1.0.2-rc.8
131 | +autopublish@1.0.5-rc.8
132 | +autoupdate@1.2.6-rc.8
133 | +babel-compiler@6.5.2-rc.8
134 | +babel-runtime@0.1.6-rc.8
135 | +base64@1.0.6-rc.8
136 | +binary-heap@1.0.6-rc.8
137 | +blaze@2.1.5-rc.8
138 | +blaze-html-templates@1.0.2-rc.8
139 | +blaze-tools@1.0.6-rc.8
140 | +boilerplate-generator@1.0.6-rc.8
141 | +caching-compiler@1.0.2-rc.8
142 | +caching-html-compiler@1.0.4-rc.8
143 | +callback-hook@1.0.6-rc.8
144 | +check@1.1.2-rc.8
145 | +ddp@1.2.3-rc.8
146 | +ddp-client@1.2.3-rc.8
147 | +ddp-common@1.2.3-rc.8
148 | +ddp-server@1.2.4-rc.8
149 | +deps@1.0.10-rc.8
150 | +diff-sequence@1.0.3-rc.8
151 | +ecmascript@0.4.1-rc.8
152 | +ecmascript-runtime@0.2.8-rc.8
153 | +ejson@1.0.9-rc.8
154 | +es5-shim@4.5.8-rc.8
155 | +fastclick@1.0.9-rc.8
156 | +geojson-utils@1.0.6-rc.8
157 | +hot-code-push@1.0.2-rc.8
158 | +html-tools@1.0.7-rc.8
159 | +htmljs@1.0.7-rc.8
160 | +http@1.1.3-rc.8
161 | +id-map@1.0.5-rc.8
162 | +insecure@1.0.5-rc.8
163 | +jquery@1.11.6-rc.8
164 | +launch-screen@1.0.8-rc.8
165 | +livedata@1.0.16-rc.8
166 | +logging@1.0.10-rc.8
167 | +meteor@1.1.12-rc.8
168 | +meteor-base@1.0.2-rc.8
169 | +minifier-css@1.1.9-rc.8
170 | +minifier-js@1.1.9-rc.8
171 | +minimongo@1.0.12-rc.8
172 | +mobile-experience@1.0.2-rc.8
173 | +mobile-status-bar@1.0.10-rc.8
174 | +modules@0.5.1-rc.8
175 | +modules-runtime@0.6.1-rc.8
176 | +mongo@1.1.5-rc.8
177 | +mongo-id@1.0.2-rc.8
178 | +npm-mongo@1.4.41-rc.8
179 | +observe-sequence@1.0.9-rc.8
180 | +ordered-dict@1.0.5-rc.8
181 | +promise@0.6.4-rc.8
182 | +random@1.0.7-rc.8
183 | +reactive-var@1.0.7-rc.8
184 | +reload@1.1.6-rc.8
185 | +retry@1.0.5-rc.8
186 | +routepolicy@1.0.8-rc.8
187 | +spacebars@1.0.9-rc.8
188 | +spacebars-compiler@1.0.9-rc.8
189 | +standard-minifier-css@1.0.4-rc.8
190 | +standard-minifier-js@1.0.4-rc.8
191 | +templating@1.1.7-rc.8
192 | +templating-tools@1.0.2-rc.8
193 | +tracker@1.0.11-rc.8
194 | +ui@1.0.9-rc.8
195 | +underscore@1.0.6-rc.8
196 | +url@1.0.7-rc.8
197 | +webapp@1.2.6-rc.8
198 | +webapp-hashing@1.0.7-rc.8
199 | diff --git a/client/main.css b/client/main.css
200 | new file mode 100644
201 | index 0000000..b6b4052
202 | --- /dev/null
203 | +++ b/client/main.css
204 | @@ -0,0 +1 @@
205 | +/* CSS declarations go here */
206 | diff --git a/client/main.html b/client/main.html
207 | new file mode 100644
208 | index 0000000..203539b
209 | --- /dev/null
210 | +++ b/client/main.html
211 | @@ -0,0 +1,25 @@
212 | +
213 | + simple
214 | +
215 | +
216 | +
217 | +