戻る

Rails5.0


民泊サイトの構築 Rails5.0

  (0)
2,000円

タスク4-13   geocomplete

このタスクについて


タスクの内容を一部抜粋します。
プロジェクトを購入していただくとこのタスクの内容の全てを読みやすい表示で見ることができます。
プログラムコードが色分けされて見やすくなります。
プログラムコードに行番号が付きます。
本文が色分けされて見やすくなります。
そしてこのアプリケーションのフルコードをダウンロードすることが可能になります。




以下のサイトを開きます。
github.com


「Requirements」の項目の「here」をクリックします。


here

APIキーの部分をクリックすると自分のキーが入ります。


APIキー



コピーボタンを押してリンクをコピーします。


コピーボタン



コピーしたリンクを「app\views\layouts\application.html.erb」に貼り付けます。


記述追加 app\views\layouts\application.html.erb
コピーしたリンクを17行目に貼り付けます。
 
<!DOCTYPE html> 
<html> 
  <head> 
    <title>Minpaku</title> 
    <%= csrf_meta_tags %> 
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %> 
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 
    <!-- googleフォント --> 
    <link href="https://fonts.googleapis.com/css2?family=Kosugi&display=swap" rel="stylesheet"> 
    <!-- アイコン --> 
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> 
    <!-- 日付ピッカー デザインsunny--> 
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/sunny/jquery-ui.css"> 
    <!-- 日付ピッカーの日本語化--> 
    <script src="https://rawgit.com/jquery/jquery-ui/master/ui/i18n/datepicker-ja.js"></script> 
    <!-- geocomplete --> 
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBojDcZmScBkIOISjoYREjgid99iZUL2Tk&libraries=places"></script> 
  </head> 
  <body> 
    <!-- _navbar.html.erb をレンダーする --> 
    <%= render 'shared/navbar' %> 
    <%= render 'shared/message' %> 
    <!-- ページをコンテナに格納 --> 
    <div class="container"> 
      <%= yield %> 
    </div> 
  </body> 
</html> 



「app\assets\javascripts」フォルダに「geocomplete.js」ファイルを新規作成します。


app\assets\javascripts\geocomplete.js(新規作成したファイル)
 
/** 
 * jQuery Geocoding and Places Autocomplete Plugin - V 1.7.0 
 * 
 * @author Martin Kleppe <kleppe@ubilabs.net>, 2016 
 * @author Ubilabs http://ubilabs.net, 2016 
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php> 
 */ 
