├── gulpfile.js ├── package.json ├── .gitignore ├── submit.php ├── LICENSE ├── README.md ├── vue.ajax-form.js └── example.html /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | var gulp = require('gulp'); 3 | var livereload = require('gulp-livereload'); 4 | 5 | gulp.task('reload', function() { 6 | gulp.src(['**/*.{php,html}', 'js/*.js']) 7 | .pipe(livereload()); 8 | }); 9 | 10 | gulp.task('watch', function() { 11 | livereload.listen(); 12 | gulp.watch('**/*.{php,html}', ['reload']); 13 | gulp.watch('js/*.js', ['reload']); 14 | }); 15 | 16 | gulp.task('default', [], function() { 17 | // fired before 'finished' event 18 | }); 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ajax-form-component", 3 | "version": "1.1.0", 4 | "description": "A component for creating simple AJAX forms.", 5 | "homepage": "http://ohdoylerules.com", 6 | "repository": "https://github.com/james2doyle/vue-ajax-form-component", 7 | "main": "vue.ajax-form.js", 8 | "author": { 9 | "name": "James Doyle", 10 | "email": "james2doyle@gmail.com", 11 | "url": "http://ohdoylerules.com/" 12 | }, 13 | "licenses": [{ 14 | "type": "MIT", 15 | "url": "http://opensource.org/licenses/MIT" 16 | }], 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "gulp": "^3.9.0", 20 | "gulp-livereload": "^3.8.1" 21 | } 22 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: https://goel.io/joe 2 | 3 | #####=== Node ===##### 4 | 5 | # Logs 6 | logs 7 | *.log 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 31 | node_modules 32 | 33 | # Debug log from npm 34 | npm-debug.log 35 | 36 | -------------------------------------------------------------------------------- /submit.php: -------------------------------------------------------------------------------- 1 | '200 OK', 7 | 400 => '400 Bad Request', 8 | 500 => '500 Internal Server Error' 9 | ); 10 | // clear the old headers 11 | header_remove(); 12 | // set the header to make sure cache is forced 13 | header("Cache-Control: no-transform,public,max-age=300,s-maxage=900"); 14 | // treat this as json 15 | header('Content-Type: application/json'); 16 | // ok, validation error, or failure 17 | header('Status: ' . $status[$code]); 18 | // return the encoded json 19 | return json_encode(array( 20 | 'status' => $code < 300, // success or not? 21 | 'data' => $data 22 | )); 23 | } 24 | 25 | // usage 26 | echo json_response(200, [$_POST, $_FILES]); 27 | 28 | exit; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 James Doyle 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 | # vue-ajax-form-component 2 | 3 | A [Vue.js](http://vuejs.org/) component for creating simple AJAX forms. 4 | 5 | ### Install 6 | 7 | Available through npm as `vue-ajax-form-component`. Or include as an inline script, like in `example.html`. 8 | 9 | ### Demo 10 | 11 | ![](http://cl.ly/image/3Q0L2K0T3Y1i/Screen%20Recording%202015-12-06%20at%2003.57%20PM.gif) 12 | 13 | You can load up the `example.html` file here to test the directive. Just make sure you put this folder in a server that has PHP. Or you can change the `ajax-form` action attribute to point to your API endpoint. 14 | 15 | ### Usage 16 | 17 | Minimal: 18 | 19 | ```html 20 | 21 | 22 | 23 | ``` 24 | 25 | Full Example: 26 | 27 | ```html 28 | 29 | 30 | 31 | ``` 32 | 33 | You can see a custom attribute called `v-response-type`. This allows you to set the response type for your form. Most cases you will be expecting `JSON` in return, and then sending that to some variable in your Vue data. 34 | 35 | But, sometimes maybe you are expecting back some HTML or some raw text. In those cases, you can use `v-response-type="html"` or `v-response-type="text"`, respectively. Then you could maybe dump the results in your view in some area. A use case for this might be for "session" flashes like success or error alerts, or maybe a preview of a blog post. 36 | 37 | You can see a `Vue` instance example in [example.html](https://github.com/james2doyle/vue-ajax-form-component/blob/master/example.html) if you want more details. 38 | 39 | ### Support 40 | 41 | This component assumes you have support for [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) and the XHR `progress` event. -------------------------------------------------------------------------------- /vue.ajax-form.js: -------------------------------------------------------------------------------- 1 | /* globals FormData, Vue */ 2 | 3 | var AjaxFormComponent = Vue.extend({ 4 | template: '
', 5 | props: { 6 | 'id': String, 7 | 'class': String, 8 | 'action': { 9 | type: String, 10 | required: true 11 | }, 12 | 'method': { 13 | type: String, 14 | required: true, 15 | validator: function(value){ 16 | switch(value.toUpperCase()){ 17 | case 'CONNECT': return true 18 | case 'DELETE': return true 19 | case 'GET': return true 20 | case 'HEAD': return true 21 | case 'OPTIONS': return true 22 | case 'POST': return true 23 | case 'PUT': return true 24 | case 'TRACE': return true 25 | case 'TRACK': return true 26 | default: return false 27 | } 28 | } 29 | }, 30 | 'v-response-type': String 31 | }, 32 | methods: { 33 | handleAjaxFormSubmit: function() { 34 | // fires before we do anything 35 | this.$dispatch('beforeFormSubmit', this); 36 | 37 | // fires whenever an error occurs 38 | var handleError = (function(err) { 39 | this.$dispatch('onFormError', this, err); 40 | }).bind(this); 41 | 42 | // set a default form method 43 | if (!this.method) { 44 | this.method = 'post'; 45 | } 46 | 47 | // fires when the form returns a result 48 | var handleFinish = (function(data) { 49 | if (xhr.readyState == 4) { 50 | // a check to make sure the result was a success 51 | if (xhr.status < 400) { 52 | this.$dispatch('onFormComplete', this, xhr.response); 53 | } else { 54 | this.$dispatch('onFormError', this, xhr.statusText); 55 | } 56 | } 57 | }).bind(this); 58 | 59 | var handleProgress = (function(evt) { 60 | // flag indicating if the resource has a length that can be calculated 61 | if (evt.lengthComputable) { 62 | // create a new lazy property for percent 63 | evt.percent = (evt.loaded / evt.total) * 100; 64 | this.$dispatch('onFormProgress', this, evt); 65 | } 66 | }).bind(this); 67 | 68 | var xhr = new XMLHttpRequest(); 69 | xhr.open(this.method, this.action, true); 70 | 71 | // you can set the form response type via v-response-type 72 | if (this.vResponseType) { 73 | xhr.responseType = this.vResponseType; 74 | } else { 75 | xhr.responseType = 'json'; 76 | } 77 | 78 | xhr.upload.addEventListener('progress', handleProgress); 79 | xhr.addEventListener('readystatechange', handleFinish); 80 | xhr.addEventListener('error', handleError); 81 | xhr.addEventListener('abort', handleError); 82 | var data = new FormData(event.target); 83 | xhr.send(data); 84 | // we have setup all the stuff we needed to 85 | this.$dispatch('afterFormSubmit', this); 86 | } 87 | } 88 | }); 89 | 90 | // register 91 | Vue.component('ajax-form', AjaxFormComponent); -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js AJAX Form Component 6 | 7 | 8 | 29 | 30 | 31 |
32 | 33 |

Vue AJAX Form Component

34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 |
48 | 49 |
50 | {{ progress }}% complete 51 |
{{ response | json }}
52 |
53 | 54 | 55 | 92 | 93 | 94 | --------------------------------------------------------------------------------