Durandal and Zurb foundation, where should I initialize foundation?
Asked Answered
A

3

7

I have been using the basic Durandal HTML template but I have replaced Bootstrap with Zurb Foundation. Everything is working well, except for the issue where I can not open any modals, drop downs or anything requiring foundation to be initialized. Generally one would place the following in the body of your webpage to do this:

<script>
  $(document).foundation();
</script>

I have attempted to place this in the Index.html, Shell.html and even randomly in the viewmodel and view. Furthermore I have tried to init it from JS in the main.js, and the viewmodel itself in the activate section. None of this worked. The (hopefully very temporary) workaround that I have found is to actually init Foundation form my javascript, when clicking a button to open the modal:

$(document).foundation();
$("#myModal").foundation("reveal", "open");

Where am I going wrong?

My main.js for reference:

(function() {
  requirejs.config({
    paths: {
      text: "../lib/require/text",
      durandal: "../lib/durandal/js",
      plugins: "../lib/durandal/js/plugins",
      transitions: "../lib/durandal/js/transitions",
      knockout: "../lib/knockout/knockout-2.3.0",
      jquery: "../lib/jquery/jquery-1.9.1",
      foundation: "../lib/foundation/js/foundation/foundation",
      "foundation.abide": "../lib/foundation/js/foundation/foundation.abide",
      "foundation.accordion": "../lib/foundation/js/foundation/foundation.accordion",
      "foundation.alert": "../lib/foundation/js/foundation/foundation.alert",
      "foundation.clearing": "../lib/foundation/js/foundation/foundation.clearing",
      "foundation.dropdown": "../lib/foundation/js/foundation/foundation.dropdown",
      "foundation.interchange": "../lib/foundation/js/foundation/foundation.interchange",
      "foundation.joyride": "../lib/foundation/js/foundation/foundation.joyride",
      "foundation.magellan": "../lib/foundation/js/foundation/foundation.magellan",
      "foundation.offcanvas": "../lib/foundation/js/foundation/foundation.offcanvas",
      "foundation.orbit": "../lib/foundation/js/foundation/foundation.orbit",
      "foundation.reveal": "../lib/foundation/js/foundation/foundation.reveal",
      "foundation.tab": "../lib/foundation/js/foundation/foundation.tab",
      "foundation.tooltip": "../lib/foundation/js/foundation/foundation.tooltip",
      "foundation.topbar": "../lib/foundation/js/foundation/foundation.topbar"
    },
    shim: {
      jquery: {
        exports: "jQuery"
      },
      foundation: {
        deps: ["jquery"]
      },
      "foundation.abide": {
        deps: ["foundation"]
      },
      "foundation.accordion": {
        deps: ["foundation"]
      },
      "foundation.alert": {
        deps: ["foundation"]
      },
      "foundation.clearing": {
        deps: ["foundation"]
      },
      "foundation.dropdown": {
        deps: ["foundation"]
      },
      "foundation.interchange": {
        deps: ["foundation"]
      },
      "foundation.joyride": {
        deps: ["foundation"]
      },
      "foundation.magellan": {
        deps: ["foundation"]
      },
      "foundation.offcanvas": {
        deps: ["foundation"]
      },
      "foundation.orbit": {
        deps: ["foundation"]
      },
      "foundation.reveal": {
        deps: ["foundation"]
      },
      "foundation.tab": {
        deps: ["foundation"]
      },
      "foundation.tooltip": {
        deps: ["foundation"]
      },
      "foundation.topbar": {
        deps: ["foundation"]
      }
    }
  });

  define(["durandal/system", "durandal/app", "durandal/viewLocator", "foundation"], function(system, app, viewLocator, foundation) {
    system.debug(true);
    app.title = "Durandal Starter Kit";
    app.configurePlugins({
      router: true,
      dialog: true,
      widget: true
    });
    app.start().then(function() {
      viewLocator.useConvention();
      app.setRoot("viewmodels/shell", "entrance");
    });
  });

}).call(this);

shell.html:

