Если вы пытаетесь перейти от angular 1 (и вас не интересует angular 2), похоже, у вас есть два варианта: вы можете либо попробовать заменить отдельные компоненты и съесть angular изнутри, либо вы можете попробовать съесть это снаружи внутрь, заменив сначала роутер.
Мне интересно попытаться выйти из-под маршрутизатора angular 1, поэтому я действительно хочу выбрать последний вариант. Это позволяет мне рассматривать angular 1 как один из способов рендеринга компонентов, а не как фреймворк, отвечающий за все.
Технология, на которую меня больше всего интересуют, - это Elm, и я был вдохновлен докладом Ричарда Фельдмана о веб-компонентах в Elm, и мне казалось, что мы должны достичь чего-то подобного с компонентами / директивами angular 1.
Отобразить директиву angular просто, используя функцию «node» из модуля Elm Html.Attributes; проблема в том, что он ничего не будет делать, если фреймворк angular не узнает, что он был добавлен в DOM, и не получит возможность его скомпилировать.
Например, предположим, что у меня есть следующий угловой компонент:
angular.module('MyApp', []) .component('pageOne', { template: '<div>Page One</div>', controller: function PageOneController () { console.log('we are in page one'); } });
Я могу визуализировать это из Вяза так:
node "page-one" [] []
Но мне это не помогает, потому что angular ничего об этом не знает. Об этом нужно сказать angular.
Elm, конечно же, генерирует виртуальный DOM, и у нас нет очевидного способа узнать, когда реальный DOM был обновлен.
Наблюдатели за мутациями
Таким образом, с помощью решения маршрутизации на стороне клиента мы эффективно меняем местами содержимое одного корневого узла и из него при изменении маршрута. Мы можем использовать наблюдатель мутаций для отслеживания этого корневого узла, а затем дать команду angular компилировать содержимое при изменении.
Что-то вроде этого:
var root = document.getElementById('root'); var observer = new MutationObserver(triggerDigest); observer.observe(root, { childList: true, subtree: true }); function triggerDigest() { var $body = angular.element(document.body); var $rootScope = $body.injector().get('$rootScope'); var $compile = $body.injector().get('$compile'); $rootScope.$apply(function() { $compile($body)($rootScope); }); }
Итак, здесь мы получаем ссылку на корневой элемент, в который мы будем встраивать приложение Elm. Мы создаем наблюдателя мутаций, который будет вызывать функцию triggerDigest. Эта функция получает ссылку на $ rootScope angular и его службу компиляции $. Затем он компилирует все дерево в соответствии с корневой областью.
Бесконечные петли
Но у нас есть проблема. В настоящее время мы отслеживаем все поддерево, и компиляция директивы angular вызовет новые мутации и вызовет еще одну компиляцию. Это приведет к бесконечной спирали компиляции и мутации и уничтожит страницу.
Одно из решений - заставить Elm сообщать нам, когда мы должны начать наблюдение, а затем прекратить наблюдение, как только мы скомпилировали. Это можно сделать с помощью портов. Мы можем создать исходящий порт в Elm следующим образом:
port watchDom : String -> Cmd msg
Затем нам нужно отправить сообщение на этот порт при изменении URL-адреса:
update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of UrlChange location -> ( { model | route = Url.parsePath route location } , (watchDom "") )
Затем мы можем изменить наш javascript следующим образом:
var root = document.getElementById('root'); var app = Elm.Main.embed(root); var observer = new MutationObserver(triggerDigest); app.ports.watchDom.subscribe(function(msg) { observer.observe(root, { childList: true, subtree: true }); }); function triggerDigest() { var $body = angular.element(document.body); var $rootScope = $body.injector().get('$rootScope'); var $compile = $body.injector().get('$compile'); $rootScope.$apply(function() { $compile($body)($rootScope); }); observer.disconnect(); }
И это разорвет петлю для нас и оставит нас с началом жизнеспособного решения. Не уверен, насколько это эффективно, но, похоже, по крайней мере, работает.
Одно предостережение при использовании портов таким образом заключается в том, что это означает, что отладчик 0.18 не будет работать должным образом. Можно было бы избежать использования портов, обернув функцию `history.pushState` и отслеживая событие` popstate`. Одним из преимуществ портов является то, что они позволяют нам предоставлять некоторые метаданные о маршруте, по которому мы движемся, например. содержит ли он контент, который, возможно, нуждается в компиляции.
Есть еще проблема
Во части второй я исследую, как работать с внутренними угловыми ссылками.
Полный исходный код этого доказательства концепции можно найти здесь.