// # $.geocomplete() 
// ## jQuery Geocoding and Places Autocomplete Plugin 
// 
// * https://github.com/ubilabs/geocomplete/ 
// * by Martin Kleppe <kleppe@ubilabs.net> 
(function($, window, document, undefined){ 
  // ## Options 
  // The default options for this plugin. 
  // 
  // * `map` - Might be a selector, an jQuery object or a DOM element. Default is `false` which shows no map. 
  // * `details` - The container that should be populated with data. Defaults to `false` which ignores the setting. 
  // * 'detailsScope' - Allows you to scope the 'details' container and have multiple geocomplete fields on one page. Must be a parent of the input. Default is 'null' 
  // * `location` - Location to initialize the map on. Might be an address `string` or an `array` with [latitude, longitude] or a `google.maps.LatLng`object. Default is `false` which shows a blank map. 
  // * `bounds` - Whether to snap geocode search to map bounds. Default: `true` if false search globally. Alternatively pass a custom `LatLngBounds object. 
  // * `autoselect` - Automatically selects the highlighted item or the first item from the suggestions list on Enter. 
  // * `detailsAttribute` - The attribute's name to use as an indicator. Default: `"name"` 
  // * `mapOptions` - Options to pass to the `google.maps.Map` constructor. See the full list [here](http://code.google.com/apis/maps/documentation/javascript/reference.html#MapOptions). 
  // * `mapOptions.zoom` - The inital zoom level. Default: `14` 
  // * `mapOptions.scrollwheel` - Whether to enable the scrollwheel to zoom the map. Default: `false` 
  // * `mapOptions.mapTypeId` - The map type. Default: `"roadmap"` 
  // * `markerOptions` - The options to pass to the `google.maps.Marker` constructor. See the full list [here](http://code.google.com/apis/maps/documentation/javascript/reference.html#MarkerOptions). 
  // * `markerOptions.draggable` - If the marker is draggable. Default: `false`. Set to true to enable dragging. 
  // * `markerOptions.disabled` - Do not show marker. Default: `false`. Set to true to disable marker. 
  // * `maxZoom` - The maximum zoom level too zoom in after a geocoding response. Default: `16` 
  // * `types` - An array containing one or more of the supported types for the places request. Default: `['geocode']` See the full list [here](http://code.google.com/apis/maps/documentation/javascript/places.html#place_search_requests). 
  // * `blur` - Trigger geocode when input loses focus. 
  // * `geocodeAfterResult` - If blur is set to true, choose whether to geocode if user has explicitly selected a result before blur. 
  // * `restoreValueAfterBlur` - Restores the input's value upon blurring. Default is `false` which ignores the setting. 
  var defaults = { 
    bounds: true, 
    strictBounds: false, 
    country: null, 
    map: false, 
    details: false, 
    detailsAttribute: "name", 
    detailsScope: null, 
    autoselect: true, 
    location: false, 
    mapOptions: { 
      zoom: 14, 
      scrollwheel: false, 
      mapTypeId: "roadmap" 
    }, 
    markerOptions: { 
      draggable: false 
    }, 
    maxZoom: 16, 
    types: ['geocode'], 
    blur: false, 
    geocodeAfterResult: false, 
    restoreValueAfterBlur: false 
  }; 
  // See: [Geocoding Types](https://developers.google.com/maps/documentation/geocoding/#Types) 
  // on Google Developers. 
  var componentTypes = ("street_address route intersection political " + 
    "country administrative_area_level_1 administrative_area_level_2 " + 
    "administrative_area_level_3 colloquial_area locality sublocality " + 
    "neighborhood premise subpremise postal_code natural_feature airport " + 
    "park point_of_interest post_box street_number floor room " + 
    "lat lng viewport location " + 
    "formatted_address location_type bounds").split(" "); 
  // See: [Places Details Responses](https://developers.google.com/maps/documentation/javascript/places#place_details_responses) 
  // on Google Developers. 
  var placesDetails = ("id place_id url website vicinity reference name rating " + 
    "international_phone_number icon formatted_phone_number").split(" "); 
  // The actual plugin constructor. 
  function GeoComplete(input, options) { 
    this.options = $.extend(true, {}, defaults, options); 
    // This is a fix to allow types:[] not to be overridden by defaults 
    // so search results includes everything 
    if (options && options.types) { 
      this.options.types = options.types; 
    } 
    this.input = input; 
    this.$input = $(input); 
    this._defaults = defaults; 
    this._name = 'geocomplete'; 
    this.init(); 
  } 
  // Initialize all parts of the plugin. 
  $.extend(GeoComplete.prototype, { 
    init: function(){ 
      this.initMap(); 
      this.initMarker(); 
      this.initGeocoder(); 
      this.initDetails(); 
      this.initLocation(); 
    }, 
    // Initialize the map but only if the option `map` was set. 
    // This will create a `map` within the given container 
    // using the provided `mapOptions` or link to the existing map instance. 
    initMap: function(){ 
      if (!this.options.map){ return; } 
      if (typeof this.options.map.setCenter == "function"){ 
        this.map = this.options.map; 
        return; 
      } 
      this.map = new google.maps.Map( 
        $(this.options.map)[0], 
        this.options.mapOptions 
      ); 
      // add click event listener on the map 
      google.maps.event.addListener( 
        this.map, 
        'click', 
        $.proxy(this.mapClicked, this) 
      ); 
      // add dragend even listener on the map 
      google.maps.event.addListener( 
        this.map, 
        'dragend', 
        $.proxy(this.mapDragged, this) 
      ); 
      // add idle even listener on the map 
      google.maps.event.addListener( 
        this.map, 
        'idle', 
        $.proxy(this.mapIdle, this) 
      ); 
      google.maps.event.addListener( 
        this.map, 
        'zoom_changed', 
        $.proxy(this.mapZoomed, this) 
      ); 
    }, 
    // Add a marker with the provided `markerOptions` but only 
    // if the option was set. Additionally it listens for the `dragend` event 
    // to notify the plugin about changes. 
    initMarker: function(){ 
      if (!this.map){ return; } 
      var options = $.extend(this.options.markerOptions, { map: this.map }); 
      if (options.disabled){ return; } 
      this.marker = new google.maps.Marker(options); 
      google.maps.event.addListener( 
        this.marker, 
        'dragend', 
        $.proxy(this.markerDragged, this) 
      ); 
    }, 
    // Associate the input with the autocompleter and create a geocoder 
    // to fall back when the autocompleter does not return a value. 
    initGeocoder: function(){ 
      // Indicates is user did select a result from the dropdown. 
      var selected = false; 
      var options = { 
        types: this.options.types, 
        bounds: this.options.bounds === true ? null : this.options.bounds, 
        componentRestrictions: this.options.componentRestrictions, 
        strictBounds: this.options.strictBounds 
      }; 
      if (this.options.country){ 
        options.componentRestrictions = {country: this.options.country}; 
      } 
      this.autocomplete = new google.maps.places.Autocomplete( 
        this.input, options 
      ); 
      this.geocoder = new google.maps.Geocoder(); 
      // Bind autocomplete to map bounds but only if there is a map 
      // and `options.bindToMap` is set to true. 
      if (this.map && this.options.bounds === true){ 
        this.autocomplete.bindTo('bounds', this.map); 
      } 
      // Watch `place_changed` events on the autocomplete input field. 
      google.maps.event.addListener( 
        this.autocomplete, 
        'place_changed', 
        $.proxy(this.placeChanged, this) 
      ); 
      // Prevent parent form from being submitted if user hit enter. 
      this.$input.on('keypress.' + this._name, function(event){ 
        if (event.keyCode === 13){ return false; } 
      }); 
      // Assume that if user types anything after having selected a result, 
      // the selected location is not valid any more. 
      if (this.options.geocodeAfterResult === true){ 
        this.$input.bind('keypress.' + this._name, $.proxy(function(){ 
          if (event.keyCode != 9 && this.selected === true){ 
              this.selected = false; 
          } 
        }, this)); 
      } 
      // Listen for "geocode" events and trigger find action. 
      this.$input.bind('geocode.' + this._name, $.proxy(function(){ 
        this.find(); 
      }, this)); 
      // Saves the previous input value 
      this.$input.bind('geocode:result.' + this._name, $.proxy(function(){ 
        this.lastInputVal = this.$input.val(); 
      }, this)); 
      // Trigger find action when input element is blurred out and user has 
      // not explicitly selected a result. 
      // (Useful for typing partial location and tabbing to the next field 
      // or clicking somewhere else.) 
      if (this.options.blur === true){ 
        this.$input.on('blur.' + this._name, $.proxy(function(){ 
          if (this.options.geocodeAfterResult === true && this.selected === true) { return; } 
          if (this.options.restoreValueAfterBlur === true && this.selected === true) { 
            setTimeout($.proxy(this.restoreLastValue, this), 0); 
          } else { 
            this.find(); 
          } 
        }, this)); 
      } 
    }, 
    // Prepare a given DOM structure to be populated when we got some data. 
    // This will cycle through the list of component types and map the 
    // corresponding elements. 
    initDetails: function(){ 
      if (!this.options.details){ return; } 
      if(this.options.detailsScope) { 
        var $details = $(this.input).parents(this.options.detailsScope).find(this.options.details); 
      } else { 
        var $details = $(this.options.details); 
      } 
      var attribute = this.options.detailsAttribute, 
        details = {}; 
      function setDetail(value){ 
        details[value] = $details.find("[" +  attribute + "=" + value + "]"); 
      } 
      $.each(componentTypes, function(index, key){ 
        setDetail(key); 
        setDetail(key + "_short"); 
      }); 
      $.each(placesDetails, function(index, key){ 
        setDetail(key); 
      }); 
      this.$details = $details; 
      this.details = details; 
    }, 
    // Set the initial location of the plugin if the `location` options was set. 
    // This method will care about converting the value into the right format. 
    initLocation: function() { 
      var location = this.options.location, latLng; 
      if (!location) { return; } 
      if (typeof location == 'string') { 
        this.find(location); 
        return; 
      } 
      if (location instanceof Array) { 
        latLng = new google.maps.LatLng(location[0], location[1]); 
      } 
      if (location instanceof google.maps.LatLng){ 
        latLng = location; 
      } 
      if (latLng){ 
        if (this.map){ this.map.setCenter(latLng); } 
        if (this.marker){ this.marker.setPosition(latLng); } 
      } 
    }, 
    destroy: function(){ 
      if (this.map) { 
        google.maps.event.clearInstanceListeners(this.map); 
        google.maps.event.clearInstanceListeners(this.marker); 
      } 
      this.autocomplete.unbindAll(); 
      google.maps.event.clearInstanceListeners(this.autocomplete); 
      google.maps.event.clearInstanceListeners(this.input); 
      this.$input.removeData(); 
      this.$input.off(this._name); 
      this.$input.unbind('.' + this._name); 
    }, 
    // Look up a given address. If no `address` was specified it uses 
    // the current value of the input. 
    find: function(address){ 
      this.geocode({ 
        address: address || this.$input.val() 
      }); 
    }, 
    // Requests details about a given location. 
    // Additionally it will bias the requests to the provided bounds. 
    geocode: function(request){ 
      // Don't geocode if the requested address is empty 
      if (!request.address) { 
        return; 
      } 
      if (this.options.bounds && !request.bounds){ 
        if (this.options.bounds === true){ 
          request.bounds = this.map && this.map.getBounds(); 
        } else { 
          request.bounds = this.options.bounds; 
        } 
      } 
      if (this.options.country){ 
        request.region = this.options.country; 
      } 
      this.geocoder.geocode(request, $.proxy(this.handleGeocode, this)); 
    }, 
    // Get the selected result. If no result is selected on the list, then get 
    // the first result from the list. 
    selectFirstResult: function() { 
      //$(".pac-container").hide(); 
      var selected = ''; 
      // Check if any result is selected. 
      if ($(".pac-item-selected")[0]) { 
        selected = '-selected'; 
      } 
      // Get the first suggestion's text. 
      var $span1 = $(".pac-container:visible .pac-item" + selected + ":first span:nth-child(2)").text(); 
      var $span2 = $(".pac-container:visible .pac-item" + selected + ":first span:nth-child(3)").text(); 
      // Adds the additional information, if available. 
      var firstResult = $span1; 
      if ($span2) { 
        firstResult += " - " + $span2; 
      } 
      this.$input.val(firstResult); 
      return firstResult; 
    }, 
    // Restores the input value using the previous value if it exists 
    restoreLastValue: function() { 
      if (this.lastInputVal){ this.$input.val(this.lastInputVal); } 
    }, 
    // Handles the geocode response. If more than one results was found 
    // it triggers the "geocode:multiple" events. If there was an error 
    // the "geocode:error" event is fired. 
    handleGeocode: function(results, status){ 
      if (status === google.maps.GeocoderStatus.OK) { 
        var result = results[0]; 
        this.$input.val(result.formatted_address); 
        this.update(result); 
        if (results.length > 1){ 
          this.trigger("geocode:multiple", results); 
        } 
      } else { 
        this.trigger("geocode:error", status); 
      } 
    }, 
    // Triggers a given `event` with optional `arguments` on the input. 
    trigger: function(event, argument){ 
      this.$input.trigger(event, [argument]); 
    }, 
    // Set the map to a new center by passing a `geometry`. 
    // If the geometry has a viewport, the map zooms out to fit the bounds. 
    // Additionally it updates the marker position. 
    center: function(geometry){ 
      if (geometry.viewport){ 
        this.map.fitBounds(geometry.viewport); 
        if (this.map.getZoom() > this.options.maxZoom){ 
          this.map.setZoom(this.options.maxZoom); 
        } 
      } else { 
        this.map.setZoom(this.options.maxZoom); 
        this.map.setCenter(geometry.location); 
      } 
      if (this.marker){ 
        this.marker.setPosition(geometry.location); 
        this.marker.setAnimation(this.options.markerOptions.animation); 
      } 
    }, 
    // Update the elements based on a single places or geocoding response 
    // and trigger the "geocode:result" event on the input. 
    update: function(result){ 
      if (this.map){ 
        this.center(result.geometry); 
      } 
      if (this.$details){ 
        this.fillDetails(result); 
      } 
      this.trigger("geocode:result", result); 
    }, 
    // Populate the provided elements with new `result` data. 
    // This will lookup all elements that has an attribute with the given 
    // component type. 
    fillDetails: function(result){ 
      var data = {}, 
        geometry = result.geometry, 
        viewport = geometry.viewport, 
        bounds = geometry.bounds; 
      // Create a simplified version of the address components. 
      $.each(result.address_components, function(index, object){ 
        var name = object.types[0]; 
        $.each(object.types, function(index, name){ 
          data[name] = object.long_name; 
          data[name + "_short"] = object.short_name; 
        }); 
      }); 
      // Add properties of the places details. 
      $.each(placesDetails, function(index, key){ 
        data[key] = result[key]; 
      }); 
      // Add infos about the address and geometry. 
      $.extend(data, { 
        formatted_address: result.formatted_address, 
        location_type: geometry.location_type || "PLACES", 
        viewport: viewport, 
        bounds: bounds, 
        location: geometry.location, 
        lat: geometry.location.lat(), 
        lng: geometry.location.lng() 
      }); 
      // Set the values for all details. 
      $.each(this.details, $.proxy(function(key, $detail){ 
        var value = data[key]; 
        this.setDetail($detail, value); 
      }, this)); 
      this.data = data; 
    }, 
    // Assign a given `value` to a single `$element`. 
    // If the element is an input, the value is set, otherwise it updates 
    // the text content. 
    setDetail: function($element, value){ 
      if (value === undefined){ 
        value = ""; 
      } else if (typeof value.toUrlValue == "function"){ 
        value = value.toUrlValue(); 
      } 
      if ($element.is(":input")){ 
        $element.val(value); 
      } else { 
        $element.text(value); 
      } 
    }, 
    // Fire the "geocode:dragged" event and pass the new position. 
    markerDragged: function(event){ 
      this.trigger("geocode:dragged", event.latLng); 
    }, 
    mapClicked: function(event) { 
        this.trigger("geocode:click", event.latLng); 
    }, 
    // Fire the "geocode:mapdragged" event and pass the current position of the map center. 
    mapDragged: function(event) { 
      this.trigger("geocode:mapdragged", this.map.getCenter()); 
    }, 
    // Fire the "geocode:idle" event and pass the current position of the map center. 
    mapIdle: function(event) { 
      this.trigger("geocode:idle", this.map.getCenter()); 
    }, 
    mapZoomed: function(event) { 
      this.trigger("geocode:zoom", this.map.getZoom()); 
    }, 
    // Restore the old position of the marker to the last knwon location. 
    resetMarker: function(){ 
      this.marker.setPosition(this.data.location); 
      this.setDetail(this.details.lat, this.data.location.lat()); 
      this.setDetail(this.details.lng, this.data.location.lng()); 
    }, 
    // Update the plugin after the user has selected an autocomplete entry. 
    // If the place has no geometry it passes it to the geocoder. 
    placeChanged: function(){ 
      var place = this.autocomplete.getPlace(); 
      this.selected = true; 
      if (!place.geometry){ 
        if (this.options.autoselect) { 
          // Automatically selects the highlighted item or the first item from the 
          // suggestions list. 
          var autoSelection = this.selectFirstResult(); 
          this.find(autoSelection); 
        } 
      } else { 
        // Use the input text if it already gives geometry. 
        this.update(place); 
      } 
    } 
  }); 
  // A plugin wrapper around the constructor. 
  // Pass `options` with all settings that are different from the default. 
  // The attribute is used to prevent multiple instantiations of the plugin. 
  $.fn.geocomplete = function(options) { 
    var attribute = 'plugin_geocomplete'; 
    // If you call `.geocomplete()` with a string as the first parameter 
    // it returns the corresponding property or calls the method with the 
    // following arguments. 
    if (typeof options == "string"){ 
      var instance = $(this).data(attribute) || $(this).geocomplete().data(attribute), 
        prop = instance[options]; 
      if (typeof prop == "function"){ 
        prop.apply(instance, Array.prototype.slice.call(arguments, 1)); 
        return $(this); 
      } else { 
        if (arguments.length == 2){ 
          prop = arguments[1]; 
        } 
        return prop; 
      } 
    } else { 
      return this.each(function() { 
        // Prevent against multiple instantiations. 
        var instance = $.data(this, attribute); 
        if (!instance) { 
          instance = new GeoComplete( this, options ); 
          $.data(this, attribute, instance); 
        } 
      }); 
    } 
  }; 
})( jQuery, window, document ); 



