MiroHibler / meteor-preloader

A Meteor "lazy-loader" for external .js and .css libraries
75 stars 3 forks source link

Loading via require AMD modules #9

Closed tcastelli closed 9 years ago

tcastelli commented 9 years ago

Hello, I have tried to use meteor-preloader for a js library based on dojo (which internally makes the use of require to load different modules). The code in a normal html file should be something like this:

<script src="http://js.arcgis.com/3.12/"></script>
    <script>
      var map, locator;
      require([
        "esri/map", "esri/tasks/locator", "esri/graphic",
        "esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/Font", "esri/symbols/TextSymbol",
        "dojo/_base/array", "esri/Color",
        "dojo/number", "dojo/parser", "dojo/dom", "dijit/registry",
        "dijit/form/Button", "dijit/form/Textarea",
        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"
      ], function(
        Map, Locator, Graphic,
        InfoTemplate, SimpleMarkerSymbol, 
        Font, TextSymbol,
        arrayUtils, Color,
        number, parser, dom, registry
      ) {    
        map = new Map("map", { ...}) .... etc etc          
         }
   );
</script>

When i try to load the javascript file with meteor-preload, on sync, the javascript file gets loaded, but when i do the require on the template.rendered or template.created the modules are not loaded since it cant find require dependencies. My way of solving this is using Modernizr meteor package to load all those parts that need that javascript library, but it would be really useful to do it with meteor-preload and avoid having to download the javascript and the modules everytime (i have the load on a dynamic form) This is how I'm trying to do it with preloader:

