1

Я пытаюсь построить модульный ngInclude. Под этим я подразумеваю, что ngIncluded JSP будет тянуть во всех зависимостях, которые ему нужны, и перекомпилировать себя перед рендерингом.Угловые множественные ngIncludes с заменой на страницу Losing Scope (?)

Этот же файл используется несколько раз на одной странице. На главной странице есть несколько вкладок - все они используют этот ngInclude только с некоторыми различными параметрами строки запроса. Кроме того, на каждой странице у меня есть «предварительная версия» включенного файла, а затем, когда они нажимают на предварительный просмотр, модальный всплывает, что снова ngIncludes тот же файл, но имеет другой параметр строки запроса, который помещает его в режим редактирования ,

Я получил это, чтобы работать везде, кроме IE9 (gah), и, конечно же, я должен поддерживать IE9, и мне приходится реорганизовывать все. Из-за желания сделать это ngInclude модульным и иметь его, включая все свои зависимости, каждый раз, когда это было объявлено, он снова вносил те же зависимости. Это было прекрасно в более современных браузерах, которые обрабатывали это более изящно, но в IE9 он взорвался бы multiple directives asking for template или multiple directives asking for transclusion. Единственные выводы, которые я мог найти, которые были применимы к моей ситуации, показали, что это связано с тем, что зависимости затягиваются в несколько раз. И с тем, что я смог до сих пор работать, я подтвердил, что это так.

Итак, вот я. Я пытаюсь выполнить эту работу, только однажды введя зависимости (с RequireJS). Я не использую маршруты. Я работаю над начальной загрузкой страницы, но как только я меняю вкладки или открываю редактирующий модал (оба действия вызывают другой ngInclude), я ничего не получаю. Я буквально получаю пустую страницу.

Я положил console.log s и вижу, что порядок операций такой, какой он есть (попадает в мою директиву перекомпиляции, он делает все на всем протяжении $compile, а затем удаляются внутренние директивы). Я также могу использовать консоль браузера для доступа к $('selectorForController').scope().items или $('selectorForController').scope().params и подтвердить, что у них есть данные, которые я ожидаю. Проблема в том, что по какой-то причине представление не обновляется (это то, что я ожидал от запуска $compile).

App Определение:

var providers = {}; 
var myApp = angular.module('myApp', ['ngSanitize', 'ngAnimate', 'ngResource', 'pascalprecht.translate', 'angularFileUpload', function($controllerProvider, $compileProvider, $provide) { 
    providers = { 
     $controllerProvider : $controllerProvider, 
     $compileProvider : $compileProvider, 
     $provide : $provide 
    }; 
}]); 
var queueLen = myApp._invokeQueue.length; 

ngIncluded JSP:

<jsp:include page="profileIncludeListTemplate.jsp"/> <%-- Contains the profileItemList.jsp ng-template --%> 
<jsp:include page="profileIncludePanelTemplate.jsp"/> <%-- Contains the profileItemPageRightPanel.jsp ng-template --%> 

<script type="text/javascript"> 
    // Define itemsData and params here 
</script> 

<%-- One of my iterations was trying to use a directive I called lazyscript to load in all of the dependencies and then recompile, but that didn't work --%> 
<%--<lazyscript data-src="/assets/js/profile/profileItemPageService.js"></lazyscript>--%> 
<%--<lazyscript data-src="/assets/js/profile/profileItemService.js"></lazyscript>--%> 
<%--<lazyscript data-src="/assets/js/profile/profileIncludeController.js"></lazyscript>--%> 
<%--<lazyscript data-src="/assets/js/profile/profileIncludeDirectives.js"></lazyscript>--%> 

<%-- Another iteration was trying to get it to work this way, where I also wrote a directive to overload the default script tag and recompile if necessary, but that didn't work either --%> 
<%--<script src="/assets/js/profile/profileItemPageService.js" type="text/javascript"></script>--%> 
<%--<script src="/assets/js/profile/profileItemService.js" type="text/javascript"></script>--%> 
<%--<script src="/assets/js/profile/profileIncludeDirectives.js" type="text/javascript"></script>--%> 
<%--<script src="/assets/js/profile/profileIncludeController.js" type="text/javascript"></script>--%> 

