0

I am working on my first Angular app (yay!). I'm trying to have a generic modal component which can declare a title, custom content and any number action buttons. Background setup follows, then my questions at the bottom.

Here's how I'd like to use the directive.

<!-- in app/views/library/index.html -->
<!-- outside <modal> #i_work works great -->
<select id="i_work">
  <option value="{{libraryType.id}}" ng-repeat="libraryType in libraryTypes">{{libraryType.name}}</option>
</select>

<modal id="library_new" title="My Custom Title">
  <form>
    <!-- inside <modal> #i_dont_work doesn't work -->
    <select id="i_dont_work">
      <option value="{{libraryType.id}}" ng-repeat="libraryType in libraryTypes">{{libraryType.name}}</option>
    </select>
  </form>

  <modal-buttons>
    <!-- [toggle-modal] is another directive that shows/hides a given <modal> -->
    <button type="button" toggle-modal="library_new">cancel</button>

    <button type="submit" ng-click="addLibrary()">save</button>
  </modal-buttons>
</modal>

Here's the directive code:

// in app/scripts/directives/modal.js
'use strict';
angular.module('sampleAngularApp')
  .directive('modal', [function () {
    return {
      templateUrl: '/scripts/directives/modal.html',
      restrict: 'E',
      transclude: true,
      link: {
        pre: function preLink(scope, element, attrs) {
          scope.id = attrs.id;
          scope.title = attrs.title;
        },
        post: function preLink(scope, element, attrs) {
          // hacky-hack to transclude specific content into other targets.
          //   bound event handlers should be preserved (as implemented they are).
          //   this actually works
          var buttonWrap = element.find('modal-buttons');
          buttonWrap.children().each(function (index, button) {
            element.find('.modal-footer').append(button);
          });
          buttonWrap.remove();
        }
      }
    };
  }]);

... and the directive template:

<!-- in app/scripts/directives/modal.html -->
<div class="modal" role="dialog">
  <div class="modal-header">
    <span class="title">{{title}}</span>
    <button type="button" class="close" toggle-modal="{{id}}">close</button>
  </div>

  <!-- the contents of .modal-body should be everything inside <modal> above, -->
  <!--   except for <modal-buttons> -->
  <div class="modal-body" ng-transclude />

  <!-- the contents of .modal-footer should be the contents of <modal-buttons> -->
  <div class="modal-footer" />
</div>

Questions:

1) Consider the <select> elements in the controller's view above. #i_work renders correctly with <option>s just fine. #i_dont_work renders a <select> with no <option>s. libraryTypes seems to not be in scope inside <modal>. Why is that, and how can I fix it?

2) Is there a better way to transclude specific content into multiple targets? Google's Polymer project provides <content /> with an optional [select] attribute to provide multiple insert targets. Does Angular have anything like this? (See Polymer's website for more information.)

ravinggenius
  • 816
  • 1
  • 6
  • 14
  • Try to use a function to retrieve the libraryTypes or wrap it in an object. – jpmorin Oct 08 '13 at 18:26
  • I did a plunker: http://plnkr.co/edit/ZLGJhMIpt7DmRcBLPImd?p=preview, Can you tell me what is not working, since it seems that both select are filled. – jpmorin Oct 08 '13 at 18:35
  • As for your other question, there is no such thing built in Angular, but you can achieve it creating your own directives like you do! – jpmorin Oct 08 '13 at 18:44
  • @jpmorin: I don't know about `ng-include`, so I forked your example to show more what I'm actually trying (http://plnkr.co/edit/nNtvcTi0J3UblqXYcI87?p=preview). On plunker it works inside and outside the modal when `$scope.libraryTypes` is set synchronously, but not asynchronously. – ravinggenius Oct 08 '13 at 20:15
  • @jpmorin Meanwhile in my app it works synchronously and asynchronously when outside the modal, and fails synchronously and asynchronously inside the modal. – ravinggenius Oct 08 '13 at 20:18
  • Do you use the same controller for both? – jpmorin Oct 08 '13 at 20:21
  • I noticed you commented out `buttonWrap.remove();`. When I commented it back in, both selects (synchronous and asynchronous) in the modal stopped working. The synchronous select outside the modal continued to work. Removing `buttonWrap.remove();` in my app allows both selects to render correctly when `$scope.libraryTypes` is set synchronously, but not asynchronously. I don't know why. – ravinggenius Oct 08 '13 at 20:33
  • "Do you use the same controller for both?" - I don't understand your question. Both what? I'm guessing "yes" only because my app only has one controller so far. In my app and in plunker, I've tried setting `$scope.libraryTypes` synchronously and asynchronously in the same controller. – ravinggenius Oct 08 '13 at 20:36
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38824/discussion-between-raving-genius-and-jpmorin) – ravinggenius Oct 08 '13 at 20:38
  • Here is a link that I found to transclude in multiple elements (http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html). You will need to use the compile function of the directive, and explicitely define the transclude function in order to tell angular what you want to do exactly! – jpmorin Oct 08 '13 at 20:57
  • Possible duplicate of [AngularJS : How to transclude and have both isolate scope and parent scope?](http://stackoverflow.com/questions/27765924/angularjs-how-to-transclude-and-have-both-isolate-scope-and-parent-scope) – Paul Sweatte Nov 28 '15 at 07:22

0 Answers0