stx
stx

Reputation: 13

Knockout JS - Invalid State Error using ko.toJSON and google maps

Hi there I'm having problems using either ko.toJSON and ko.mapping.toJSON returning an invalid state error after I've applied the google maps binding. I'm supposed to be posting the viewmodel back to an mvc controller. I've managed to reproduce the issue in a simple viewmodel. Is there a way to strip the googlemap and marker off or is there an alternative method? What am I doing wrong?

<script src="/Scripts/knockout-2.3.0.debug.js"></script>
<script src="/Scripts/knockout.mapping-latest.debug.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
    function MyViewModel() {
        var self = this;
        self.test = ko.observable('Hello');
        self.mapOne = ko.observable({
            lat: ko.observable(12.24),
            lng: ko.observable(24.54)
        });

        self.mapTwo = ko.observable({
            lat: ko.observable(40.76),
            lng: ko.observable(-73.98)
        });

        self.save = function () {
            //Invalid state error occurs here!
            var jsonData = ko.toJSON(self);
        };
    }

    ko.bindingHandlers.map = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var mapObj = ko.utils.unwrapObservable(valueAccessor());
            var latLng = new google.maps.LatLng(
                ko.utils.unwrapObservable(mapObj.lat),
                ko.utils.unwrapObservable(mapObj.lng));
            var mapOptions = {
                center: latLng,
                zoom: 6,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };

            mapObj.googleMap = new google.maps.Map(element, mapOptions);

            mapObj.marker = new google.maps.Marker({
                map: mapObj.googleMap,
                position: latLng,
                title: "You Are Here",
                draggable: true
            });

            mapObj.onChangedCoord = function (newValue) {
                var latLng = new google.maps.LatLng(
                    ko.utils.unwrapObservable(mapObj.lat),
                    ko.utils.unwrapObservable(mapObj.lng));
                mapObj.googleMap.setCenter(latLng);
            };

            mapObj.onMarkerMoved = function (dragEnd) {
                var latLng = mapObj.marker.getPosition();
                mapObj.lat(latLng.lat());
                mapObj.lng(latLng.lng());
            };

            mapObj.lat.subscribe(mapObj.onChangedCoord);
            mapObj.lng.subscribe(mapObj.onChangedCoord);

            google.maps.event.addListener(mapObj.marker, 'dragend', mapObj.onMarkerMoved);

            $("#" + element.getAttribute("id")).data("mapObj", mapObj);
        }
    };
    var viewModel = new MyViewModel();
    $(document).ready(function () {
        ko.applyBindings(viewModel);
    });
</script>

<div id="map1Div" data-bind="style: { width: '300px', height: '300px' }, map: mapOne"></div>
<input data-bind="value: mapOne().lat" />
<input data-bind="value: mapOne().lng" />
<button class="btn btn-primary" type="submit" data-bind="click: save">Save</button>

Any help would be appreciated

Upvotes: 1

Views: 925

Answers (1)

pax162
pax162

Reputation: 4735

The problem is caused by adding the extra google maps properties to your original viewModel. When mapping encounters them, it doesn't know what to do. If they are placed in a separate object, it seems to work. mapObjVM is the original viewModel, mapObj is the google maps props container:

ko.bindingHandlers.map = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var mapObjVM = ko.utils.unwrapObservable(valueAccessor());
        var mapObj = {};
        var latLng = new google.maps.LatLng(
            ko.utils.unwrapObservable(mapObjVM.lat),
            ko.utils.unwrapObservable(mapObjVM.lng));
        var mapOptions = {
            center: latLng,
            zoom: 6,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };

        mapObj.googleMap = new google.maps.Map(element, mapOptions);

        mapObj.marker = new google.maps.Marker({
            map: mapObj.googleMap,
            position: latLng,
            title: "You Are Here",
            draggable: true
        });

        mapObj.onChangedCoord = function (newValue) {
            var latLng = new google.maps.LatLng(
                ko.utils.unwrapObservable(mapObjVM.lat()),
                ko.utils.unwrapObservable(mapObjVM.lng()));
            mapObj.googleMap.setCenter(latLng);
        };

        mapObj.onMarkerMoved = function (dragEnd) {
            var latLng = mapObj.marker.getPosition();

            mapObjVM.lat(latLng.lat());
            mapObjVM.lng(latLng.lng());
        };

        mapObjVM.lat.subscribe(mapObj.onChangedCoord);
        mapObjVM.lng.subscribe(mapObj.onChangedCoord);

        google.maps.event.addListener(mapObj.marker, 'dragend', mapObj.onMarkerMoved);

        $("#" + element.getAttribute("id")).data("mapObj", mapObj);
    }
};

Upvotes: 2

Related Questions