diff --git a/README.md b/README.md index 2078e10..c6d3eda 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Feel free to submit a pull request to improve the content! 1. [Blaze tutorial](https://www.meteor.com/tutorials/blaze/creating-an-app): [`/content/blaze`](https://github.com/meteor/tutorials/tree/master/content/blaze) 2. [Angular tutorial](https://www.meteor.com/tutorials/angular/creating-an-app): [`/content/angular`](https://github.com/meteor/tutorials/tree/master/content/angular) 3. [React tutorial](https://www.meteor.com/tutorials/react/creating-an-app): [`/content/react`](https://github.com/meteor/tutorials/tree/master/content/react) +4. [Vue tutorial](https://www.meteor.com/tutorials/vue/creating-an-app): [`/content/vue`](https://github.com/meteor/tutorials/tree/master/content/vue) ### Tutorial step-by-step repositories @@ -17,6 +18,7 @@ We also maintain all of the tutorials as step-by-step git repositories here: 1. [Blaze](https://github.com/meteor/simple-todos) 2. [Angular](https://github.com/meteor/simple-todos-angular) 3. [React](https://github.com/meteor/simple-todos-react) +4. [Vue](https://github.com/meteor/simple-todos-vue) ### Tutorial viewer diff --git a/content/react/metadata.js b/content/react/metadata.js index 9042fda..9169964 100644 --- a/content/react/metadata.js +++ b/content/react/metadata.js @@ -8,7 +8,7 @@ if (Meteor.isClient) { TutorialRegistry.registerTutorial("react", { title: "Simple Todos React", subtitle: "Learn how to use Meteor and React together", - tutorialSourceLink: "github.com/meteor/tutorials/content/angular", + tutorialSourceLink: "github.com/meteor/tutorials/content/react", steps: [ { title: "Creating an app", diff --git a/content/vue/metadata.js b/content/vue/metadata.js new file mode 100644 index 0000000..4e28a8f --- /dev/null +++ b/content/vue/metadata.js @@ -0,0 +1,74 @@ +if (Meteor.isClient) { + DiffBox.registerTutorial("simple-todos-vue", { + gitHubRepoName: "meteor/simple-todos-vue", + patchFilename: "generated/vue.multi.patch" + }); +} + +TutorialRegistry.registerTutorial("vue", { + title: "Simple Todos Vue", + subtitle: "Learn how to use Meteor and Vue together", + tutorialSourceLink: "github.com/meteor/tutorials/content/vue", + steps: [ + { + title: "Creating an app", + slug: "creating-an-app", + template: "sharedStep01" + }, + { + title: 'Components', + slug: "components", + template: 'vue-step02' + }, + { + title: 'Collections', + slug: "collections", + template: 'vue-step03' + }, + { + title: 'Forms and events', + slug: "forms-and-events", + template: 'vue-step04' + }, + { + title: 'Update and remove', + slug: "update-and-remove", + template: 'vue-step05' + }, + { + title: 'Running on mobile', + slug: "running-on-mobile", + template: "sharedStep06" + }, + { + title: 'Temporary UI state', + slug: "temporary-ui-state", + template: 'vue-step07' + }, + { + title: 'Adding user accounts', + slug: "adding-user-accounts", + template: 'vue-step08' + }, + { + title: 'Security with methods', + slug: "security-with-methods", + template: 'vue-step09' + }, + { + title: 'Publish and subscribe', + slug: "publish-and-subscribe", + template: 'vue-step10' + }, + { + title: 'Testing', + slug: "testing", + template: 'vue-step11' + }, + { + title: 'Next steps', + slug: "next-steps", + template: 'vue-step12' + } + ] +}); diff --git a/content/vue/step02.md b/content/vue/step02.md new file mode 100644 index 0000000..01d8f77 --- /dev/null +++ b/content/vue/step02.md @@ -0,0 +1,77 @@ +{{#template name="vue-step02"}} +# Defining views with React components + +To start working with React as our view library, let's add some NPM packages which will allow us to get started with React. + +Open a new terminal in the same directory as your running app, and type: + +```sh +meteor npm install --save react react-dom +``` + +> Note: `meteor npm` supports the same features as `npm`, though the difference can be important. Consult the [`meteor npm` documentation](https://docs.meteor.com/commandline.html#meteornpm) for more information. + +### Replace the starter code + +To get started, let's replace the code of the default starter app. Then we'll talk about what it does. + +First, replace the content of the initial HTML file: + +{{> DiffBox tutorialName="simple-todos-react" step="2.2"}} + +Second, replace the contents of **`client/main.js`** with: + +{{> DiffBox tutorialName="simple-todos-react" step="2.3"}} + +Now we need to create a new directory called `imports`, a specially-named directory which will behave differently than other directories in the project. Files outside the `imports` directory will be loaded automatically when the Meteor server starts, while files inside the `imports` directory will only load when an `import` statement is used to load them. + +After creating the `imports` directory, we will create two new files inside it: + +{{> DiffBox tutorialName="simple-todos-react" step="2.4"}} + +{{> DiffBox tutorialName="simple-todos-react" step="2.5"}} + +We just added three things to our app: + +1. An `App` React component +2. A `Task` React component +3. Some initialization code (in our `client/main.js` client JavaScript entrypoint), in a `Meteor.startup` block, which knows how to call code when the page is loaded and ready. This code loads the other components and renders them into the `#render-target` html element. + +You can read more about how imports work and how to structure your code in the [Application Structure article](http://guide.meteor.com/structure.html) of the Meteor Guide. + +Later in the tutorial, we will refer to these components when adding or changing code. + +### Check the result + +In our browser, the app should **roughly** look like the following (though much less pretty): + +> #### Todo List +> - This is task 1 +> - This is task 2 +> - This is task 3 + +If your app doesn't look like this, use the GitHub link at the top right corner of each code snippet to see the entire file, and make sure your code matches the example. + +### HTML files define static content + +Meteor parses all of the HTML files in your app folder and identifies three top-level tags: **<head>**, **<body>**, and **<template>**. + +Everything inside any <head> tags is added to the `head` section of the HTML sent to the client, and everything inside <body> tags is added to the `body` section, just like in a regular HTML file. + +Everything inside <template> tags is compiled into Meteor _templates_, which can be included inside HTML with `{{dstache}}> templateName}}` or referenced in your JavaScript with `Template.templateName`. In this tutorial, we won't be using this feature of Meteor because we will be defining all of our view components with React. + +### Define view components with React + +In React, view components are subclasses of `React.Component` (which we import with `import { Component } from 'react';`). Your component can have any methods you like, but there are several methods such as `render` that have special functions. Components can also receive data from their parents through attributes called `props`. We'll go over some of the more common features of React in this tutorial; you can also check out [Facebook's React tutorial](https://facebook.github.io/react/docs/tutorial.html). + +### Return markup from the render method with JSX + +The most important method in every React component is `render()`, which is called by React to get a description of the HTML that this component should display. The HTML content is written using a JavaScript extension called JSX, which kind of looks like writing HTML inside your JavaScript. You can see some obvious differences already: in JSX, you use the `className` attribute instead of `class`. An important thing to know about JSX is that it isn't a templating language like Spacebars or Angular - it actually compiles directly to regular JavaScript. Read more about JSX [in the React docs](https://facebook.github.io/react/docs/jsx-in-depth.html). + +JSX is supported by the `ecmascript` Atmosphere package, which is included in all new Meteor apps by default. + +{{> DiffBox tutorialName="simple-todos-react" step="2.6"}} + +Now that you've added the CSS, the app should look a lot nicer. Check in your browser to see that the new styles have loaded. + +{{/template}} diff --git a/content/vue/step03.md b/content/vue/step03.md new file mode 100644 index 0000000..fb8a09d --- /dev/null +++ b/content/vue/step03.md @@ -0,0 +1,25 @@ +{{#template name="vue-step03"}} + +# Storing tasks in a collection + +{{> step03CollectionsIntro tutorialName="simple-todos-react"}} + +### Using data from a collection inside a React component + +To use data from a Meteor collection inside a React component, we can use an Atmosphere package `react-meteor-data` which allows us to create a "data container" to feed Meteor's reactive data into React's component hierarchy. + +```bash +meteor add react-meteor-data +``` + +To use `react-meteor-data`, we need to wrap our component in a *container* using the `withTracker` Higher Order Component: + +{{> DiffBox step="3.4" tutorialName="simple-todos-react"}} + +The wrapped `App` component fetches tasks from the `Tasks` collection and supplies them to the underlying `App` component it wraps as the `tasks` prop. It does this in a reactive way, so that when the contents of the database change, the `App` re-renders, as we'll soon see! + +When you make these changes to the code, you'll notice that the tasks that used to be in the todo list have disappeared. That's because our database is currently empty — we need to insert some tasks! + +{{> step03InsertingTasksFromConsole}} + +{{/template}} diff --git a/content/vue/step04.md b/content/vue/step04.md new file mode 100644 index 0000000..685e050 --- /dev/null +++ b/content/vue/step04.md @@ -0,0 +1,42 @@ +{{#template name="vue-step04"}} + +# Adding tasks with a form + +In this step, we'll add an input field for users to add tasks to the list. + +First, let's add a form to our `App` component: + +{{> DiffBox step="4.1" tutorialName="simple-todos-react"}} + +> Tip: You can add comments to your JSX code by wrapping them in `{/* ... */}` + +You can see that the `form` element has an `onSubmit` attribute that references a method on the component called `handleSubmit`. In React, this is how you listen to browser events, like the submit event on the form. The `input` element has a `ref` property which will let us easily access this element later. + +Let's add a `handleSubmit` method to our `App` component: + +{{> DiffBox step="4.2" tutorialName="simple-todos-react"}} + +Now your app has a new input field. To add a task, just type into the input field and hit enter. If you open a new browser window and open the app again, you'll see that the list is automatically synchronized between all clients. + +### Listening for events in React + +As you can see, in React you handle DOM events by directly referencing a method on the component. Inside the event handler, you can reference elements from the component by giving them a `ref` property and using `ReactDOM.findDOMNode`. Read more about the different kinds of events React supports, and how the event system works, in the [React docs](https://facebook.github.io/react/docs/events.html). + +### Inserting into a collection + +Inside the event handler, we are adding a task to the `tasks` collection by calling `Tasks.insert()`. We can assign any properties to the task object, such as the time created, since we don't ever have to define a schema for the collection. + +Being able to insert anything into the database from the client isn't very secure, but it's okay for now. In step 10 we'll learn how we can make our app secure and restrict how data is inserted into the database. + +### Sorting our tasks + +Currently, our code displays all new tasks at the bottom of the list. That's not very good for a task list, because we want to see the newest tasks first. + +We can solve this by sorting the results using the `createdAt` field that is automatically added by our new code. Just add a sort option to the `find` call inside the data container wrapping the `App` component: + +{{> DiffBox step="4.3" tutorialName="simple-todos-react"}} + +Let's go back to the browser and make sure this worked: any new tasks that you add should appear at the top of the list, rather than at the bottom. + +In the next step, we'll add some very important todo list features: checking off and deleting tasks. +{{/template}} diff --git a/content/vue/step05.md b/content/vue/step05.md new file mode 100644 index 0000000..ba9c8ca --- /dev/null +++ b/content/vue/step05.md @@ -0,0 +1,23 @@ +{{#template name="vue-step05"}} + +# Checking off and deleting tasks + +Until now, we have only interacted with a collection by inserting documents. Now, we will learn how to update and remove them. + +Let's add two new elements to our `task` component, a checkbox and a delete button, with event handlers for both: + +{{> DiffBox step="5.1" tutorialName="simple-todos-react"}} + +### Update + +In the code above, we call `Tasks.update` to check off a task. + +The `update` function on a collection takes two arguments. The first is a selector that identifies a subset of the collection, and the second is an update parameter that specifies what should be done to the matched objects. + +In this case, the selector is just the `_id` of the relevant task. The update parameter uses `$set` to toggle the `checked` field, which will represent whether the task has been completed. + +### Remove + +The code from above uses `Tasks.remove` to delete a task. The `remove` function takes one argument, a selector that determines which item to remove from the collection. + +{{/template}} diff --git a/content/vue/step07.md b/content/vue/step07.md new file mode 100644 index 0000000..5b84d74 --- /dev/null +++ b/content/vue/step07.md @@ -0,0 +1,33 @@ +{{#template name="vue-step07"}} + +# Storing temporary UI data in component state + +In this step, we'll add a client-side data filtering feature to our app, so that users can check a box to only see incomplete tasks. We're going to learn how to use React's component state to store temporary information that is only used on the client. + +First, we need to add a checkbox to our `App` component: + +{{> DiffBox step="7.1" tutorialName="simple-todos-react"}} + +You can see that it reads from `this.state.hideCompleted`. React components have a special field called `state` where you can store encapsulated component data. We'll need to initialize the value of `this.state.hideCompleted` in the component's constructor: + +{{> DiffBox step="7.2" tutorialName="simple-todos-react"}} + +We can update `this.state` from an event handler by calling `this.setState`, which will update the state property asynchronously and then cause the component to re-render: + +{{> DiffBox step="7.3" tutorialName="simple-todos-react"}} + +Now, we need to update our `renderTasks` function to filter out completed tasks when `this.state.hideCompleted` is true: + +{{> DiffBox step="7.4" tutorialName="simple-todos-react"}} + +Now if you check the box, the task list will only show tasks that haven't been completed. + +### One more feature: Showing a count of incomplete tasks + +Now that we have written a query that filters out completed tasks, we can use the same query to display a count of the tasks that haven't been checked off. To do this we need to fetch a count in our data container and add a line to our `render` method. Since we already have the data in the client-side collection, adding this extra count doesn't involve asking the server for anything. + +{{> DiffBox step="7.5" tutorialName="simple-todos-react"}} + +{{> DiffBox step="7.6" tutorialName="simple-todos-react"}} + +{{/template}} diff --git a/content/vue/step08.md b/content/vue/step08.md new file mode 100644 index 0000000..4505343 --- /dev/null +++ b/content/vue/step08.md @@ -0,0 +1,74 @@ +{{#template name="vue-step08"}} + +# Adding user accounts + +Meteor comes with an accounts system and a drop-in login user interface that lets you add multi-user functionality to your app in minutes. + +> Currently, this UI component uses Blaze, Meteor's default UI engine. In the future, there might also be a React-specific component for this. + +To enable the accounts system and UI, we need to add the relevant packages. In your app directory, run the following command: + +```bash +meteor add accounts-ui accounts-password +``` + +### Wrapping a Blaze component in React + +To use the Blaze UI component from the `accounts-ui` package, we need to wrap it in a React component. To do so, let's create a new component called `AccountsUIWrapper` in a new file: + +{{> DiffBox step="8.2" tutorialName="simple-todos-react"}} + +Let's include the component we just defined inside App: + +{{> DiffBox step="8.3" tutorialName="simple-todos-react"}} + +Then, add the following code to configure the accounts UI to use usernames instead of email addresses: + +{{> DiffBox step="8.4" tutorialName="simple-todos-react"}} + +We also need to import that configuration code in our client side entrypoint: + +{{> DiffBox step="8.5" tutorialName="simple-todos-react"}} + +### Adding user-related functionality + +Now users can create accounts and log into your app! This is very nice, but logging in and out isn't very useful yet. Let's add two features: + +1. Only display the new task input field to logged in users +2. Show which user created each task + +To do this, we will add two new fields to the `tasks` collection: + +1. `owner` - the `_id` of the user that created the task. +2. `username` - the `username` of the user that created the task. We will save the username directly in the task object so that we don't have to look up the user every time we display the task. + +First, let's add some code to save these fields into the `handleSubmit` event handler: + +{{> DiffBox step="8.6" tutorialName="simple-todos-react"}} + +Modify the data container to get information about the currently logged in user: + +{{> DiffBox step="8.7" tutorialName="simple-todos-react"}} + +Then, in our render method, add a conditional statement to only show the form when there is a logged in user: + +{{> DiffBox step="8.8" tutorialName="simple-todos-react"}} + +Finally, add a statement to display the `username` field on each task right before the text: + +{{> DiffBox step="8.9" tutorialName="simple-todos-react"}} + +In your browser, add some tasks and notice that your username shows up. Old tasks that we added before this step won't have usernames attached; you can just delete them. + +Now, users can log in and we can track which user each task belongs to. Let's look at some of the concepts we just discovered in more detail. + +### Automatic accounts UI + +If our app has the `accounts-ui` package, all we have to do to add a login dropdown is render the included UI component. This dropdown detects which login methods have been added to the app and displays the appropriate controls. In our case, the only enabled login method is `accounts-password`, so the dropdown displays a password field. If you are adventurous, you can add the `accounts-facebook` package to enable Facebook login in your app - the Facebook button will automatically appear in the dropdown. + +### Getting information about the logged-in user + +In your data container, you can use `Meteor.user()` to check if a user is logged in and get information about them. For example, `Meteor.user().username` contains the logged in user's username. You can also use `Meteor.userId()` to just get the current user's `_id`. + +In the next step, we will learn how to make our app more secure by doing data validation on the server. +{{/template}} diff --git a/content/vue/step09.md b/content/vue/step09.md new file mode 100644 index 0000000..104394a --- /dev/null +++ b/content/vue/step09.md @@ -0,0 +1,37 @@ +{{#template name="vue-step09"}} + +# Security with methods + +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. + +### Removing `insecure` + +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: + +```bash +meteor remove insecure +``` + +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. + +### Defining methods + +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_. + +{{> DiffBox step="9.2" tutorialName="simple-todos-react"}} + +Now that we have defined our methods, we need to update the places we were operating on the collection to use the methods instead: + +{{> DiffBox step="9.3" tutorialName="simple-todos-react"}} + +{{> DiffBox step="9.4" tutorialName="simple-todos-react"}} + +Now all of our inputs and buttons will start working again. What did we gain from all of this work? + +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. +2. We can add extra validation logic to `setChecked` and `deleteTask` in later steps when users can make tasks private. +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. + +{{> step09OptimisticUI}} + +{{/template}} diff --git a/content/vue/step10.md b/content/vue/step10.md new file mode 100644 index 0000000..12454b2 --- /dev/null +++ b/content/vue/step10.md @@ -0,0 +1,78 @@ +{{#template name="vue-step10"}} + +# Filtering data with publish and subscribe + +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. + +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: + +```bash +meteor remove autopublish +``` + +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`. + +First lets add a publication for all tasks: + +{{> DiffBox step="10.2" tutorialName="simple-todos-react"}} + +And then let's subscribe to that publication when the `App` component is created: + +{{> DiffBox step="10.3" tutorialName="simple-todos-react"}} + +Once you have added this code, all of the tasks will reappear. + +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. + +### Adding a button to make tasks private + +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. + +First, we need to add a new method that we can call to set a task's private status: + +{{> DiffBox step="10.4" tutorialName="simple-todos-react"}} + +Now, we need to pass a new property to the `Task` to decide whether we want +to show the private button; the button should show up only if the currently +logged in user owns this task: + +{{> DiffBox step="10.5" tutorialName="simple-todos-react"}} + +Let's add the button, using this new prop to decide whether it should be displayed: + +{{> DiffBox step="10.7" tutorialName="simple-todos-react"}} + +We need to define the event handler called by the button: + +{{> DiffBox step="10.8" tutorialName="simple-todos-react"}} + +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` NPM Package for this: + +```bash +meteor npm install --save classnames +``` + +Then we'll use that package to choose a class based on the task are rendering: + +{{> DiffBox step="10.10" tutorialName="simple-todos-react"}} + +### Selectively publishing tasks based on privacy status + +Now that we have a way of setting which tasks are private, we should modify our +publication function to only send the tasks that a user is authorized to see: + +{{> DiffBox step="10.11" tutorialName="simple-todos-react"}} + +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! + +### Extra method security + +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: + +{{> DiffBox step="10.12" tutorialName="simple-todos-react"}} + +> 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. + +We're done with our private task feature! Now our app is secure from attackers trying to view or modify someone's private tasks. + +{{/template}} diff --git a/content/vue/step11.md b/content/vue/step11.md new file mode 100644 index 0000000..4349e40 --- /dev/null +++ b/content/vue/step11.md @@ -0,0 +1,40 @@ +{{#template name="vue-step11"}} + +# Testing + +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. + +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. + +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: + +```bash +meteor add meteortesting:mocha +meteor npm install --save-dev chai +``` + +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`): + +```bash +TEST_WATCH=1 meteor test --driver-package meteortesting:mocha +``` + +If you do so, you should see a `0 passing` message in your console window. + +Let's add a simple test (that doesn't do anything yet): + +{{> DiffBox tutorialName="simple-todos-react" step="11.2"}} + +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: + +{{> DiffBox tutorialName="simple-todos-react" step="11.3"}} + +Here we create a single task that's associated with a random `userId` that'll be different for each test run. + +Now we can write the test to call the `tasks.remove` method "as" that user and verify the task is deleted: + +{{> DiffBox tutorialName="simple-todos-react" step="11.4"}} + +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). + +{{/template}} diff --git a/content/vue/step12.md b/content/vue/step12.md new file mode 100644 index 0000000..7cc7ff1 --- /dev/null +++ b/content/vue/step12.md @@ -0,0 +1,20 @@ +{{#template name="vue-step12"}} + +# What's next? + +Congratulations on your newly built Meteor app! + +Your app currently supports collaborating on a single todo list. To see how you +could add more functionality, check out the Todos example — a more +complete app that can handle sharing multiple lists. Also, try Local Market, a +cross-platform customer engagement app that shows off native hardware +functionality and social features. + +```bash +meteor create --example todos +meteor create --example localmarket +``` + +{{> step11NextSteps}} + +{{/template}} diff --git a/generated/vue.multi.patch b/generated/vue.multi.patch new file mode 100644 index 0000000..e69de29 diff --git a/package.js b/package.js index 2a590b8..b0e2220 100644 --- a/package.js +++ b/package.js @@ -23,6 +23,7 @@ Package.onUse(function (api) { 'content/angular/metadata.js', 'content/blaze/metadata.js', 'content/react/metadata.js', + 'content/vue/metadata.js', ]); api.addFiles([ @@ -59,6 +60,17 @@ Package.onUse(function (api) { 'content/react/step10.md', 'content/react/step11.md', 'content/react/step12.md', + + 'content/vue/step02.md', + 'content/vue/step03.md', + 'content/vue/step04.md', + 'content/vue/step05.md', + 'content/vue/step07.md', + 'content/vue/step08.md', + 'content/vue/step09.md', + 'content/vue/step10.md', + 'content/vue/step11.md', + 'content/vue/step12.md', 'content/step00.html', 'content/shared/explanations.md', @@ -70,7 +82,8 @@ Package.onUse(function (api) { 'generated/react.multi.patch', 'generated/blaze.multi.patch', - 'generated/angular.multi.patch' + 'generated/angular.multi.patch', + 'generated/vue.multi.patch' ], 'client'); // Also, exports all of the templates from the content/ directory @@ -79,4 +92,5 @@ Package.onUse(function (api) { api.export('REACT_TUT'); api.export('ANGULAR_TUT'); api.export('BLAZE_TUT'); + api.export('VUE_TUT'); });