Router.map(function() {
    this.route('xxxxxx', {
        path: '/xxxxx',
        template: 'xxxxxx',
        layoutTemplate: 'layout',
        waitOn: function() { return [subs.subscribe('xxxx'),
            subs.subscribe(xxxxx')];
             },
        controller: 'PreloadController',
        'preload': {  

            'sync'   : ["http://js.arcgis.com/3.12/"];              
            ,

            'onSync' : function ( filePath ) {
                var file = filePath.replace( /\?.*$/,"" ).replace( /.*\//,"" );
                switch ( file ) {                  
                    default:{
                        try {
                            return !!esri;
                        } catch ( error ) {
                            return false;
                        }                        
                    }
                }
            },..... etc

and then in my template.created code:

Template.xxxxx.created = function(){
  require([
        "esri/map", "esri/tasks/locator", "esri/graphic",
        "esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/Font", "esri/symbols/TextSymbol",
        "dojo/_base/array", "esri/Color",
        "dojo/number", "dojo/parser", "dojo/dom", "dijit/registry",
        "dijit/form/Button", "dijit/form/Textarea",
        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"
      ], function(
        Map, Locator, Graphic,
        InfoTemplate, SimpleMarkerSymbol, 
        Font, TextSymbol,
        arrayUtils, Color,
        number, parser, dom, registry
      ) {     

        map = new Map("map", { ...}) .... etc etc          
         }
   );

Many thanks in advance for anny suggestion on how to do that, the complete code example can be found here https://developers.arcgis.com/javascript/jssamples/locator_address.html

MiroHibler commented 9 years ago

Am I missing some code example here (after "...The code in a normal html file should be something like this:")?

tcastelli commented 9 years ago

true i edited the code and seems like it got erased, fixing it now! (finally seems like it looks okay now) :)

MiroHibler commented 9 years ago

Boy, this one is tough... I'm not even sure if preloader is a good match for this... Anyway, I'd need some time to wrap my head around it...

MiroHibler commented 9 years ago

Ok, I think I've got it! :)

I had to update Preloader though, by adding some new methods/hooks (and also found a few bugs here and there ;)

For testing purposes I used the example from the ArcGis' site.

So, first update miro:preloader:

meteor update miro:preloader    // just `meteor update` will do as well

Then, try this (there's even online demo for your convenience ;) :

/* main.css */

html, body {
    height: 100%; width: 100%;
    margin: 0; padding: 0;
}

#map {
    padding:0;
    border:solid 1px #343642;
    margin:5px 5px 5px 0px;
}

#leftPane {
    width:20%;
    border-top: solid 1px #343642;
    border-left: solid 1px #343642;
    border-bottom: solid 1px #343642;
    margin:5px 0px 5px 5px;
    color: #343642;
    font:100% Georgia,"Times New Roman",Times,serif;
    /*letter-spacing: 0.05em;*/
}
<!-- main.html -->

<head>
    <title>ArcGis</title>
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600' rel='stylesheet' type='text/css'>
</head>

<template name="mainWindow">
    <div id="mainWindow" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'sidebar', gutters:false" style="width:100%; height:100%;">

        <div id="leftPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'left'">
        Enter an address then click the locate button to use a sample address locator to return the location for
        street addresses in the United States.
        <br>

        <textarea id="address">380 New York St, Redlands</textArea>
            <br>
            <button id="locate" data-dojo-type="dijit/form/Button">Locate</button>
        </div>

        <div id="map" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'"></div>
    </div>
</template>
// main.js

var routePath = 'http://js.arcgis.com/3.12/',
    routeLoaded = false,
    loadHandler = function () {
        routeLoaded = true;
    };

Router.route( '/', {
    name: 'home',
    template: 'mainWindow',
    controller: PreloadController,

    preload: {
        // This is new:
        timeOut: 5000,
        styles: [
            routePath + 'dijit/themes/claro/claro.css',
            routePath + 'esri/css/esri.css'
        ],
        sync: routePath,
        // ...this is also new...
        onBeforeSync: function ( fileName ) {
            if ( fileName === routePath ) {
                var script    = document.createElement( 'script' );

                script.rel    = 'preload javascript';
                script.type   = 'text/javascript';
                script.src    = routePath;
                script.onload = loadHandler;

                document.body.appendChild( script );

                return false;
            }
        },
        onSync: function ( fileName ) {
            if ( routeLoaded && fileName === routePath ) {
                // Check for Dojo
                return !!require && !!define;
            }
        },
        // ...and this is also new (optional)
        onAfterSync: function ( fileName ) {
            // We'll probably want to reload the main library,
            // so don't mark it cached (just testing...)
            return false;
        }
    }
});

Template.mainWindow.rendered = function () {
    var map, locator;

    if ( routeLoaded ) {
        require([
            "esri/map", "esri/tasks/locator", "esri/graphic",
            "esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",
            "esri/symbols/Font", "esri/symbols/TextSymbol",
            "dojo/_base/array", "esri/Color",
            "dojo/number", "dojo/parser", "dojo/dom", "dijit/registry",

            "dijit/form/Button", "dijit/form/Textarea",
            "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"
        ], function (
            Map, Locator, Graphic,
            InfoTemplate, SimpleMarkerSymbol,
            Font, TextSymbol,
            arrayUtils, Color,
            number, parser, dom, registry
        ) {
            parser.parse();

            map = new Map( "map", {
                basemap: "streets",
                center: [-93.5, 41.431],
                zoom: 5
            });

            locator = new Locator("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");
            locator.on("address-to-locations-complete", showResults);

            // listen for button click then geocode
            registry.byId("locate").on("click", locate);

            map.infoWindow.resize(200,125);

            function locate() {
                map.graphics.clear();
                var address = {
                    "SingleLine": dom.byId("address").value
                };
                locator.outSpatialReference = map.spatialReference;
                var options = {
                    address: address,
                    outFields: ["Loc_name"]
                };
                locator.addressToLocations(options);
            }

            function showResults(evt) {
                var symbol = new SimpleMarkerSymbol();
                var infoTemplate = new InfoTemplate(
                    "Location",
                    "Address: ${address}<br />Score: ${score}<br />Source locator: ${locatorName}"
                );
                symbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
                symbol.setColor(new Color([153,0,51,0.75]));

                var geom;
                arrayUtils.every(evt.addresses, function(candidate) {
                    console.log(candidate.score);
                    if (candidate.score > 80) {
                        console.log(candidate.location);
                        var attributes = {
                            address: candidate.address,
                            score: candidate.score,
                            locatorName: candidate.attributes.Loc_name
                        };
                        geom = candidate.location;
                        var graphic = new Graphic(geom, symbol, attributes, infoTemplate);
                            //add a graphic to the map at the geocoded location
                            map.graphics.add(graphic);
                            //add a text symbol to the map listing the location of the matched address.
                            var displayText = candidate.address;
                            var font = new Font(
                                "16pt",
                                Font.STYLE_NORMAL,
                                Font.VARIANT_NORMAL,
                                Font.WEIGHT_BOLD,
                                "Helvetica"
                            );

                            var textSymbol = new TextSymbol(
                                displayText,
                                font,
                                new Color("#666633")
                            );
                            textSymbol.setOffset(0,8);
                            map.graphics.add(new Graphic(geom, textSymbol));
                            return false; //break out of loop after one candidate with score greater  than 80 is found.
                        }
                    }
                );
                if ( geom !== undefined ) {
                    map.centerAndZoom(geom, 12);
                }
            }
        });
    }
};

I hope this will help :)

tcastelli commented 9 years ago

thank you very much! gonna try it right now! :) :) update: After playing with the new hooks for a while i could finally manage to implement the loading with multiple async and sync files and a complex autoform with dynamic changes! Thanks again, it works like a charm!

MiroHibler commented 9 years ago

Great! I'm glad to hear that :)

And thank you for using it (and making me fix some bugs and add some new functionalities ;)