「app\views\pages\home.html.erb」ファイルを編集します。


1.4行目に「, id:"autolocation"」の記述を追加します。
<%= text_field_tag :search, params[:search], placeholder: "どちらのお部屋をおさがしですか?", id:"autolocation", class: "form-control" %>



2.63行目にスクリプトの記述を追加します。
   $(function() { 
    $("#autolocation").geocomplete(); 
  }) 




この続きはプロジェクトを購入していただくことで見ることができます。
プロジェクトを購入していただくとこのタスクの内容の全てを読みやすい表示で見ることができます。
プログラムコードが色分けされて見やすくなります。
プログラムコードに行番号が付きます。
本文が色分けされて見やすくなります。
そしてプロジェクトを購入するとこのアプリケーションのフルコードをダウンロードすることができます。

まだレビューはありません。

民泊サイトの構築 Rails5.0

2,000円

民泊サイトを構築します。
ホストは部屋を登録し、ゲストは宿泊予約できます。
ゲストはクレジットカードで支払いをし、ホストには料金の80%が自動で口座に振り込まれます。
Googleマップによる表示、AJAX検索、日付カレンダーによる予約、フルカレンダーによる部屋管理ができます。
リアルタイムメッセージにより連絡がとれます。
予約確認メールの送信も可能。
写真のアップロード機能、アマゾンS3の利用方法も解説。
レビュー機能の実装、電話番号認証の実装方法解説。
HEROKUへのデプロイ方法を解説。
フルコードのダウンロード可能。

タスク数: 136