├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── demo └── index.html ├── gulpfile.js ├── package.json ├── src ├── images │ └── example.gif └── js │ ├── Components │ ├── googleAutocomplete.vue │ └── index.js │ ├── Libraries │ ├── Autocomplete.js │ └── Loader.js │ ├── bootstrap.js │ └── demo.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | *.log 4 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gustavo Ocanto 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 | # Google Autocomplete 2 | 3 | Demo 4 | Version 5 | Downloads 6 | Downloads 7 | License 8 | Vue1.* 9 | 10 | 11 | I am sharing this component because I was overwhelmed by complicated examples to achieve this simple job. So, I will try to be as simple as I can during my explanation. 12 | 13 | Google Autocomplete component is no more than a ```Vue.js``` wrapper around the official Google Places API. In spite of the demo written in ```Vue.js```, the Autocomplete object can be pulled in from any ***JS framework***. 14 | 15 | 16 | # Requirements 17 | You will have to install Vue & Vuemit: 18 | 19 | ```js 20 | npm install vue --save 21 | ``` 22 | 23 | ```js 24 | npm install vuemit --save 25 | ``` 26 | 27 | The Vuemit library is used to manage the events between the google component and its parent one. 28 | 29 | 30 | ***Note:*** If you happen to be using ```Vue 1.*```, you will have to pull from the vue-1 branch. 31 | 32 | 33 | # Demo 34 | 35 | View live demo. 36 | 37 | ![example](https://github.com/gocanto/google-autocomplete/blob/master/src/images/example.gif) 38 | 39 | 40 | # Installation 41 | To install this package you just need to open your console and type ```npm i google-autocomplete-vue --save```. If there are any problems during the installation, you can try again using the ```force``` param: ```npm i -f google-autocomplete-vue --save``` 42 | 43 | 44 | ## Getting started 45 | 46 | ***First of all***, you have to sign up on ***Google API Console*** to get your API key: 47 | https://console.developers.google.com 48 | Once this has been done, you will have to copy the ***API KEY given by google*** and paste it in your JS file entry point. Example: 49 | 50 | - Bootstrap File: bootstrap.js. You will have to ***require Vuemit*** in this file to have the events handler set globaly. As so: Example 51 | 52 | - Entry point file: demo.js 53 | 54 | > **Note:** Important keys have to be kept within an .env file, so be aware of this while pushing your code to your git control. 55 | 56 | 57 | ***Second of all***, you will have to import the component in your application entry point, so you can call it globally when needed. Example: 58 | 59 | ```js 60 | import GoogleAutocomplete from 'google-autocomplete-vue'; 61 | ``` 62 | 63 | 64 | ## Validation on server side 65 | 66 | Places validation is a ***laravel library*** that will help you out to handle your user addresses. Its aim is making sure addresses submitted by users are valid through 3rd party services, as google. 67 | 68 | Take a look at its repository: Places Validation 69 | 70 | 71 | # Guide 72 | 73 | * First of all, you have to create an entry point to compile the component and generate your bundle file. An example is available here. 74 | 75 | 76 | * Second of all, you will have to create your vue object to control the component: 77 | 78 | ```javascript 79 | 80 | require('./bootstrap'); 81 | 82 | 83 | new Vue({ 84 | 85 | el: '#demo', 86 | 87 | 88 | data: 89 | { 90 | output: {}, address: {}, sent: false, response: {}, config: {} 91 | }, 92 | 93 | 94 | mounted() 95 | { 96 | //Set an event listener for 'setAddress'. 97 | Vuemit.listen('setAddress', this.onAddressChanged); 98 | Vuemit.listen('addressWasCleared', this.onAddressCleared); 99 | }, 100 | 101 | 102 | methods: 103 | { 104 | /** 105 | * Submit the data to be evaluated. 106 | * 107 | * @return {Void} 108 | */ 109 | submit() 110 | { 111 | this.sent = true; 112 | this.output = this.address; 113 | this.address = {}; 114 | }, 115 | 116 | 117 | /** 118 | * Checks whether the output data is valid. 119 | * 120 | * @return {Bool} 121 | */ 122 | isValid() 123 | { 124 | return Object.keys(this.output).length > 0; 125 | }, 126 | 127 | 128 | /** 129 | * Checks whether the output data is not valid. 130 | * 131 | * @return {Bool} 132 | */ 133 | isNotValid() 134 | { 135 | return ! this.isValid(); 136 | }, 137 | 138 | 139 | /** 140 | * The callback fired when the autocomplete address change event is fired. 141 | * 142 | * @param {Object} 143 | * @return {Void} 144 | */ 145 | onAddressChanged(payload) 146 | { 147 | if (Object.keys(paypload.place).length > 0) { 148 | this.address = payload.address; 149 | this.response = payload.response; 150 | } 151 | } 152 | 153 | /** 154 | * The callback fired when the autocomplete clear event is fired. 155 | * 156 | * @param {Object} 157 | * @return {Void} 158 | */ 159 | onAddressCleared() 160 | { 161 | this.address = {}; 162 | this.response = {}; 163 | } 164 | } 165 | }); 166 | ``` 167 | 168 | See the example here. 169 | 170 | 171 | * Third of all, you have to compile these two files with **browserify**, **webpack** or **laravel-elixir-vue-2** to make them readable for every browser. Example: 172 | 173 | ```javascript 174 | require('laravel-elixir-vue-2'); 175 | var elixir = require('laravel-elixir'); 176 | 177 | elixir.config.sourcemaps = false; 178 | elixir.config.assetsPath = 'src'; 179 | 180 | elixir(function(mix) 181 | { 182 | mix.webpack('demo.js', 'dist/demo.js'); 183 | }); 184 | ``` 185 | 186 | See the example here 187 | 188 | 189 | * Finally, you can use the component in your **HTML** file using this syntax: 190 | 191 | ```HTML 192 | 198 | 199 | ``` 200 | 201 | ***:config*** is the config passed to the Autocomplete constructor of the places API. See the documentation. Config corresponds to the `options` argument in the doc. 202 | 203 | See the example here. 204 | 205 | 206 | Also, you can pass any ```css class``` through the "class" prop. 207 | 208 | 209 | # Contributing 210 | 211 | Please feel free to fork this package and contribute by submitting a pull request to enhance the functionalities. 212 | 213 | 214 | # License 215 | 216 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 217 | 218 | 219 | # How can I thank you? 220 | Why not star the github repo? Share the link for this repository on Twitter? Spread the word! 221 | 222 | 223 | Don't forget to [follow me on twitter](https://twitter.com/gocanto)! 224 | 225 | Thanks! 226 | 227 | Gustavo Ocanto. 228 | gustavoocanto@gmail.com 229 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | google-places-complete - demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 | 33 |
34 | 35 |
36 |
37 |

