문제

I'm trying to implement Google maps in a Jade template. Using KeystoneJS as a CMS, I have a number of "profiles" (basically people with their addresses) that I want to add to a map as markers.

block js
  script.
        var map;
        function initialize() {
            var mapOptions = {
              center: new google.maps.LatLng(51.0360272, 3.7359072),
              zoom: 8
            };
            map = new google.maps.Map(document.getElementById("map-canvas"),
                mapOptions);

        }

        google.maps.event.addDomListener(window, 'load', initialize);

block content
   .container
       script(src='https://maps.googleapis.com/maps/api/js?key=<GOOGLE_API_KEY>&sensor=false')

     if data.profiles
        each profile in data.profiles
            #{new google.maps.Marker({position: new google.maps.LatLng(profile.address.geo[1], profile.address.geo[0]), map: map, title: profile.name.full})}

    div(id="map-canvas", style="width:100%; height:700px;")

The map shows correctly but when I add the 'each' code block I get an error "Cannot read property 'maps' of undefined".

How can I add a piece of js code that executes on 'each' in Jade?

도움이 되었습니까?

해결책

You're really close, the only problem is that the inside of the variable, i.e. #{this_stuff} is all executed in jade's context (which won't have the google object, as this is client-side).

It's a bit tricky because you're dealing with two completely different javascript environments here: server side and client side.

So you need to output server-side variables in your jade, into javascript code that will be executed client-side.

On a related note, you can use Jade variable syntax in script blocks, but can't do other things (like loops).

Firstly, let's clean it up so all your script tags are in the js block (which, assuming you're using the example KeystoneJS templates would be down the bottom of the <body> tag) and get those profiles being generated correctly:

block js
    script(src='https://maps.googleapis.com/maps/api/js?key=<GOOGLE_API_KEY>&sensor=false')
    script.
        var map;
        function initialize() {
            var mapOptions = {
                center: new google.maps.LatLng(51.0360272, 3.7359072),
                zoom: 8
            };
            map = new google.maps.Map(document.getElementById("map-canvas"),
                mapOptions);
        }

        google.maps.event.addDomListener(window, 'load', initialise);

    if data.profiles
        each profile in data.profiles
            script.
                new google.maps.Marker({
                    position: new google.maps.LatLng(#{profile.address.geo[1]}, #{profile.address.geo[0]}),
                    map: map,
                    title: "#{profile.name.full}"
                });

block content
   .container
        div(id="map-canvas", style="width:100%; height:700px;")

This is getting closer (and the Jade will generate what you're expecting now), but it won't work (yet) because you're potentially adding the markers to the map before the initialize function has run.

It's also not escaping the values, so things like a " character in the name would cause a syntax error.

A more robust way of doing it would be to populate a client-side array then loop over that after the map has been created. We'll also use JSON.stringify to make sure the values are escaped properly.

block js
    script(src='https://maps.googleapis.com/maps/api/js?key=<GOOGLE_API_KEY>&sensor=false')
    script.
        var map,
            profiles = [];

        function initialize() {
            var mapOptions = {
                center: new google.maps.LatLng(51.0360272, 3.7359072),
                zoom: 8
            };
            map = new google.maps.Map(document.getElementById("map-canvas"),
                mapOptions);

            for (var i = 0; i < profiles.length; i++) {
                new google.maps.Marker({
                    position: new google.maps.LatLng(profiles[i].geo[1], profiles[i].geo[0]),
                    map: map,
                    title: profiles[i].name
                });
            }
        }

        google.maps.event.addDomListener(window, 'load', initialise);

    if data.profiles
        each profile in data.profiles
            script.
                profiles.push({
                    geo: !{JSON.stringify(profile.address.geo)},
                    name: !{JSON.stringify(profile.name.full)}
                });

block content
   .container
        div(id="map-canvas", style="width:100%; height:700px;")

Note the change to !{variable} so the JSON isn't escaped

Finally, I'd recommend building up the profiles array in your route .js file for the view, rather than doing it in the jade template. It's a lot cleaner, and you won't end up with heaps of <script> tags in your page.

So your route would look something like this (I am assuming a fair bit to give you the idea, and using underscore to make the code neater than vanilla javascript)

var keystone = require('keystone'),
    _ = require('underscore');

exports = module.exports = function(req, res) {

    var view = new keystone.View(req, res),
        locals = res.locals;

    // Load the profiles
    view.query('profiles', keystone.list('Profile').model.find());

    // Create the array of profile markers
    view.on('render', function(next) {
        locals.profileMarkers = locals.profiles ? _.map(locals.profiles, function(profile) {
            return { geo: profile.address.geo, name: profile.name.full };
        }) : [];
        next();
    });

    // Render the view
    view.render('profiles');

}

Then in your view template:

block js
    script(src='https://maps.googleapis.com/maps/api/js?key=<GOOGLE_API_KEY>&sensor=false')
    script.
        var profileMarkers = !{JSON.stringify(profileMarkers)},
            map;

        function initialize() {
            var mapOptions = {
                center: new google.maps.LatLng(51.0360272, 3.7359072),
                zoom: 8
            };
            map = new google.maps.Map(document.getElementById("map-canvas"),
                mapOptions);

            _.each(profileMarkers, function(profile) {
                new google.maps.Marker({
                    position: new google.maps.LatLng(profile.geo[1], profile.geo[0]),
                    map: map,
                    title: profile.name
                });
            });
        }

        google.maps.event.addDomListener(window, 'load', initialise);

block content
   .container
        div(id="map-canvas", style="width:100%; height:700px;")

다른 팁

"Cannot read property 'maps' of undefined"

Cannot help with this, seems that google maps fail to initialize or something like that.

But I can tell you that if you want to execute JavaScript in your Jade template, you just need to start the line with a dash: var x = Math.random()

I think there is (another) error in the line which starts with #{new google.maps.Maker....
This is not JS, it's Jade, and the result the the constructor would be used as HTML tag, I think you don't want that.

Here is an example, you can paste it into this online editor on http://jade-lang.com/demo/

- var ar = [1,2,3]
ul
  each item in ar
    - var x = Math.random()*item
    li= x

You can check out my gist for some Jade syntax hints: https://gist.github.com/timaschew/7543cf3d0c455f686784

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top