Search code examples
google-mapsmeteormeteor-autoformsimple-schema

How to show reactive map at meteor/autoform, and get marker full adress


So i've been struggling for a week to make this happen!!!!

In short, my structure is as follows :

Address = new SimpleSchema({
    location: {
        type: Object
    },
    fullAddress: {
        type: String
    },
    country: {
        type: String
    },
    governorate: {
        type: String
    },
    city: {
        type: String
    },
    street: {
        type: String
    },
    building: {
        type: Number
    }
});

Branch = new SimpleSchema({
    address: {
         type: Address
    },
    ....
});

Companies = new Mongo.Collection('companies');
CompanySchema = new SimpleSchema({
    branch: {
       type: [Branch],
       minCount: 1
    },
    ....
});
Companies.attachSchema(CompanySchema);

As you can see, I have an array of branches, with all branch has an address & location.

I want to be able to show a map for each [Branch]/Address when calling autoform like:

{{> quickForm collection="Companies" type="insert" id="company_form"}}

Then, have some map click listener to place a marker, and then reverse geoDecode location to populate the address fields

I have tried following yogiben:autoform-map, but the package is incomplete (has MyLocation button issues zoom exceptions, and cannot show multiple maps per page) thus, cannot be used in production.

I am disparate...Please help!


Solution

  • So, I created my own solution, and I'll share it to benefit any one having the same issue.

    I modified yogiben/meteor-autoform-map and added a pull request adding geoCoding feature along with a lot of other stuff.

    The following is my current autoform configurations & handling:-

    Schema

    Location = new SimpleSchema({
        location: {
            type: Object,
            autoform:{
                type: 'map',
                label: false,
                afFieldInput: {
                    mapType: 'terrain',
                    defaultLat: 30.0444196,
                    defaultLng: 31.23571160000006,
                    geolocation: true,
                    autolocate: true,
                    searchBox: true,
                    language: function(){ return getUserLanguage(); },
                    direction: function(){ return (getUserLanguage() == 'ar')? 'rtl' : 'ltr'; },
                    zoom: 16,
                    key: Meteor.settings.public.google_maps_key,
                    googleMap: {
                        mapTypeControl: false
                    },
                    geoCoding: true,
                    geoCodingCallBack: 'reverseGeoCode',
                    animateMarker: true
                }
            }
        },
        placeId: {
            type: String,
            autoform: {
                type: 'hidden'
            },
            autoValue: function(){
                return '';
            }
        },
        createdAt: {
            type: Date,
            autoValue: function(){
                return new Date();
            },
            autoform: {
                type: 'hidden'
            }
        }
    });
    
    Address = new SimpleSchema({
        location: {
            type: Location,
            label: function(){
                return (Session.get('lang') == 'ar')? 'الموقع على الخريطة' : 'Map Location';
            }
        },
        country: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'الدولة' : 'Country';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'الدولة' : 'Country';
                    }
                },
                type: 'hidden'
            }
        },
        governorate: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'المحافطة' : 'Governorate';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'المحافظة' : 'Governorate';
                    }
                }
            }
        },
        city: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'المدينة' : 'City';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'المدينة' : 'City';
                    }
                }
            }
        },
        district: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'الحي' : 'District';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'الحي' : 'District';
                    }
                }
            }
        },
        street: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'الشارع' : 'Street';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'اسم \ رقم الشارع' : 'Street Name / Number';
                    }
                }
            }
        },
        building: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'رقم المبنى' : 'Building Number';
            },
            regEx: /[\d+\-]/,
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'رقم المبنى' : 'Building Number';
                    }
                },
            }
        },
        createdAt: {
            type: Date,
            autoValue: function(){
                return new Date();
            },
            autoform: {
                type: 'hidden'
            }
        }
    });
    
    Branch = new SimpleSchema({
        name: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'اسم الفرع' : 'Branch Name';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'اسم الفرع' : 'Branch Name';
                    }
                }
            }
        },
        address: {
            type: Address,
            label: function(){
                return (Session.get('lang') == 'ar')? 'العنوان' : 'Address';
            },
            minCount: 1,
            optional: false
        },
        createdAt: {
            type: Date,
            autoValue: function(){
                return new Date();
            },
            autoform: {
                type: 'hidden'
            }
        }
    });
    
    CompaniesSchema = new SimpleSchema({
        name: {
            type: String,
            label: function(){
                return (Session.get('lang') == 'ar')? 'اسم الشركة' : 'Company Name';
            },
            autoform: {
                afFieldInput: {
                    placeholder: function(){
                        return (Session.get('lang') == 'ar')? 'اسم الشركة' : 'Company Name';
                    }
                }
            }
        },
        branches: {
            type: [Branch],
            label: function(){
                return (Session.get('lang') == 'ar')? 'الفرع' : 'Branch';
            }
        },
        createdAt: {
            type: Date,
            autoValue: function(){
                return new Date();
            },
            autoform: {
                type: 'hidden'
            }
        }
    });
    

    client/main.js

    Meteor.startup(() => {
        // ================================================================ //
        // Functions that need to be global perior to Packages loading      //
        // ================================================================ //
        // Get Initial/Current user language 
        getUserLanguage = function () {
            return Session.get('lang') || Meteor.settings.public.defaultLang;
        };
    
        // Reverse Geocode location
        // @param t         => Template Instance    - object
        // @param geocoder  => google.map.geocoder  - object
        // @param location  => google.maps.LatLng   - object
        reverseGeoCode = function(t, geocoder, location){
            var latlng = {lat: location.lat(), lng: location.lng()};
            var geoCodingOpt = {
                'location'  : latlng,
                'language'  : Session.get('lang'),
                'region'    : 'eg'
            };
    
            geocoder.geocode(geoCodingOpt, function(results, status){
                if(status === 'OK'){
                    if(results.length !== 0){
                        var address = results[0];
                        var cmp = address.address_components;
                        var pfx = t.data.name.replace(/.location/g,'');
                        var sel = '[name="' + pfx + '.' + 'xx' + '"]';
                        var selObj = {
                            placeId     : sel.replace('xx', 'location.placeId'),
                            country     : sel.replace('xx', 'country'),
                            governorate : sel.replace('xx', 'governorate'),
                            city        : sel.replace('xx', 'city'),
                            district    : sel.replace('xx', 'district'),
                            street      : sel.replace('xx', 'street'),
                            building    : sel.replace('xx', 'building')
                        };
    
                        // Address Container DOM element
                        $addressElm = $(t.firstNode.closest('.autoform-array-item'));
                        // Place ID
                        $addressElm.find(selObj.placeId).val(address.place_id);
                        // Country
                        $addressElm.find(selObj.country).val(cmp[5].long_name);
                        // Governorate
                        $addressElm.find(selObj.governorate).val(cmp[4].long_name);
                        // City
                        $addressElm.find(selObj.city).val(cmp[3].long_name);
                        // District
                        $addressElm.find(selObj.district).val(cmp[2].long_name);
                        // Street
                        $addressElm.find(selObj.street).val(cmp[1].long_name);
                        // Building
                        $addressElm.find(selObj.building).val(cmp[0].long_name);
    
                        // Clear DOM refference for Garbage Collection
                        $addressElm.prevObject = null;
                        $addressElm = null;
                    }
                }
            });
        }
    });
    

    Hope that helps anyone.

    Feel free to inquire any elaborations