How do I integrate Foundation 3 in an AMD manner

2019-05-23 06:27发布

问题:

EDIT 4

There's a module defined in (from Foundation 3 package) app.js:

(function($, window, undefined) {
    'use strict';

    var $doc = $(document), Modernizr = window.Modernizr;

    $(document).ready(function() {
        $.fn.foundationAlerts ? $doc.foundationAlerts() : null;
        // ...
        $.fn.foundationClearing ? $doc.foundationClearing() : null;

        $('input, textarea').placeholder();
    });
    // touch support detction is omitted
})(jQuery, this);

I tried to interpret it to the next form:

BOOTSTRAP.JS

require.config({
    paths: {
        // other paths then..
        'foundation': '../libs/zurb'
    },
    shim: {
        'foundation/jquery.foundation.topbar': { deps: ['jquery'] },
        'foundation/jquery.foundation.accordion': { deps: ['jquery'] },
        // ..all that stuff..
        'foundation/jquery.placeholder': { deps: ['jquery'] }
    }
});

require(['domReady', 'app'], function(domReady, app) {
    domReady(function() {
        app.initialize();
    });
});

APP.JS

Well.. I found that this doesn't work as it was expected:

define(
    [
        'jquery',
        'underscore',
        'backbone',
        'routing/AppRouter',
        'foundation/modernizr.foundation',
        'foundation/jquery.foundation.accordion',
        // all that foundation scripts...
        'foundation/jquery.placeholder'
    ],
    function($, _, Backbone, AppRouter) {
        return {
            initialize: function() {
                var $doc = $(document);

                // these things fail
                $.fn.foundationAccordion ? $doc.foundationAccordion() : null;
                // ...
                $.fn.placeholder ? $('input, textarea').placeholder() : null;

                // this works great!
                $('#slider').orbit();

                // router/controller initialization
                AppRouter.initialize();
            }
        };
    }
);

When the page gets loaded one can see that foundation's ui elements don't work at all (accordion doesn't expand its panels etc).

When I entered $(document).foundationAccordion() in Chrome console (page had been loaded by the time) it enabled UI elements on a page.

Help!!

回答1:

Make sure 100% that all the DOM elements that you want to spice up with the foundation plugins are already in main DOM tree when you trigger their initializers. Seems to me that you are dynamically creating interface with backbone and templates but Foundation doesn't seem to be prepared with this kind of approach in mind. Looking at the code it looks like it's targeted more at static pages then highly dynamic one page apps.

Not an expert in using foundation but I would try to move the initialization of foundation scripts after the Router initialization where I assume first view gets rendered to confirm my assumption. I'm afraid though that it won't work automagically for the whole app and every time you redraw or create new view dynamically that uses the foundation components you'll have to reapply at least some of the scripts to the dom elements as they won't apply themselves to elements created dynamically later in the app lifecycle.

Some scripts are using delegation from document and should always work - like alerts component, but others like accordion for example will not work unless called after the accordion element (with mandatory accordion class) was inserted into the DOM tree - so being part of a detached node before backbone view was inserted into the main DOM tree doesn't count unless you modify the foundation plugin which doesn't support attaching accordion behavior to elements with different selectors or to elements in detached DOM nodes

I'd recommend you going over the source files of all foundation plugins to see how they work and how to tame them in context of a one page app as their implementation quality vary quite a lot from what i've seen in quick look. Check it out on their github.



回答2:

for non AMD modules, you need to explicitly specify the exports in the shim. I don't know the current content of those files, so it's a bit hard to figure just by looking at the loader code. Just a quick note, you're missing, in the define, the .js extension, unless they are already defined AMD, you need to provide the full path, including the extension.

requirejs will only pass in an argument to the callback if it's a module. if it isn't, you'll have to globally access it. You may do the defines by hand if you must for each non-module, but that defeats the purpose of require



回答3:

After rereading Tom's answer I finally got it! Though this is not a complete picture I'll share what I've got)

My bootstrap.js file has changed:

paths: {
    // things like json2, underscore, jquery, backbone are ommited..
    'fn': '../libs/zurb'
},
shim: {
    'fn/jquery.foundation.accordion': {
        deps: ['jquery'], exports: 'jQuery.fn.foundationAccordion'
    },
    // ...
    'fn/jquery.foundation.orbit': {
        deps: ['jquery'], exports: 'jQuery.fn.orbit'
}

So it was trully helpful to look inside each of foundation's scripts to see what they've got.

Then I was able to write the following:

define(['jquery', 'underscore', 'backbone'], function($, _, Backbone) {
    var AppRouter = Backbone.Router.extend({
        routes: {
            '': 'list',
            'products/page/:page': 'list'
        }
    });

    var renderSlider = function() {
        // pay attention here
        require(['views/SliderView', 'fn/jquery.foundation.orbit'], function(SliderView, orbit) {
            $('#slider-wrapper .twelve.columns').prepend(new SliderView().el);
            $('#slider').orbit();
        });
    };

    var renderProductsList = function(page) {
        require(['collections/ProductsList', 'views/ProductListView', 'fn/jquery.foundation.accordion'], function(ProductsList, ProductListView, foundationAccordion) {
            var p = page ? parseInt(page, 10) : 1;
            var productsList = new ProductsList();

            productsList.fetch({
                success: function() {
                    $("#content").html(new ProductListView({ model: productsList, page: p }).el);
                    // pay attention here
                    $(document).foundationAccordion();
                },
                error: function() {
                    console.log('error!');
                }
            });
        });
    }

    var initialize = function() {
        var appRouter = new AppRouter();

        appRouter.on('route:list', function(page) {
            renderSlider();
            renderProductsList(page);
        });

        Backbone.history.start();
    };

    return {
        initialize: initialize
    };
});

Then I just wrapped all of this within:

define(['jquery', 'underscore', 'backbone', 'routing/AppRouter'], function($, _, Backbone, AppRouter) {
    return {
        initialize: function() {
            AppRouter.initialize();
        }
    };
});

Voila!!!