38 | A small vue plugin to pull in the google places autocomplete in your projects. 39 |

40 |
41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 | 49 |

50 | 56 | 57 | Select a valid address! 58 |

59 |
60 | 61 |

 

62 | 63 |

64 | 67 |

68 | 69 |
70 |
71 | 72 |
73 |
74 | Address retrieved from google places 75 |
76 |
77 | 78 |
{{ output }}
79 | 80 |
{{ response }}
81 |
82 |
83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('laravel-elixir-vue-2'); 2 | var elixir = require('laravel-elixir'); 3 | 4 | elixir.config.sourcemaps = false; 5 | elixir.config.assetsPath = 'src'; 6 | 7 | elixir(function(mix) 8 | { 9 | //The demo js file. 10 | mix.webpack('demo.js', 'dist/demo.js') 11 | 12 | //the library files. 13 | .copy('src/js/Libraries/\**.*', 'dist/Libraries') 14 | 15 | //Exposing the component files to the dist folder. 16 | .copy('src/js/Components/\**.*', 'dist/Components') 17 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "MIT", 3 | "version": "1.0.17", 4 | "author": "Gustavo Ocanto", 5 | "name": "google-autocomplete-vue", 6 | "main": "dist/Components/index.js", 7 | "description": "Google autocomplete vuejs component", 8 | "homepage": "https://github.com/gocanto/google-autocomplete", 9 | "scripts": { 10 | "prepublish": "gulp" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/gocanto/google-autocomplete.git" 15 | }, 16 | "keywords": [ 17 | "vue", 18 | "google", 19 | "component", 20 | "autocomplete" 21 | ], 22 | "devDependencies": { 23 | "gulp": "^3.*", 24 | "gulp-notify": "^2.*", 25 | "laravel-elixir": "6.0.0-11", 26 | "laravel-elixir-vue-2": "^0.2.0", 27 | "laravel-elixir-webpack-official": "^1.0.6", 28 | "vue": "^2.*" 29 | }, 30 | "dependencies": { 31 | "vuemit": "^1.*" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/images/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gocanto/google-autocomplete/bf4715901309c3067061d2ea6bf5141e69002a0e/src/images/example.gif -------------------------------------------------------------------------------- /src/js/Components/googleAutocomplete.vue: -------------------------------------------------------------------------------- 1 | 120 | 121 | 130 | -------------------------------------------------------------------------------- /src/js/Components/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Registering the component as global. 4 | * 5 | * @author Gustavo Ocanto 6 | * @license https://github.com/gocanto/google-autocomplete/blob/master/LICENSE.md 7 | */ 8 | 9 | import Vue from 'vue'; 10 | import GoogleAutocomplete from './googleAutocomplete.vue'; 11 | 12 | Vue.component('google-autocomplete', GoogleAutocomplete); -------------------------------------------------------------------------------- /src/js/Libraries/Autocomplete.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Autocomplete class. 4 | * 5 | * @author Gustavo Ocanto 6 | * @license https://github.com/gocanto/google-autocomplete/blob/master/LICENSE.md 7 | */ 8 | 9 | import Loader from './Loader'; 10 | 11 | class Autocomplete 12 | { 13 | /** 14 | * Create a new autocomplete instance. 15 | * 16 | * @param {String} ref 17 | * @return {Void} 18 | */ 19 | constructor(ref, config = {}) 20 | { 21 | /** 22 | * The retrieved place. 23 | * 24 | * @type {Object} 25 | */ 26 | this.place = {}; 27 | 28 | /** 29 | * The google autocomplete payload. 30 | * 31 | * @type {Object} 32 | */ 33 | this.response = {}; 34 | 35 | /** 36 | * The autocomplete instance. 37 | * 38 | * @type {Object} 39 | */ 40 | this.autocomplete = null; 41 | 42 | /** 43 | * The bounded input. 44 | * 45 | * @type {String} 46 | */ 47 | this.ref = document.getElementById(ref); 48 | 49 | /** 50 | * The autocomplete config 51 | * 52 | * @type {Object} 53 | */ 54 | this.config = config; 55 | 56 | //Boots the autocomplete. 57 | this.boot(); 58 | } 59 | 60 | /** 61 | * Create a new google map instance. 62 | * 63 | * @return Autocomplete 64 | */ 65 | static make(ref, config = {}) 66 | { 67 | return new Autocomplete(ref, config); 68 | } 69 | 70 | /** 71 | * Load the google places API. 72 | * 73 | * @return {Void} 74 | */ 75 | boot() 76 | { 77 | Loader.load(() => { return this.bind(this); }); 78 | } 79 | 80 | /** 81 | * Binds the autocomplete to the given reference. 82 | * 83 | * @param {Self} 84 | */ 85 | bind(obj) 86 | { 87 | obj.autocomplete = new google.maps.places.Autocomplete( 88 | obj.ref, this.config 89 | ); 90 | 91 | obj.autocomplete.addListener('place_changed', () => { obj.pipe(); }); 92 | } 93 | 94 | /** 95 | * Pipes out the retrieved place information. 96 | * 97 | * @return {Void} 98 | */ 99 | pipe() 100 | { 101 | let data = {}; 102 | let googleInputs = window.GOOGLE_AUTOCOMPLETE.inputs; 103 | let place = this.response = this.autocomplete.getPlace(); 104 | 105 | if (place.address_components !== undefined) { 106 | 107 | for (let i = 0; i < place.address_components.length; i++) { 108 | 109 | let input = place.address_components[i].types[0]; 110 | 111 | if (googleInputs[input]) { 112 | data[input] = place.address_components[i][googleInputs[input]]; 113 | } 114 | } 115 | 116 | this.place = JSON.parse( 117 | JSON.stringify(data) 118 | ); 119 | } 120 | } 121 | 122 | /** 123 | * Bind the browser location to the given input. 124 | * 125 | * @return {Void} 126 | */ 127 | geolocate() 128 | { 129 | if (navigator.geolocation) { 130 | 131 | navigator.geolocation.getCurrentPosition( position => { 132 | 133 | let geolocation = { 134 | lat: position.coords.latitude, 135 | lng: position.coords.longitude 136 | }; 137 | 138 | let circle = new google.maps.Circle({ 139 | center: geolocation, 140 | radius: position.coords.accuracy 141 | }); 142 | 143 | this.autocomplete.setBounds(circle.getBounds()); 144 | }); 145 | } 146 | } 147 | 148 | /** 149 | * Returns the retrieved address. 150 | * 151 | * @return {Object} 152 | */ 153 | getPlace() 154 | { 155 | return this.place; 156 | } 157 | 158 | /** 159 | * Returns the retrieved response. 160 | * 161 | * @return {Object} 162 | */ 163 | getResponse() 164 | { 165 | return this.response; 166 | } 167 | 168 | /** 169 | * Returns the google autocomplete object. 170 | * 171 | * @return {Object} 172 | */ 173 | getInstance() 174 | { 175 | return this.autocomplete; 176 | } 177 | } 178 | 179 | export default Autocomplete; 180 | -------------------------------------------------------------------------------- /src/js/Libraries/Loader.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Loader class. 4 | * 5 | * @author Gustavo Ocanto 6 | * @license https://github.com/gocanto/google-autocomplete/blob/master/LICENSE.md 7 | */ 8 | 9 | class Loader 10 | { 11 | /** 12 | * Create a new Loader instance. 13 | * 14 | * @return {Void} 15 | */ 16 | constructor() 17 | { 18 | this.config = window.GOOGLE_AUTOCOMPLETE; 19 | } 20 | 21 | /** 22 | * Loads and script and fires a given callback. 23 | * 24 | * @param {String} src 25 | * @param {Object} 26 | * @return {Void} 27 | */ 28 | static load(callback) 29 | { 30 | let loader = new Loader(); 31 | 32 | const script = document.createElement("script"); 33 | 34 | document.body.appendChild(script); 35 | 36 | if (callback) { 37 | script.onload = callback; 38 | } 39 | 40 | script.src = loader.source(); 41 | } 42 | 43 | /** 44 | * Returns the google API url for a given library. 45 | * 46 | * @return {String} 47 | */ 48 | source() 49 | { 50 | return this.config.domain + '?key=' + this.config.key + '&libraries=' + this.config.library; 51 | } 52 | } 53 | 54 | export default Loader; -------------------------------------------------------------------------------- /src/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | |------------------------------------------------------------------------------------ 4 | | Vue 5 | |------------------------------------------------------------------------------------ 6 | | 7 | | Vue is a modern JavaScript library for building interactive web interfaces 8 | | using reactive data binding and reusable components. Vue's API is clean 9 | | and simple, leaving you to focus on building your next great project. 10 | */ 11 | 12 | window.Vue = require('vue'); 13 | 14 | /* 15 | |------------------------------------------------------------------------------------ 16 | | Vuemit 17 | |------------------------------------------------------------------------------------ 18 | | 19 | | The smallest Vue.js events handler. 20 | | @link https://github.com/gocanto/vuemit 21 | */ 22 | 23 | window.Vuemit = require('vuemit'); 24 | 25 | /* 26 | |------------------------------------------------------------------------------------ 27 | | Global components entry point 28 | |------------------------------------------------------------------------------------ 29 | | 30 | | We register all the global components within this file. 31 | */ 32 | 33 | require('./Components/index'); 34 | 35 | /* 36 | |------------------------------------------------------------------------------------ 37 | | Google Autocomplete Component 38 | |------------------------------------------------------------------------------------ 39 | | 40 | | The google autocomplete is the configuration object for the component. 41 | | Here, you will be able to set the API domain, your personal key 42 | | number, and the library required. 43 | */ 44 | 45 | window.GOOGLE_AUTOCOMPLETE = { 46 | 'domain': 'https://maps.googleapis.com/maps/api/js', 47 | 'key': 'AIzaSyBCxExDtVmUoKn1B18Vs0ULNKq0qu6hmsM', 48 | 'library' : 'places', 49 | 50 | // google inputs retrieved. 51 | 'inputs': { 52 | administrative_area_level_1: 'long_name', 53 | street_number: 'short_name', 54 | postal_code: 'short_name', 55 | locality: 'long_name', 56 | country: 'long_name', 57 | route: 'long_name' 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/js/demo.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * The demo Vue instance. 4 | * 5 | * @author Gustavo Ocanto 6 | * @license https://github.com/gocanto/google-autocomplete/blob/master/LICENSE.md 7 | */ 8 | 9 | require('./bootstrap'); 10 | 11 | new Vue({ 12 | 13 | el: '#demo', 14 | 15 | data: 16 | { 17 | output: {}, address: {}, sent: false, response: {}, configs: {} 18 | }, 19 | 20 | mounted() 21 | { 22 | //Set an event listener for 'setAddress'. 23 | Vuemit.listen('setAddress', this.onAddressChanged); 24 | }, 25 | 26 | methods: 27 | { 28 | /** 29 | * Submit the data to be evaluated. 30 | * 31 | * @return {Void} 32 | */ 33 | submit() 34 | { 35 | this.sent = true; 36 | this.output = this.address; 37 | this.address = {}; 38 | }, 39 | 40 | /** 41 | * Checks whether the output data is valid. 42 | * 43 | * @return {Bool} 44 | */ 45 | isValid() 46 | { 47 | return Object.keys(this.output).length > 0; 48 | }, 49 | 50 | /** 51 | * Checks whether the output data is not valid. 52 | * 53 | * @return {Bool} 54 | */ 55 | isNotValid() 56 | { 57 | return ! this.isValid(); 58 | }, 59 | 60 | /** 61 | * The callback fired when the autocomplete event was fired. 62 | * 63 | * @param {Object} 64 | * @return {Void} 65 | */ 66 | onAddressChanged(payload) 67 | { 68 | if (Object.keys(payload.place).length > 0) { 69 | this.address = payload.place; 70 | this.response = payload.response; 71 | } 72 | } 73 | } 74 | 75 | }); --------------------------------------------------------------------------------