4

I am trying to show a simple map in HotTowel.

In home.html page I have that:

<section>
  <div id="map-canvas" data-bind="map: map"></div>
</section>

In home.js I have that:

define(['services/logger'], function (logger) {
  var vm = {
    activate: activate,
    title: 'Home View',
    map: map
};

  return vm;

  function activate() {
    google.maps.event.addDomListener(window, 'load', initialize);
    logger.log('Home View Activated', null, 'home', true);
    return true;
  }

  var map;
  function initialize() {
    var mapOptions = {
      zoom: 8,
      center: new google.maps.LatLng(-34.397, 150.644),
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
  }
});

How to bind model with view to show map?

Mariusz
  • 442
  • 5
  • 16

4 Answers4

3

EDIT**

The below answer was for Durandal 1.2. In durandal 2.0 the viewAttached event was renamed to attached. You can read Durandals documentation about it here.


Durandal has a viewAttached event that is called on your viewmodel once the view has been databound and attached to the dom. That would be a good place to call the google maps api.

define(['services/logger'], function (logger) {
  var vm = {
    viewAttached: initialize
    title: 'Home View',
    map: map
};

  return vm;

  var map;
  function initialize(view) {
    logger.log('Home View Activated', null, 'home', true);
    var mapOptions = {
      zoom: 8,
      center: new google.maps.LatLng(-34.397, 150.644),
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
  }
});

EDIT AGAIN to address peoples comments**

As per Jamie Hammond's comment it is a better practice to scope your DOM transversal to the view that's being attached. If the DOM element is apart of the view.

So, inside your viewAttached (in durandal 1.2) or attached (in durandal 2.0) you would:

var map;
var mapOptions = { /*map options*/ };
var vm = {
   attached: function (view) {
       var mapCanvas = $('#map-canvas', view).get(0);
       map = new google.maps.Map(mapCanvas, mapOptions);
   }
}

I haven't messed with Durandal 2.0 at all because I've been pretty busy with work and stuff and when I was messing around with Durandal 1.0 it was just for fun but I do love the framework and hope to one day get to play with 2.0. With that said I did have an issue with generating a map in the viewattached in Durandal 1.0. But, I wasn't using Google maps. I was using Leafletjs. My solution to the problem was creating a delay in the viewAttached that would redraw the map after a short delay. This was because Durandal's transitioning in the view was not working well with leaflets ability to draw the map in the dom element as it was flying and fading in.

So, inside the viewAttached I would draw the map like so:

window.setTimeout(drawMap, 10);

Again, this was a very specific problem I had and not a problem with Durandal. This was more of a problem with Leafletjs not rendering the map correctly when the DOM element was still transitioning in.

Evan Larsen
  • 9,935
  • 4
  • 46
  • 60
  • 2
    In Durandal 2.0, "viewAttached" was renamed to "attached". Also, the code above does not work correctly even after you fix the obvious syntax errors like adding a comma after initialize. Like others, I am still looking for a solution that works with the new google API. – pilavdzice Jan 14 '14 at 21:53
  • You should always scope to the view. e.g $(view).find('#map-canvas') – Jamie Hammond Jan 15 '14 at 11:18
  • are you loading google with something like ***Google Maps Web Services API wrapper for .NET 0.30.0*** http://www.nuget.org/packages/GoogleMapsApi/ or how do you start using google map? – Gildas.Tambo Jan 17 '14 at 12:57
  • I think that nuget package is for a .net library. I'm referring to google maps javascript library. – Evan Larsen Jan 17 '14 at 16:01
2

Evan,

I'm also trying to get this working but no joy. I have my html as and viewmodel exactly as you have, and I know the viewAttached composition is being called because I'm getting my logger event - but no map!

The only other thing I can think of is where you call your googlemaps from? I'm doing in in my index.html are you doing the same?

Regards

BrettH
  • 189
  • 3
  • 8
  • can you add logger.log(document.getElementById('map-canvas')) and see if you are able to get a reference to the dom element from that viewattached event – Evan Larsen Jan 14 '14 at 22:09
  • For me everything runs without errors and puts stuff into the map-canvas div, but then nothing happens after that. I don't understand why use attached instead of compositionComplete? Does it matter if you include the google API script file before or after other includes? – pilavdzice Jan 14 '14 at 22:29
  • check out the answer i just posted it may work for you – Gildas.Tambo Jan 15 '14 at 10:08
  • The browser will download scripts asynchronously in the order you have them referenced and once downloaded it will execute them in that same order. So, depending on when you are calling the google maps api.. I would make sure the google scripts are loaded before then. – Evan Larsen Jan 15 '14 at 19:34
  • Thanks for that - after examining the parsed html in the browser dev tools I realised that it was setting my containing div to a height of 0px as well! Phew, that was driving me nuts! – BrettH Jan 15 '14 at 21:51
1

BrettH,

For me, the problem was that the height was 0px. My module that works looks like this:

define(['plugins/router', 'knockout', 'plugins/utility'], function (router, ko, utility) { var vm = { }; vm.map = undefined;

vm.compositionComplete = function () {

    var myLatlng = new google.maps.LatLng(29.4000, 69.1833);
    var mapOptions = {
        zoom: 6,
        center: myLatlng
    }

    vm.map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

    var georssLayer = new google.maps.KmlLayer({
        url: 'http://www.visualtravelguide.com/Pakistan-Islamabad.kmz'
    });
    georssLayer.setMap(vm.map);

    utility.resizeElementHeight(document.getElementById('map-canvas'), 10);
    $(window).resize(function () {
        utility.resizeElementHeight(document.getElementById('map-canvas'), 10);
    });

};

return vm;

});

My utility module looks like this:

define(['jquery','knockout'], function ($,ko) {

return {

    //resizes an element so height goes to bottom of screen, got this from a stack overflow
    //usage:
    // resizeElementHeight(document.getElementById('projectSelectDiv'));
    //$(window).resize(function () {
    //     resizeElementHeight(document.getElementById('projectSelectDiv'));
    //});
    //adjustpixels is an optional parameter if you want to leave room at the bottom
    resizeElementHeight: function (element,adjustPixels) {
        var height = 0;
        var adjust = 0;
        if (adjustPixels != undefined)
            adjust = adjustPixels;
        var body = window.document.body;
        if (window.innerHeight) {
            height = window.innerHeight;
        } else if (body.parentElement.clientHeight) {
            height = body.parentElement.clientHeight;
        } else if (body && body.clientHeight) {
            height = body.clientHeight;
        }
        element.style.height = ((height - element.offsetTop-adjust) + "px");
    },
    //looks up name by id, returns blank string if not found
    //pass in a list and an id (they can be observables)
    LookupNameById: function (l, wId) {
            var list = ko.utils.unwrapObservable(l);
            var id = ko.utils.unwrapObservable(wId);
            var name = '';
            $.each(list, function (key, value) {

                if (value.Id() == id)
                    name = value.Name();
            });

            return name;
    },
    //sets the widths of the columns of headertable to those of basetable
    setHeaderTableWidth: function (headertableid,basetableid) {
        $("#"+headertableid).width($("#"+basetableid).width());
        $("#"+headertableid+" tr th").each(function (i) {
            $(this).width($($("#"+basetableid+" tr:first td")[i]).width());
        });
        $("#" + headertableid + " tr td").each(function (i) {
            $(this).width($($("#" + basetableid + " tr:first td")[i]).width());
        });
    }

};

});

Hope this helps you.

pilavdzice
  • 958
  • 8
  • 27
1

first go to your main.js and add 'async': '../Scripts/async',

require.config({
    paths: {
        'text': '../Scripts/text',
        'durandal': '../Scripts/durandal',
        'plugins': '../Scripts/durandal/plugins',
        'mapping': '../Scripts/knockout.mapping-latest',
        'async': '../Scripts/async',
        'transitions': '../Scripts/durandal/transitions'
    },
    shim: { mapping: { deps: ['knockout'] } }
});

notice that we need to add async.js in the scripts folder so go to Download async.js download the file and save it in hottowel script folder as async.js

the in the main.js add this

// convert Google Maps into an AMD module
define('gmaps', ['async!http://maps.google.com/maps/api/js?v=3&sensor=false'],
function(){
    // return the gmaps namespace for brevity
    return window.google.maps;
});

in any viewmodel you can now use it like this

define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'gmaps'], function (router, ko, logger, app, gmaps) {

i hope this will help:

enter image description here

Gildas.Tambo
  • 22,173
  • 7
  • 50
  • 78