├── .gitignore
├── .npmignore
├── LICENSE.mit
├── README.md
├── package.json
└── src
├── form-errors.js
├── form.js
├── helpers.js
├── http.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | dist/
3 | node_modules/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | src/
3 |
--------------------------------------------------------------------------------
/LICENSE.mit:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Cody Mercer
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-laravel-forms
2 | Form helpers for [Laravel](https://laravel.com) backed [Vue.js](https://vuejs.org) projects.
3 |
4 | **Disclaimer: This plugin is still in a BETA state**
5 |
6 | ## Installation
7 | **Install package via NPM**
8 | ```
9 | npm install vue-laravel-forms
10 | ```
11 |
12 | ## Setup
13 | **Install plugin within project**
14 | ```javascript
15 | import Vue from 'vue'
16 | import { FormHelpers } from 'vue-laravel-forms'
17 |
18 | Vue.use(FormHelpers);
19 | ```
20 |
21 | or
22 |
23 | ```javascript
24 | window.Vue = require('vue');
25 | require('vue-laravel-forms');
26 | ```
27 |
28 | Alternatively, you may import the various components of this plugin separately.
29 | ```javascript
30 | import { Form, FormErrors, Http } from 'vue-laravel-forms'
31 |
32 | window.AppForm = Form;
33 | window.AppFormErrors = FormErrors;
34 |
35 | _.extend(App, new Http()) // Vue.http config not needed
36 | _.extend(App, new Http(Vue.http)) // Vue.http config needed
37 | ```
38 |
39 | ## Usage
40 | ### Creating a Form
41 | _Components installed via Vue_
42 | ```javascript
43 | Vue.component('user-registration-form', {
44 | forms: {
45 | userRegistrationForm: {
46 | name: '',
47 | email: '',
48 | password: '',
49 | password_confirmation: ''
50 | }
51 | }
52 | }
53 | ```
54 |
55 | _Components installed separately_
56 | ```javascript
57 | Vue.component('user-registration-form', {
58 | data() {
59 | return {
60 | userRegistrationForm: new AppForm({
61 | name: '',
62 | email: '',
63 | password: '',
64 | password_confirmation: '',
65 | });
66 | }
67 | }
68 | });
69 | ```
70 |
71 | ### Submitting a Form
72 | _Via a POST request (Components installed via Vue)_
73 | ```javascript
74 | Vue.component('user-registration-form', {
75 |
76 | // Create your form using one of the techniques described above.
77 |
78 | methods: {
79 | registerUser() {
80 | this.$forms.post('api/users', this.userRegistrationForm)
81 | .then(response => console.log(response.data))
82 | .catch(errors => console.log(errors));
83 | }
84 | }
85 | ```
86 |
87 | _Via a POST request (Components installed separately)_
88 | ```javascript
89 | Vue.component('user-registration-form', {
90 |
91 | // Create your form using one of the techniques described above.
92 |
93 | methods: {
94 | registerUser() {
95 | App.postForm('api/users', this.userRegistrationForm)
96 | .then(response => console.log(response.data))
97 | .catch(errors => console.log(errors));
98 | }
99 | }
100 | ```
101 |
102 | ##### Available methods for submitting a form
103 | _Components installed via Vue_
104 | * `vm.$form.delete(uri, form)`
105 | * `vm.$form.post(uri, form)`
106 | * `vm.$form.put(uri, form)`
107 | * `vm.$form.submit(method, uri, form)`
108 |
109 | _Components installed Separately_
110 | * `App.deleteForm(uri, form)`
111 | * `App.postForm(uri, form)`
112 | * `App.putForm(uri, form)`
113 | * `App.sendForm(method, uri, form)`
114 |
115 |
116 | ### Template Helpers
117 | ##### Check a field for errors
118 | ```javascript
119 | Vue.component('user-registration-form', {
120 |
121 | methods: {
122 | checkFieldForError(field) {
123 | return this.userRegistrationForm.errors.has(field);
124 | }
125 | }
126 |
127 | });
128 | ```
129 |
130 | ##### Use the `fieldClass` helper method
131 |
132 | ```javascript
133 | formInstance.fieldClass(field, defaultClass, errorClass)
134 | ```
135 |
136 | ```vue
137 |
138 | // Truncated for brevity
139 |
140 | ```
141 |
142 | Alternatively, pass callbacks for `defaultClass` and `errorClass`.
143 | ```vue
144 |
145 | // Truncated for brevity
146 |
147 | ```
148 | ```javascript
149 | Vue.component('user-registration-form', {
150 |
151 | methods: {
152 | getFieldClass(field) {
153 | return `form-group ${field}`;
154 | },
155 |
156 | getFieldErrorClass(field) {
157 | return `has-error ${field}-error`;
158 | }
159 | }
160 |
161 | });
162 | ```
163 |
164 | ##### Get the error message for a field
165 | ```vue
166 |
167 | {{ userRegistrationForm.errors.get('email') }}
168 |
169 | ```
170 |
171 | ## Contributing
172 | If you have any questions, feedback or would like to make improvements, **please** open an issue or pull request.
173 |
174 | ## Credits
175 | * [Taylor Otwell](https://github.com/taylorotwell) - This project is heavily based on the form helpers that were
176 | included in the alpha release of [laravel/spark](https://spark.laravel.com).
177 |
178 | ## License
179 | [MIT](https://opensource.org/licenses/MIT)
180 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-laravel-forms",
3 | "version": "1.0.10-beta",
4 | "description": "Form helpers for Laravel backed Vue.js projects.",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "compile": "babel --presets=es2015 -d dist src",
8 | "prepublish": "npm run compile"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/cklmercer/vue-laravel-forms.git"
13 | },
14 | "keywords": [
15 | "vue",
16 | "laravel",
17 | "forms"
18 | ],
19 | "author": "Cody Mercer ",
20 | "maintainer": "Matt Trask ",
21 | "license": "MIT",
22 | "devDependencies": {
23 | "babel-cli": "^6.11.4",
24 | "babel-preset-es2015": "^6.13.2",
25 | "lodash": "^4.15.0",
26 | "vue": "^1.0.26",
27 | "vue-resource": "^0.9.3"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/form-errors.js:
--------------------------------------------------------------------------------
1 | import { flatten, has, isEmpty, toArray } from 'lodash'
2 |
3 | class FormErrors
4 | {
5 | /*
6 | * Create a new FormErrors instance.
7 | */
8 | constructor() {
9 | this.errors = {};
10 | }
11 |
12 | /*
13 | * Get all of the raw errors for the collection.
14 | */
15 | all() {
16 | return this.errors;
17 | }
18 |
19 | /*
20 | * Determine if the collection has any errors.
21 | */
22 | hasErrors() {
23 | return ! isEmpty(this.errors);
24 | }
25 |
26 | /*
27 | * Get all of the errors for the collection in a flat array.
28 | */
29 | flatten() {
30 | return flatten(toArray(this.errors));
31 | }
32 |
33 | /*
34 | * Forget all of the errors currently in the collection.
35 | */
36 | forget() {
37 | this.errors = {};
38 | }
39 |
40 | /*
41 | * Get the first error for the given field.
42 | */
43 | get(field) {
44 | if (this.has(field)) {
45 | return this.errors[field][0];
46 | }
47 | }
48 |
49 | /*
50 | * Determine if the collection has any errors for the given field.
51 | */
52 | has(field) {
53 | return has(this.errors, field);
54 | }
55 |
56 | /*
57 | * Set the raw errors for the collection.
58 | */
59 | set(errors) {
60 | if (typeof errors === 'object') {
61 | this.errors = errors;
62 | } else {
63 | this.errors = {
64 | form: ['Something went wrong. Please try again or contact customer support.']
65 | }
66 | }
67 | }
68 | }
69 |
70 | export default FormErrors;
--------------------------------------------------------------------------------
/src/form.js:
--------------------------------------------------------------------------------
1 | import FormErrors from './form-errors';
2 | import { assignIn, forIn, keys, pick } from 'lodash';
3 |
4 | class Form
5 | {
6 | /*
7 | * Create a new Form instance.
8 | */
9 | constructor(fields) {
10 | this.busy = false;
11 | this.errors = new FormErrors();
12 | this.initialFields = fields;
13 | this.successful = false;
14 | assignIn(this, fields);
15 | }
16 |
17 | /*
18 | * Get the form's fields.
19 | */
20 | get fields() {
21 | let fields = pick(this, keys(this.initialFields));
22 |
23 | // Here we unset null fields.
24 | forIn(fields, function (value, key) {
25 | if (value == null) {
26 | delete fields[key];
27 | }
28 | })
29 |
30 | return fields;
31 | }
32 |
33 | /*
34 | * Get the html/css class for the given field.
35 | */
36 | fieldClass(field, defaultClass = '', errorClass = '') {
37 | let defaultClassString = typeof defaultClass == 'function' ? defaultClass(field) : defaultClass;
38 | let errorClassString = typeof errorClass == 'function' ? errorClass(field) : errorClass;
39 |
40 | return this.errors.has(field) ? `${defaultClassString} ${errorClassString}` : defaultClassString;
41 | }
42 |
43 | /*
44 | * Finish processing the form.
45 | */
46 | finishProcessing() {
47 | this.busy = false;
48 | this.successful = true;
49 | }
50 |
51 | /*
52 | * Completely reset the form.
53 | */
54 | reset() {
55 | this.resetFields();
56 | this.resetStatus();
57 | }
58 |
59 | /*
60 | * Reset the fields to their initial state..
61 | */
62 | resetFields() {
63 | keys(this.initialFields).forEach((key) => this[key] = this.initialFields[key]);
64 | }
65 |
66 | /*
67 | * Reset the errors and other state for the form.
68 | */
69 | resetStatus() {
70 | this.busy = false;
71 | this.errors.forget();
72 | this.successful = false;
73 | }
74 |
75 | /*
76 | * Set the errors on the form.
77 | */
78 | setErrors(errors) {
79 | this.busy = false;
80 | this.errors.set(errors);
81 | }
82 |
83 | /*
84 | * Start processing the form.
85 | */
86 | startProcessing() {
87 | this.errors.forget();
88 | this.busy = true;
89 | this.successful = false;
90 | }
91 |
92 | }
93 |
94 | export default Form;
--------------------------------------------------------------------------------
/src/helpers.js:
--------------------------------------------------------------------------------
1 | function getFileExtension(file) {
2 | return file.name.substr(file.name.lastIndexOf('.') + 1);
3 | }
4 |
5 | function isFile(field) {
6 | return Boolean(field) && Boolean(field.size);
7 | }
8 |
9 | export { getFileExtension, isFile }
--------------------------------------------------------------------------------
/src/http.js:
--------------------------------------------------------------------------------
1 | import { forIn } from 'lodash';
2 | import { getFileExtension, isFile } from './helpers';
3 |
4 | export default function (Http = null) {
5 |
6 | if ( ! Http) {
7 | var Vue = require('vue');
8 | var VueResource = require('vue-resource');
9 | Vue.use(VueResource)
10 | }
11 |
12 | return {
13 | /*
14 | * Helper method for submitting a form as a DELETE request.
15 | */
16 | deleteForm(uri, form) {
17 | return this.sendForm('delete', uri, form);
18 | },
19 |
20 | /*
21 | * Helper method for submitting a form as a POST request.
22 | */
23 | postForm(uri, form) {
24 | let formData = new FormData;
25 |
26 | forIn(form.fields, (value, key) => {
27 | if (isFile(value)) {
28 | let ext = getFileExtension(value);
29 | formData.append(key, value, `${key}.${ext}`);
30 | } else {
31 | formData.append(key, value)
32 | }
33 | });
34 |
35 | return this.sendForm('post', uri, form, formData);
36 | },
37 |
38 |
39 | /*
40 | * Helper method for submitting a form as a PUT request.
41 | */
42 | putForm(uri, form) {
43 | return this.sendForm('put', uri, form);
44 | },
45 |
46 | /*
47 | * Helper method for submitting a form.
48 | */
49 | sendForm(method, uri, form, formData = null) {
50 | let submitter = Http ? Http : Vue.http;
51 |
52 | let data = formData ? formData : JSON.parse(JSON.stringify(form.fields));
53 |
54 | return new Promise((resolve, reject) => {
55 | form.startProcessing();
56 |
57 | submitter[method](uri, data)
58 | .then(response => {
59 | form.finishProcessing();
60 | resolve(response.data);
61 | })
62 | .catch(errors => {
63 | form.setErrors(errors.data);
64 | form.busy = false;
65 | reject(errors.data);
66 | });
67 | });
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Http from './http';
2 | import Form from './form';
3 | import FormErrors from './form-errors';
4 | import { has } from 'lodash';
5 |
6 | function FormHelpers (Vue) {
7 |
8 | let http = Vue.hasOwnProperty('http') ? Vue.http : null;
9 | let formHelper = new Http(http);
10 |
11 | Object.defineProperty(Vue.prototype, '$forms', {
12 | get() {
13 | return {
14 | /*
15 | * Create a new Form instance.
16 | */
17 | create(fields) {
18 | return new Form(fields);
19 | },
20 |
21 | /*
22 | * Create a new FormErrors instance.
23 | */
24 | errors() {
25 | return new FormErrors();
26 | },
27 |
28 | /*
29 | * Submit the given Form to the given URI via a DELETE request.
30 | */
31 | delete(uri, form) {
32 | return formHelper.deleteForm(uri, form);
33 | },
34 |
35 | /*
36 | * Submit the given Form to the given URI via a POST request.
37 | */
38 | post(uri, form) {
39 | return formHelper.postForm(uri, form);
40 | },
41 |
42 | /*
43 | * Submit the given Form to the given URI via a PUT request
44 | */
45 | put(uri, form) {
46 | return formHelper.putForm(uri, form);
47 | },
48 |
49 | /*
50 | * Submit the given Form to the given URI using the given HTTP method.
51 | */
52 | submit(method, uri, form, formData = null) {
53 | return formHelper.sendForm(method, uri, form, formData);
54 | }
55 | }
56 | }
57 | });
58 |
59 | Vue.mixin({
60 |
61 | /*
62 | * The 'beforeCreate' life-cycle hook for Vue 2.0.
63 | */
64 | beforeCreate() {
65 | registerForms(this);
66 | },
67 |
68 | /*
69 | * The 'init' life-cycle hook for Vue 1.0.
70 | */
71 | init() {
72 | registerForms(this);
73 | }
74 |
75 | });
76 | }
77 |
78 | /*
79 | * Register the forms in the forms option.
80 | */
81 | function registerForms(vm) {
82 | let forms = vm.$options.forms;
83 |
84 | if (typeof forms == 'object') {
85 | let dataIsFunction = typeof vm.$options.data == 'function';
86 | let data = dataIsFunction ? vm.$options.data() : vm.$options.data;
87 |
88 | if (typeof data == 'undefined') {
89 | data = {};
90 | }
91 |
92 | for (var form in forms) {
93 | if (has(data, form)) {
94 | throw new Error(`The form, ${form}, has a name which is colliding with another form or data property!`)
95 | }
96 | data[form] = new Form(forms[form]);
97 | }
98 |
99 | vm.$options.data = function () { return data };
100 | }
101 | }
102 |
103 | if (typeof window !== 'undefined' && window.Vue) {
104 | window.Vue.use(FormHelpers);
105 | }
106 |
107 | export { FormHelpers, Http, Form, FormErrors }
108 |
--------------------------------------------------------------------------------