├── .gitignore
├── .meteor
├── .gitignore
├── release
├── platforms
├── .id
├── .finished-upgraders
├── packages
└── versions
├── client
├── main.css
├── main.html
├── main.js
└── ui
│ ├── Hello.vue
│ ├── Post.vue
│ ├── ThreadItem.vue
│ ├── SortField.vue
│ ├── SelectedThread.vue
│ ├── App.vue
│ └── Forum.vue
├── server
└── main.js
├── imports
├── api
│ ├── collections.js
│ ├── publications.js
│ └── methods.js
├── ui
│ ├── filters.js
│ └── mixins.less
└── vuex
│ ├── store.js
│ └── modules
│ ├── thread.js
│ └── forum.js
├── package.json
├── .gitattributes
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.3.4.1
2 |
--------------------------------------------------------------------------------
/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/client/main.css:
--------------------------------------------------------------------------------
1 | /* CSS declarations go here */
2 |
--------------------------------------------------------------------------------
/server/main.js:
--------------------------------------------------------------------------------
1 | import '/imports/api/methods';
2 | import '/imports/api/publications';
3 |
--------------------------------------------------------------------------------
/client/main.html:
--------------------------------------------------------------------------------
1 |
2 | meteor-vuex-example
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/imports/api/collections.js:
--------------------------------------------------------------------------------
1 | export const Threads = new Mongo.Collection('threads')
2 | export const Posts = new Mongo.Collection('posts')
3 |
--------------------------------------------------------------------------------
/imports/ui/filters.js:
--------------------------------------------------------------------------------
1 | import { Vue } from 'meteor/akryum:vue';
2 | import moment from 'moment';
3 |
4 | Vue.filter('dateFromNow', (date) => {
5 | return moment(date).fromNow();
6 | });
7 |
--------------------------------------------------------------------------------
/imports/ui/mixins.less:
--------------------------------------------------------------------------------
1 | .padding(@px) {
2 | padding: @px;
3 | }
4 |
5 | .margin(@px) {
6 | margin: @px;
7 | }
8 |
9 | .border-radius(@px) {
10 | border-radius: @px;
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "meteor-vuex-example",
3 | "private": true,
4 | "scripts": {
5 | "start": "meteor run"
6 | },
7 | "dependencies": {
8 | "meteor-node-stubs": "~0.2.0"
9 | },
10 | "devDependencies": {
11 | "moment": "^2.14.1",
12 | "vue": "^1.0.24",
13 | "vuex": "^1.0.0-rc.2"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | ad1q0e1puof3110gh2de
8 |
--------------------------------------------------------------------------------
/client/main.js:
--------------------------------------------------------------------------------
1 | // Libs
2 | import {Meteor} from 'meteor/meteor';
3 | import {Vue} from 'meteor/akryum:vue';
4 |
5 | // Api
6 | import '/imports/api/methods';
7 |
8 | // Filters
9 | import '/imports/ui/filters';
10 |
11 | // Main app
12 | Meteor.startup(() => {
13 | new Vue({
14 | el: 'body',
15 | replace: false
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/imports/api/publications.js:
--------------------------------------------------------------------------------
1 | import {Meteor} from 'meteor/meteor';
2 | import {Threads, Posts} from './collections';
3 |
4 | Meteor.publish('threads', function() {
5 | return Threads.find();
6 | });
7 |
8 | Meteor.publish('posts', function(thread_id) {
9 | check(thread_id, String);
10 |
11 | return Posts.find({
12 | thread_id
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 | 1.3.0-split-minifiers-package
14 |
--------------------------------------------------------------------------------
/client/ui/Hello.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Hello {{name}}
4 |
5 |
6 |
7 |
8 |
26 |
27 |
36 |
--------------------------------------------------------------------------------
/client/ui/Post.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
17 |
18 |
19 |
37 |
--------------------------------------------------------------------------------
/imports/vuex/store.js:
--------------------------------------------------------------------------------
1 | import {StoreModule} from 'meteor/akryum:vuex';
2 |
3 | const root = new StoreModule();
4 |
5 | // Add some initial state
6 | root.addState({
7 | name: 'world'
8 | });
9 |
10 | // Using centralized getters is good practice
11 | // They are also cached by vue just like computed props
12 | root.addGetters({
13 | name: state => state.name
14 | });
15 |
16 | // Only mutations can change the store state
17 | root.addMutations({
18 | NAME(state, text) {
19 | state.name = text;
20 | }
21 | });
22 |
23 | // Using centralized actions is good practice
24 | root.addActions({
25 | setName({store}, text) {
26 | // state is immutable
27 | store.dispatch('NAME', text);
28 | }
29 | });
30 |
31 | // Submodule
32 | import forum from './modules/forum';
33 | root.addModule(forum);
34 |
35 | // Export the vuex native store
36 | export const store = root.exportStore();
37 |
--------------------------------------------------------------------------------
/client/ui/ThreadItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{data.name}}
4 | {{data.date | dateFromNow}}
5 |
6 |
7 |
8 |
25 |
26 |
50 |
--------------------------------------------------------------------------------
/client/ui/SortField.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{fieldDisplay}}
4 | {{directionDisplay}}
5 |
6 |
7 |
8 |
34 |
35 |
43 |
--------------------------------------------------------------------------------
/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-base # Packages every Meteor app needs to have
8 | mobile-experience # Packages for a great mobile UX
9 | mongo # The database Meteor supports right now
10 | reactive-var # Reactive variable for tracker
11 | jquery # Helpful client-side library
12 | tracker # Meteor's client-side reactive programming library
13 |
14 | standard-minifier-css # CSS minifier run for production mode
15 | standard-minifier-js # JS minifier run for production mode
16 | es5-shim # ECMAScript 5 compatibility for older browsers.
17 | ecmascript # Enable ECMAScript2015+ syntax in app code
18 |
19 | static-html
20 | akryum:vue
21 | akryum:vue-component
22 | akryum:vuex
23 | akryum:vue-less
24 | check
25 |
--------------------------------------------------------------------------------
/imports/api/methods.js:
--------------------------------------------------------------------------------
1 | import {Meteor} from 'meteor/meteor';
2 | import {Threads, Posts} from './collections';
3 |
4 | Meteor.methods({
5 | 'threads.create': function(name) {
6 | check(name, String);
7 |
8 | let _id = Threads.insert({
9 | name,
10 | created: new Date(),
11 | date: new Date()
12 | })
13 |
14 | return _id;
15 | },
16 | 'threads.remove': function(_id) {
17 | check(_id, String);
18 | Posts.remove({
19 | thread_id: _id
20 | });
21 | Threads.remove(_id);
22 | },
23 | 'posts.create': function(thread_id, message) {
24 | check(thread_id, String);
25 | check(message, String);
26 |
27 | let thread = Threads.findOne(thread_id);
28 | if(!thread) {
29 | throw new Meteor.Error('Thread not found');
30 | }
31 |
32 | let _id = Posts.insert({
33 | thread_id,
34 | message,
35 | created: new Date()
36 | });
37 |
38 | Threads.update(thread_id, {
39 | $set: {
40 | date: new Date()
41 | }
42 | });
43 |
44 | return _id;
45 | },
46 | 'posts.remove': function(_id) {
47 | check(_id, String);
48 | Posts.remove(_id);
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/client/ui/SelectedThread.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{selectedThread.name}}
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
50 |
--------------------------------------------------------------------------------
/client/ui/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple meteor app with vuex
2 |
3 | A simple meteor project featuring [vue](https://vuejs.org/) as ui layer ([more info](https://github.com/Akryum/meteor-vue-component)), with [state management](https://github.com/Akryum/meteor-vue-component/tree/master/packages/vuex).
4 |
5 | ## Steps to reproduce
6 |
7 | In the console, create the project and add the relevant packages:
8 |
9 | meteor create meteor-vuex-example
10 | cd ./meteor-vuex-example
11 | meteor remove blaze-html-templates autopublish insecure
12 | meteor add static-html check akryum:vue akryum:vue-component akryum:vuex akryum:vue-less
13 | meteor
14 |
15 | All the required npm dependencies will be automatically added to your project's `package.json` and installed with `meteor npm install`.
16 |
17 | Replace the `client/main.html` file with:
18 |
19 | ```html
20 |
21 | meteor-vuex-example
22 |
23 |
24 |
25 |
26 |
27 | ```
28 |
29 | Replace the `client/main.js` file with:
30 |
31 | ```javascript
32 | // Libs
33 | import {Meteor} from 'meteor/meteor';
34 | import {Vue} from 'meteor/akryum:vue';
35 |
36 | // Main app
37 | import App from '/imports/ui/App.vue';
38 |
39 | Meteor.startup(() => {
40 | new Vue({
41 | el: 'body',
42 | replace: false,
43 | components: {
44 | App
45 | }
46 | });
47 | });
48 | ```
49 |
50 | Create store modules and add components with `vuex` options ([more info](https://github.com/Akryum/meteor-vue-component/tree/master/packages/vuex#usage)).
51 |
--------------------------------------------------------------------------------
/client/ui/Forum.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Forum ({{count}})
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
55 |
--------------------------------------------------------------------------------
/.meteor/versions:
--------------------------------------------------------------------------------
1 | akryum:npm-check@0.0.3
2 | akryum:vue@1.0.3
3 | akryum:vue-component@0.5.1
4 | akryum:vue-component-dev-client@0.0.5
5 | akryum:vue-component-dev-server@0.0.1
6 | akryum:vue-less@0.0.3
7 | akryum:vuex@0.3.0
8 | allow-deny@1.0.5
9 | autoupdate@1.2.10
10 | babel-compiler@6.8.3
11 | babel-runtime@0.1.9_1
12 | base64@1.0.9
13 | binary-heap@1.0.9
14 | blaze@2.1.8
15 | blaze-tools@1.0.9
16 | boilerplate-generator@1.0.9
17 | caching-compiler@1.0.5_1
18 | caching-html-compiler@1.0.6
19 | callback-hook@1.0.9
20 | check@1.2.3
21 | ddp@1.2.5
22 | ddp-client@1.2.8_1
23 | ddp-common@1.2.6
24 | ddp-server@1.2.8_1
25 | deps@1.0.12
26 | diff-sequence@1.0.6
27 | ecmascript@0.4.6_1
28 | ecmascript-runtime@0.2.11_1
29 | ejson@1.0.12
30 | es5-shim@4.5.12_1
31 | fastclick@1.0.12
32 | geojson-utils@1.0.9
33 | hot-code-push@1.0.4
34 | html-tools@1.0.10
35 | htmljs@1.0.10
36 | http@1.1.7
37 | id-map@1.0.8
38 | jquery@1.11.9
39 | launch-screen@1.0.12
40 | livedata@1.0.18
41 | logging@1.0.13_1
42 | meteor@1.1.15_1
43 | meteor-base@1.0.4
44 | minifier-css@1.1.12_1
45 | minifier-js@1.1.12_1
46 | minimongo@1.0.17
47 | mobile-experience@1.0.4
48 | mobile-status-bar@1.0.12
49 | modules@0.6.4
50 | modules-runtime@0.6.4_1
51 | mongo@1.1.9_1
52 | mongo-id@1.0.5
53 | npm-mongo@1.4.44_1
54 | observe-sequence@1.0.12
55 | ordered-dict@1.0.8
56 | promise@0.7.2_1
57 | random@1.0.10
58 | reactive-var@1.0.10
59 | reload@1.1.10
60 | retry@1.0.8
61 | routepolicy@1.0.11
62 | spacebars@1.0.12
63 | spacebars-compiler@1.0.12
64 | standard-minifier-css@1.0.7_1
65 | standard-minifier-js@1.0.7_1
66 | static-html@1.0.10_1
67 | templating-tools@1.0.4
68 | tracker@1.0.14
69 | ui@1.0.11
70 | underscore@1.0.9
71 | url@1.0.10
72 | webapp@1.2.9_1
73 | webapp-hashing@1.0.9
74 |
--------------------------------------------------------------------------------
/imports/vuex/modules/thread.js:
--------------------------------------------------------------------------------
1 | import { StoreSubModule } from 'meteor/akryum:vuex';
2 |
3 | const subModule = new StoreSubModule('thread');
4 |
5 | subModule.addState({
6 | selectedThreadId: null
7 | });
8 |
9 | subModule.addGetters({
10 | selectedThreadId: sate => sate.selectedThreadId
11 | });
12 |
13 | subModule.addMutations({
14 | FORUM_SELECTED_THREAD_ID(state, id) {
15 | state.selectedThreadId = id;
16 | }
17 | });
18 |
19 | subModule.addActions({
20 | selectThread({store}, id) {
21 | // state is immutable
22 | store.dispatch('FORUM_SELECTED_THREAD_ID', id);
23 | },
24 | createThread(_, name) {
25 | return this.callMethod('threads.create', name, (err, result) => {
26 | if(err) {
27 | console.error(err);
28 | } else {
29 | // Call another action on the submodule
30 | this.actions.selectThread(result);
31 | }
32 | });
33 | },
34 | removeThread ({state}) {
35 | return this.callMethod('threads.remove', state.selectedThreadId);
36 | },
37 | createPost ({state}, msg) {
38 | return this.callMethod('posts.create', state.selectedThreadId, msg)
39 | },
40 | removePost(_, id) {
41 | return this.callMethod('posts.remove', id)
42 | }
43 | });
44 |
45 | // Meteor integration
46 |
47 | // Import meteor collections
48 | import {Threads, Posts} from '/imports/api/collections';
49 |
50 | subModule.addTrackers({
51 | selectedThread() {
52 | let sub;
53 | return {
54 | init(data) {
55 | data.selectedThread = null;
56 | data.posts = [];
57 | },
58 | watch(state) {
59 | // Dynamic subscription
60 | if(sub) {
61 | sub.stop();
62 | }
63 | if(state.selectedThreadId) {
64 | sub = Meteor.subscribe('posts', state.selectedThreadId);
65 | console.log('subscribed posts to thread ', state.selectedThreadId);
66 | }
67 |
68 | return {
69 | id: state.selectedThreadId
70 | }
71 | },
72 | update(data, {id}) {
73 | data.selectedThread = Object.freeze(Threads.findOne({
74 | _id: id
75 | }));
76 | data.posts = Object.freeze(Posts.find({
77 | thread_id: id
78 | }, {
79 | sort: {created: -1}
80 | }).fetch());
81 | console.log('posts', data.posts);
82 | },
83 | getters: {
84 | getSelectedThread: data => data.selectedThread,
85 | getPosts: data => data.posts
86 | }
87 | }
88 | }
89 | })
90 |
91 | export default subModule;
92 |
--------------------------------------------------------------------------------
/imports/vuex/modules/forum.js:
--------------------------------------------------------------------------------
1 | import {StoreSubModule} from 'meteor/akryum:vuex';
2 |
3 | const subModule = new StoreSubModule('forum');
4 |
5 | const sortFields = ['date', 'name'];
6 |
7 | subModule.addState({
8 | sortDirection: -1,
9 | sortField: sortFields[0]
10 | });
11 |
12 | subModule.addGetters({
13 | sortDirection: state => state.sortDirection,
14 | sortField: state => state.sortField
15 | });
16 |
17 | subModule.addMutations({
18 | FORUM_SORT_DIRECTION(state, direction) {
19 | state.sortDirection = direction;
20 | },
21 | FORUM_SORT_FIELD(state, field) {
22 | state.sortField = field;
23 | }
24 | });
25 |
26 | subModule.addActions({
27 | toggleSortDirection({store, state}) {
28 | // state is immutable
29 | store.dispatch('FORUM_SORT_DIRECTION', -1*state.sortDirection);
30 | },
31 | cycleSortField({store, state}) {
32 | // state is immutable
33 | let index = sortFields.indexOf(state.sortField) + 1;
34 | if(index === sortFields.length) {
35 | index = 0;
36 | }
37 | store.dispatch('FORUM_SORT_FIELD', sortFields[index]);
38 | }
39 | });
40 |
41 | // Meteor integration
42 |
43 | // Import a meteor collection
44 | import {Threads} from '/imports/api/collections';
45 |
46 | // Add trackers to the store module
47 | subModule.addTrackers({
48 | // Name of the tracker
49 | threads() {
50 | // Context variables
51 | let sub;
52 |
53 | // You can execute arbitrary code here
54 |
55 | return {
56 | // Initialize the meteor data
57 | init(data) {
58 | data.threads = []
59 | },
60 | // When the tracker is being used
61 | activate() {
62 | // Subscribe to the publication
63 | sub = Meteor.subscribe('threads');
64 | },
65 | // When the tracker is no longer used
66 | deactivate() {
67 | // Stop the subscription
68 | sub.stop();
69 | },
70 | // Watch store changes
71 | // State is relative to the module
72 | watch(state) {
73 | // state is immutable
74 | return {
75 | sortDirection: state.sortDirection,
76 | sortField: state.sortField
77 | }
78 | },
79 | // Update the meteor data
80 | // Data is relative to the module
81 | update(data, {sortDirection, sortField}) {
82 | // Meteor data query
83 | let threads = Threads.find({}, {
84 | sort: {
85 | [sortField]: sortDirection
86 | }
87 | }).fetch();
88 |
89 | // Update the module meteor data
90 | data.threads = Object.freeze(threads);
91 | },
92 | // Getters
93 | // These are computed properties and are cached by vue
94 | getters: {
95 | // Getters should follow the get naming convention
96 | getThreads: data => data.threads
97 | },
98 | // If true, the tracker will be activated right away
99 | // Else, you need to add it on a vue component or call tracker.addClient()
100 | isActivated: false
101 | }
102 | }
103 | });
104 |
105 |
106 | // Nested Submodule
107 | import thread from './thread';
108 | subModule.addModule(thread);
109 |
110 | export default subModule;
111 |
--------------------------------------------------------------------------------