<%-- I can't define the ngController here because it will throw an error on initial load and stop the rest of the execution, so I have the recompile apply it --%> 
<%-- So I ultimately settled on this, which defines which dependencies need to be pulled in so that I can hand that list to Require --%> 
<div 
    data-recompile 
    data-recompile-controller="ProfileIncludeCtrl" 
    data-recompile-dependencies="/assets/js/profile/profileItemPageService.js,/assets/js/profile/profileItemService.js,/assets/js/profile/profileIncludeController.js,/assets/js/profile/profileIncludeDirectives.js" 
    data-recompile-finished="false" 
    class="profile-include" 
    data-ng-class="{'form-horizontal': !params.edit}"> 

    <profile-item-list-directive></profile-item-list-directive> 
    <profile-item-page-right-panel-directive></profile-item-page-right-panel-directive> 
</div> 

Перекомпилируйте Директива:

myApp.directive('recompile', function($window, $q) { 
    return { 
     restrict: 'A', 
     link: function ($scope, elem, attrs) { 
      if($(elem).attr('data-recompile-finished') == 'true') return; 
      var dependencies = $(elem).attr('data-recompile-dependencies').split(','); 

      function recompiler() { 
       $(elem).attr('data-recompile-finished', 'true'); 
       $(elem).attr('data-ng-controller', $(elem).attr('data-recompile-controller')); // Link the angular controller 

       var queue = myApp._invokeQueue; 
       for (var i = queueLen; i < queue.length; i++) { 
        var call = queue[i]; 
        var provider = providers[call[0]]; 
        if (provider) { 
         provider[call[1]].apply(provider, call[2]); 
        } 
       } 

       if ($('[data-ng-app="myApp"]').injector()) { 
        $('[data-ng-app="myApp"]').injector().invoke(function ($compile, $rootScope) { 
         $compile($(elem))($rootScope); 
        }); 
       } 
      } 

      requirejs(dependencies, recompiler); 
     } 
    }; 
}); 

Соответствующая часть внешнего контроллера (определено в profileIncludeController.js):

myApp.controller('ProfileIncludeCtrl', function($scope, $rootScope, $resource, $timeout, ProfileItemService, ProfileItemPageService) { 
    $scope.items = []; 
    $scope.params = []; 

    $scope.params = buildParamsObject(params); 
    $scope.items = itemsData.data || []; 
}); 

Соответствующие части директив (определены в profileIncludeDirectives.js). Эти директивы транслируются, потому что они должны иметь доступ к области доступа друг друга, а также к некоторым функциям, которые находятся в ProfileIncludeCtrl, потому что эти функции действительно не принадлежали ни одной из директив.

myApp.directive('profileItemListDirective', function($rootScope, $timeout) { 
    return { 
     restrict : 'E', 
     templateUrl : 'profileItemList.jsp', 
     replace: true, 
     transclude : true, 
     scope : true, 
     controller : function($scope, ProfileItemService, ProfileItemPageService) { 
      console.log("listDirective controller"); 
     }, 
     link : function($scope, element, attrs) { 
      console.log("listDirective link"); 
     } 
    }; 
}); 

myApp.directive('profileItemPageRightPanelDirective', function($rootScope, $timeout) { 
    return { 
     restrict : 'E', 
     templateUrl : 'profileItemPageRightPanel.jsp', 
     replace: true, 
     transclude : true, 
     scope : true, 
     controller : function($scope, ProfileItemService, ProfileItemPageService) { 
      console.log("panelDirective controller"); 
     }, 
     link : function($scope, element, attrs) { 
      console.log("panelDirective link"); 
      // $scope.params is not defined here, but I need and expect it to be 
     } 
    }; 
}); 

Любые указания будут оценены!

ответ

2

Оказалось, что я смог исправить это, слегка изменив функцию перекомпиляции. Использование службы $controller и установка атрибута данных $ngControllerController для детей - основная идея, которая это сделала.

function recompiler() { 
    var ctrl = $elem.attr('data-recompile-controller'); 
    $elem.attr('data-ng-controller', ctrl); // This is for aesthetics only 

    $elem.removeAttr("data-recompile") 
      .removeAttr("recompile") 
      .removeAttr("data-recompile-controller") 
      .removeAttr("recompile-controller") 
      .removeAttr("data-recompile-dependencies") 
      .removeAttr("recompile-dependencies"); 

    var queue = myApp._invokeQueue; 
    for (var i = 0; i < queue.length; i++) { 
     var call = queue[i]; 
     var provider = providers[call[0]]; 
     if (provider) { 
      provider[call[1]].apply(provider, call[2]); 
     } 
    } 

    var templateCtrl = $controller(ctrl, { $scope: $scope }); 
    $elem.children().data('$ngControllerController', templateCtrl); 
    $compile($elem.contents())($scope); 
} 
Смежные вопросы