<div>
    <nav class="top-bar hide-for-small">
        <ul class="title-area">
            <!-- Title Area -->
            <li class="name">
                <h4 style="color: white">KRS Template</h4>
            </li>
            <!-- Remove the class "menu-icon" to get rid of menu icon. Take out "Menu" to just have icon alone -->
            <li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
        </ul>
        <div class="top-bar-section">
            <!-- Right Nav Section -->
            <ul class="right" data-bind="foreach: router.navigationModel">
                <li data-bind="css: { active: isActive }">
                    <a data-bind="attr: { href: hash }, html: title"></a>
                </li>
            </ul>
        </div>
    </nav>
    <div class="container-fluid page-host" data-bind="router: { transition: 'entrance', cacheViews: true }"></div>
</div>

shell.js:

(function() {

  define(["plugins/router", "durandal/app", "foundation"], function(router, app, foundation) {
    return {
      router: router,
      activate: function() {
        router.map([
          {
            route: "",
            title: "Welcome",
            moduleId: "viewmodels/welcome",
            nav: true
          }, {
            route: "List",
            moduleId: "viewmodels/ListOfItems",
            nav: true
          }
        ]).buildNavigationModel();
        return router.activate();
      }
    };
  });

}).call(this);

Where the actual problem lies: ListOfItems.html:

<section>
    <div>
        <h2 data-bind="text: displayName" style="text-align: center"></h2>
        <div class="row">
            <div class="small-12 column">
                <div data-bind="foreach: items">
                    <div class="panel">
                        <div class="row">
                            <div class="small-6 column">
                                <b>Number: <span data-bind="text: NumberA"></span></b>
                                <br />
                                <b>Number B: <span data-bind="text: NumberB"></span></b>
                                <br />

                            </div>
                            <div class="small-6 column">
                                <b>Text A: <span data-bind="text: StringA"></span></b>
                                <br />
                                <b>Text B: <span data-bind="text: StringB"></span></b>
                                <br />
                                <b>Text C: <span data-bind="text: StringC"></span></b>
                                <br />
                            </div>
                            <div class="row">
                                <div class="small-12 column">
                                    <a class="button" data-bind="click: function () { $parent.openModal(this); }">Open Modal</a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div id="myModal" class="reveal-modal" data-reveal>
            <h2>Edit Item</h2>
            <p class="lead">Please alter the item</p>
            <input id="Stepper" type="number" data-bind="value: ModalNumberA" />
            <input id="Number1" type="number" data-bind="value: ModalNumberB" />
            <input type="text" placeholder="Scope name" data-bind="value: ModalStringA" />
            <input type="text" placeholder="Scope name" data-bind="value: ModalStringB" />
            <input type="text" placeholder="Scope name" data-bind="value: ModalStringC" />
            <a class="small button" data-bind="click: saveModalChanges">Save</a>
            <a class="small button" data-bind="click: closeModal">Close</a>
            <a class="close-reveal-modal">&#215;</a>
        </div>
    </div>
</section>

ListOfItems.js (generated from CoffeeScript):

(function() {
  var _this = this;

  define(["durandal/app", "knockout", "krs", "jquery", "foundation.reveal", "foundation.dropdown"], function(app, ko, krs, $, reveal, dropdown) {
    return {
      displayName: "List of Items",
      items: ko.observableArray([]),
      result: ko.observable(),
      ModalNumberA: ko.observable(),
      ModalNumberB: ko.observable(),
      ModalStringA: ko.observable(),
      ModalStringB: ko.observable(),
      ModalStringC: ko.observable(),
      ItemBeingEdited: ko.observable(),
      activate: function() {
        var _this = this;
        return callTheWebApi("http://localhost:54129/api/mocked/GetMockedViewModelList", "GET", "", function(result) {
          console.log(result);
          _this.items(result);
        });
      },
      openModal: function(item) {
        this.ItemBeingEdited(item);
        this.ModalNumberA(item.NumberA);
        this.ModalNumberB(item.NumberB);
        this.ModalStringA(item.StringA);
        this.ModalStringB(item.StringB);
        this.ModalStringC(item.StringC);
        $(document).foundation();
        return $("#myModal").foundation("reveal", "open");
      },
      saveModalChanges: function() {
        var itemBeingEdited,
          _this = this;
        itemBeingEdited = new Object();
        itemBeingEdited.NumberA = this.ModalNumberA();
        itemBeingEdited.NumberB = this.ModalNumberB();
        itemBeingEdited.StringA = this.ModalStringA();
        itemBeingEdited.StringB = this.ModalStringB();
        itemBeingEdited.StringC = this.ModalStringC();
        return callTheWebApi("http://localhost:54129/api/mocked/SaveMockedViewModel", "GET", "{'model':" + JSON.stringify(itemBeingEdited) + "}", function(success) {
          var templist;
          if (success) {
            _this.items()[_this.items().indexOf(_this.ItemBeingEdited())] = itemBeingEdited;
            templist = _this.items();
            _this.items(null);
            _this.items(templist);
            return $("#myModal").foundation("reveal", "close");
          }
        });
      },
      closeModal: function() {
        return $("#myModal").foundation("reveal", "close");
      }
    };
  });

}).call(this);

