diff --git a/.meteor/packages b/.meteor/packages index e2ab627d9..526f0e58f 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -14,6 +14,9 @@ accounts-password@1.4.0 ############ Your Packages ############ +#tutorial-step-1 - You can follow along with the video here: http://docs.vulcanjs.org/example-simple.html +# This is where you enable packages. + example-simple # example-movies # example-instagram diff --git a/packages/example-simple/lib/client/main.js b/packages/example-simple/lib/client/main.js index ae6ea31db..cd505610f 100644 --- a/packages/example-simple/lib/client/main.js +++ b/packages/example-simple/lib/client/main.js @@ -1 +1,2 @@ +// client/main.js #tutorial-step-3 - This is the file that is called into package.js. import '../modules/index.js'; diff --git a/packages/example-simple/lib/components/movies/MoviesList.jsx b/packages/example-simple/lib/components/movies/MoviesList.jsx index 7101f2a18..ff9ce6418 100644 --- a/packages/example-simple/lib/components/movies/MoviesList.jsx +++ b/packages/example-simple/lib/components/movies/MoviesList.jsx @@ -1,8 +1,13 @@ /* -List of movies. +components/MoviesList.jsx #tutorial-step-7 - +The component for our list of movies, which we called in to modules/routes.js. + Wrapped with the "withList" and "withCurrentUser" containers. +#tutorial-step-11 - +Now, we are going to look at this in a bit more detail. +This component is a React component. We only have one but it does a bunch of things... */ import React from 'react'; @@ -11,57 +16,109 @@ import { Components, withList, withCurrentUser, registerComponent } from 'meteor import Movies from '../../modules/movies/collection.js'; -const MoviesList = ({results = [], currentUser, loading, loadMore, count, totalCount}) => - -
- +{ + /* These are "props". They are variables for the component that are passed by the components parent. + In this case, to create the parent we wrapped the component in "Higer Order Compoents" (See the Higer Order Compoents section below.) + By doing this, we can pass on those props to the children of he HOCs and give them access to the props... */ +} +const MoviesList = ({ + results = [], + currentUser, + loading, + loadMore, + count, + totalCount +}) => ( +
+ {/* First, this is a Helment tag. It's a React package that loads head tags. We're using it to load the Bootstrap stylesheet. + This is not Vulcan specific but it is an easy way to add tags to the head... */} - + {/* user accounts */} -
- +
+ {/* ...This is the log in form component. It allowed you to create an account in our web app. + It takes care of all accounts stuff like changing passwords, signing in and out, and so on + Just pop this in anywhere you want to use it. It's in the Vulcan accounts package... */} -
- {loading ? - - : - + {/* ... We have a test for the loding variable (See the "Higher Order Components" section at the bottom and then the "props" section at the top.)... */} + {loading ? ( + + ) : (
- - {/* new document form */} + {/* new document form - this if for inserting new documents. Because the collection has the schema, when we generate the form, it know what the colleciton should look like + You only need to specify the colleciton. You don't need to code any of the form. Validation will work and it will show you fields based on your user permission...*/} - {Movies.options.mutations.new.check(currentUser) ? -
+ {Movies.options.mutations.new.check(currentUser) ? ( +

Insert New Document

- -
: - null - } + +
+ ) : null} - {/* documents list */} + {/* documents list - this is another small utility in Vulcan and it will display it as a card... */} - {results.map(movie => )} - - {/* load more */} + {results.map(movie => ( + + ))} - {totalCount > results.length ? - {e.preventDefault(); loadMore();}}>Load More ({count}/{totalCount}) : + {/* load more - this is the load more button. On click we trigger the loadMore function which is passed as a prop by withList... */} + + {totalCount > results.length ? ( + { + e.preventDefault(); + loadMore(); + }} + > + Load More ({count}/{totalCount}) + + ) : (

No more items.

- } - + )}
- } - + )}
+); +// ...this is where we specify how to load the data in the options object that we pass to withList +// if you want, you can specify many more options here, like how often to look for data or what fields to query from, filtering and sorting options. ... const options = { collection: Movies, limit: 5, }; +// These two functions (withList & withCurrentUser) are Higher Order Components (HOC) and by wrapping our component with this we can give it "props". (See the "props" section at the top.) registerComponent('MoviesList', MoviesList, withCurrentUser, [withList, options]); + + +// #tutorial-step-12 - Well. that's it! If you like this, go on to the movies-example, where we get more granular when it comes to data loading. +// Well thanks for tuning in! Come visit us at our chat room at slack.vulcanjs.org. See you soon! diff --git a/packages/example-simple/lib/modules/index.js b/packages/example-simple/lib/modules/index.js index 05f9c6f50..f3b30a31a 100644 --- a/packages/example-simple/lib/modules/index.js +++ b/packages/example-simple/lib/modules/index.js @@ -1,3 +1,6 @@ +// modules/index.js - #tutorial-step-8 - +// This is where we import our collections + // The Movies collection import './movies/collection.js'; diff --git a/packages/example-simple/lib/modules/movies/collection.js b/packages/example-simple/lib/modules/movies/collection.js index 085da13f3..7ffff832b 100644 --- a/packages/example-simple/lib/modules/movies/collection.js +++ b/packages/example-simple/lib/modules/movies/collection.js @@ -1,6 +1,9 @@ /* -The main Movies collection definition file. +modules/movies/collection.js - #tutorial-step-9 - +This is the main Movies collection definition file. + +A collection in VulcanJS is basically is a model, a type of data, like posts, comments, or users. */ @@ -12,17 +15,30 @@ import schema from './schema.js'; Movies collection definition +We create a new collection with the createCollection function */ const Movies = createCollection({ + // It takes the collection name... collectionName: 'Movies', + // ...the type name, which is it's name of that type's singular instance + // usually it is the same as the collection name but singular. + // It comes in useful when it is time to build our GraphQL schema... typeName: 'Movie', + // ...this is a JS schema, not GraphQL... schema, + // ...then our default resolvers and default mutations... + + // A resolver is the thing that gives you data, that fetches it in the database and sends it to the client. + // There are three default resolvers: list - for a list of documents, single - for a single document, and total - for the total number of documents that match a given query. + // You can code your own too. Check out the next example, the movies example to do so... resolvers: getDefaultResolvers('Movies'), + // A mutation is the act of changing data on the server. + // There are three default mutaitons: inserting a new document, editing an existing document, and removing a document. You can only do this if you own it. mutations: getDefaultMutations('Movies'), }); @@ -31,8 +47,11 @@ const Movies = createCollection({ Permissions for members (regular users) +...members are default users in Vulcan... + */ const membersActions = [ + // ...these are the actions that members can do... 'movies.new', 'movies.edit.own', 'movies.remove.own', @@ -43,13 +62,22 @@ Users.groups.members.can(membersActions); Default sort +This is the default sort view for this data type... + */ Movies.addDefaultView(terms => ({ options: { sort: { + // ...We want to order by when it was created. + // This gets passed to MongoDB. + // This will insert in the same order on the server and the client, + // which is how the app knew where to put our new Jaws 14 entry in the page. createdAt: -1 } } })); export default Movies; + +// There were three things I mentioned that you might not have heard of: +// schema, revolvers, and mutations. I will talk about them in the next steps. diff --git a/packages/example-simple/lib/modules/movies/schema.js b/packages/example-simple/lib/modules/movies/schema.js index bec9dc1ef..9ac92c2d2 100644 --- a/packages/example-simple/lib/modules/movies/schema.js +++ b/packages/example-simple/lib/modules/movies/schema.js @@ -1,22 +1,24 @@ /* +modules/movies/schema.js - #tutorial-step-10 - +This is a JS object that defines every property of a collection document... + A SimpleSchema-compatible JSON schema */ const schema = { - // default properties _id: { type: String, optional: true, - viewableBy: ['guests'], + viewableBy: ["guests"] }, createdAt: { type: Date, optional: true, - viewableBy: ['guests'], + viewableBy: ["guests"], onInsert: (document, currentUser) => { return new Date(); } @@ -24,45 +26,50 @@ const schema = { userId: { type: String, optional: true, - viewableBy: ['guests'], + viewableBy: ["guests"], resolveAs: { - fieldName: 'user', - type: 'User', + fieldName: "user", + type: "User", resolver: (movie, args, context) => { - return context.Users.findOne({ _id: movie.userId }, { fields: context.Users.getViewableFields(context.currentUser, context.Users) }); + return context.Users.findOne( + { _id: movie.userId }, + { fields: context.Users.getViewableFields(context.currentUser, context.Users) } + ); }, addOriginalField: true } }, - + // custom properties name: { - label: 'Name', + label: "Name", type: String, optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], + // ...these next three are interesting—they take a user group that says which group can do what action. + // ...guests are anonymous users... + viewableBy: ["guests"], + /// ...members can only edit documents that they own. This is part of the default mutations. Back to modules/movies/collection.js... + insertableBy: ["members"], + editableBy: ["members"] }, year: { - label: 'Year', + label: "Year", type: String, optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], + viewableBy: ["guests"], + insertableBy: ["members"], + editableBy: ["members"] }, review: { - label: 'Review', + label: "Review", type: String, optional: true, - control: 'textarea', - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'] - }, - + control: "textarea", + viewableBy: ["guests"], + insertableBy: ["members"], + editableBy: ["members"] + } }; export default schema; diff --git a/packages/example-simple/lib/modules/routes.js b/packages/example-simple/lib/modules/routes.js index 5236bbf3c..b532e7ae3 100644 --- a/packages/example-simple/lib/modules/routes.js +++ b/packages/example-simple/lib/modules/routes.js @@ -1,5 +1,11 @@ +// modules/routes.js #tutorial-step-6 - +// Thi is the file that is called into package.js that allows the component to be found. + +// First, we import this from vulcan core, which is a utility to add a new route. import { addRoute, Components } from 'meteor/vulcan:core'; +// Then, we add the component for what we want to add. import '../components/movies/MoviesList.jsx'; +// Next, we add the name 'movies', the path, which is the root, and the component name 'MovieList'. addRoute({ name: 'movies', path: '/', componentName: 'MoviesList' }); diff --git a/packages/example-simple/lib/server/main.js b/packages/example-simple/lib/server/main.js index 717ea74f3..3915b2103 100644 --- a/packages/example-simple/lib/server/main.js +++ b/packages/example-simple/lib/server/main.js @@ -1,2 +1,3 @@ +// server/main.js #tutorial-step-4 - This is the file that is called into package.js. import '../modules/index.js'; import './seed.js'; \ No newline at end of file diff --git a/packages/example-simple/lib/server/seed.js b/packages/example-simple/lib/server/seed.js index f579bc98e..ad5db4e6f 100644 --- a/packages/example-simple/lib/server/seed.js +++ b/packages/example-simple/lib/server/seed.js @@ -1,6 +1,7 @@ /* -Seed the database with some dummy content. +server/seed.js #tutorial-step-5 - +This is a file to seed the database with some dummy content. */ diff --git a/packages/example-simple/package.js b/packages/example-simple/package.js index 4a1481df9..e8aee3c5b 100644 --- a/packages/example-simple/package.js +++ b/packages/example-simple/package.js @@ -1,3 +1,5 @@ +// packages.js #tutorial-step-2 - Decribes the contents of the package as well as the dependencies. + Package.describe({ name: 'example-simple', }); @@ -5,6 +7,8 @@ Package.describe({ Package.onUse(function (api) { api.use([ + + // Here are our dependencies: // vulcan core 'vulcan:core@1.8.3', @@ -17,6 +21,7 @@ Package.onUse(function (api) { api.addFiles('lib/stylesheets/style.css'); + // Here is the entry point for client & server: api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client');