If anyone could point out where I am going wrong I would really appreciate it.

Archaimbaud answered 20/2, 2014 at 17:29 Comment(4)
Is there a reason that you wrap your view model define calls in a self executing function?Mcdaniel
Hi, I am developing in CoffeeScript, I think it is just a side effect of compiling. But TY, I will test your answer below and give feedback.Archaimbaud
Did you ever get this figured out?Bellda
We ended up going with bootstrap, but I had a workaround where I had to init it for each view model.Archaimbaud
C
2

When Foundation is initialized in the block below it requires all objects it binds methods to like modals, dropdowns, etc. to be within the DOM. This means that you can do the following to insure most of foundations functionality is present.

Initializing Code

$(document).foundation();

Giving it some KISS and keeping it simple you can add this to each 'viewModel' within the attached or compositionComplete callback as listed here. This will insure all the elements are within the DOM when Foundation is initialized.

Example

define(function(require){
  var backend = require('backend');

  return {
    customers:ko.observableArray([]),
    activate:function(){
       // This runs as soon as the viewModel is loaded
    }
    attached:function(){
       // This runs as soon as the view is full rendered
       $(document).foundation();
    }
  };
});

Additionally I use micro-templating for modal (which often are not in the DOM during Foundation initialization) and one trick I have found is to initialize foundation against the object itself.

Example

$('#dynamicElement').foundation();

This works for things that use data attributes such as modals and allows you to run Foundation methods against these objects.


Regarding the Foundation with KnockOut.js mentions in other answers. I to have looked into this as I love Foundation and KnockOut is my bread and butter, but the two just do not work nicely together. In the end you are rewriting all of Foundation to work with KO and well I personally do not have the time for that, but would love to see it happen.

If you really need the responsive design and all the bells and whistles of Foundation incorporated into Durandal you really need to use Bootstrap as it does offer those features in a compatible way.

Caliche answered 14/8, 2014 at 2:10 Comment(2)
HI Matt, thanks for the answer. I ended up using bootstrap with Durandal, I generated the HTML from PineGrow and popped it into the view and it worked like a charm. Now I am doing the same with Bootstrap and angular.Archaimbaud
That is probably the best thing to do at this time. I hope to see Foundation make it into Durandal sometime, but with Rob joining the Angular2.0 team it is not likely..Caliche
A
1

You may want to consider creating custom Knockout bindings for Foundation. Otherwise, you're going to wind up with DOM references all throughout your viewmodels, which is, in most cases, an anti-pattern for Durandal.

If you're not familiar with custom Knockout bindings, you can read about them at http://knockoutjs.com/documentation/custom-bindings.html. If you need help with custom bindings, return here, and I'll be happy to help.

If I recall, foundation() is a heavyweight initialization call. You need to do it only once. A simple custom binding placed on your shell.html would trivialize initialization--the Durandal/Knockout way.

Arbil answered 24/2, 2014 at 5:57 Comment(1)
Hi, Thanks for the answer, I have added the custom binding and I run in in my Main.js, it works when I have the observable in my view and viewmodel. It still fails when I try to move it higher up the chain into the shell or even index. It is not quite yet perfect but as close and efficient as I think I can get ATM.Archaimbaud
M
0

I believe that script tags get stripped out from views by durandal so that is probably why that is not working out.

Not sure of the structure or nature of your project but if your durandal app is being delivered via a web page you could initialise foundation in the web page.

Alternatively you could initialise it in the compositionComplete or attached methods of your shell view model.

Mcdaniel answered 21/2, 2014 at 7:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.