Вы все знаете Twitter Bootstrap , не так ли? Это отличная библиотека, которая позволяет вашему веб-приложению выглядеть довольно хорошо, не тратя много времени на CSS. Мы используем дизайн на основе Bootstrap в нашем текущем проекте, в котором также используется AngularJS, и, поскольку мы планируем создать много новых страниц, мы решили, что чем раньше мы представим некоторые компоненты нашего пользовательского интерфейса, тем лучше. И, что не должно быть сюрпризом, навигационная панель, естественно, стала первым кандидатом.
обзор
Итак, в этом посте я собираюсь показать, как вывести Twitter Bootstrap Navbar в отдельный гибкий AngularJS-компонент. Весь исходный код доступен в моем репозитории GitHub , поэтому все должно быть легко отслеживать и повторять.
Базовая настройка
Чтобы начать с основ, я подготовил три страницы, которые используют одну и ту же панель навигации. Эти страницы скоро станут нашим супер простым веб-приложением, использующим AngularJS:
Следующим шагом будет добавление AngularJS и преобразование трех страниц в одно небольшое приложение. Это сделано в этом коммите . И после этого мы готовы начать работу над нашим компонентом Navbar.
проблема
На каждой странице мы видим очень похожий HTML-фрагмент, отображающий элемент navbar.
<div class="navbar navbar-inverse"> <div class="navbar-inner"> <div class="container"> <a class="brand">Example App</a> <div class="nav-collapse"> <ul class="nav"> <li class="active"><a href="home.html">Home</a></li> <li><a href="second.html">Second</a></li> <li><a href="third.html">Third</a></li> </ul> </div> </div> </div> </div>
Разница лишь в том, где находится активный класс, который выделяет ссылку. И каждый раз, когда мы видим такое повторение, наш СУХОЙ детектор должен начать звонить и не останавливаться, пока мы не сделаем что-нибудь с этим.
Простая директива
Для начала мы переместим этот фрагмент кода в директиву AngularJS. Сначала это будет только «тупой» фрагмент кода, который просто отображает один и тот же HTML в разных местах. Весь html из перечисленного выше теперь находится в файле components / bootstrapNavbar.html.
Следующее, что нам нужно, это объявление директивы в файле JS:
angular.module("navbarapp", ["controllers"]) .directive("bootstrapNavbar", function() { // (1) return { restrict: "E", // (2) replace: true, // (3) transclude: true, // (4) templateUrl: "bootstrapNavbar.html" // (5) }}); ;
Что здесь происходит:
- мы объявляем директиву, имейте в виду, что имя директивы, которую вы используете в коде, будет boostrap-navbar, а camelCase преобразуется в — символьный.
- restrict: E — означает, что директива используется в качестве элемента, есть и другие опции: C-класс, A-атрибут, больше на документах
- replace: true — означает, что разметка из шаблона полностью заменит элемент директивы в разметке страницы
- transclude: true — что это значит … ты лучше скажи мне, я не могу описать более смутно, что это делается в документах
- templateUrl — файл, в котором мы храним содержимое нашего шаблона
После этого мы должны заменить HTML-разметку navbar на каждой странице чем-то вроде:
<bootstrap-navbar></bootstrap-navbar>
Теперь пришло время проверить это. К сожалению, есть одна проблема. Политика безопасности не позволяет браузеру выполнять вызовы XHR (Ajax) из файла html, хранящегося на нашем диске. Поэтому нам нужно настроить облегченный httpServer для предоставления наших файлов браузеру. Вы можете сделать это, используя Jetty или что-нибудь подобное. Я попробовал один из Python, и он работал без проблем. В общем, перейдите в каталог, где хранятся все файлы, выполните:
python -m SimpleHTTPServer 9000
а затем откройте браузер, используя адрес http: // localhost: 9000 / home.html . Вы должны увидеть ту же страницу, что и раньше, но теперь она использует нашу директиву.
Гибкость ++
Итак, у нас есть первая директива, ура! Но его IQ не так высок. Если вы перейдете на вторую и третью страницу, вы заметите, что она всегда отображает ссылку на домашнюю страницу как активную, что не совсем верно для всех страниц. И в этом параграфе мы постараемся сделать его умнее.
Во-первых, давайте добавим атрибут current-tab к использованию нашей директивы:
<bootstrap-navbar current-tab="Second"></bootstrap-navbar>
Переданное значение должно совпадать с именем ссылки в навигационной панели, поэтому в нашем примере приложения это должно быть: Home, Second или Third.
Следующим шагом является подготовка нашего файла шаблона для легкой модификации. Поскольку мы будем использовать селекторы jQuery, чтобы найти ссылку для активации, мы удалим активный класс и добавим атрибут id к элементу ul, чтобы упростить поиск с помощью селектора:
<div class="navbar navbar-inverse"> <div class="navbar-inner"> <div class="container"> <a class="brand">Example App</a> <div class="nav-collapse"> <ul class="nav" id="navigation-tabs"> <!-- id added here --> <li><a href="../home.html">Home</a></li> <!-- removed class='active' --> <li><a href="../second.html">Second</a></li> <li><a href="../third.html">Third</a></li> </ul> </div> </div> </div> </div>
Основная логика
Теперь последнее, что нам нужно сделать, это немного магии селектора jQuery с небольшим добавлением магии AngularJS.
В угловых директивах есть функция compile, которая позволяет изменять разметку шаблона директивы, поэтому мы будем использовать ее здесь. Эта функция получает значение параметров, определенных для использования компонента на наших страницах, поэтому в основном мы имеем доступ ко всем атрибутам, которые объявлены там, где используется наш компонент navbar, в частности, к атрибуту current-tab (снова преобразованному в currentTab внутри файла JS). ).
Поэтому, когда мы знаем, какая ссылка должна быть выделена, селекторы jQuery выполняют свою работу:
angular.module("navbarapp", ["controllers"]) .directive("bootstrapNavbar", function() { return { restrict: "E", replace: true, transclude: true, templateUrl: "components/bootstrapNavbar.html", compile: function(element, attrs) { // (1) var li, liElements, links, index, length; liElements = $(element).find("#navigation-tabs li"); // (2) for (index = 0, length = liElements.length; index < length; index++) { li = liElements[index]; links = $(li).find("a"); // (3) if (links[0].textContent === attrs.currentTab) $(li).addClass("active"); // (4) } } }}); ;
Итак, что мы делаем здесь:
- определить функцию компиляции, которая передается вещам, элементу, для которого она вызывается (разметка шаблона в нашем случае) и attrs, объект со всеми атрибутами, объявленными там, где использовалась наша директива.
- выбрать все элементы списка в узле # navigation-tabs
- найти все ссылки там, это будет только одна
- если текстовое содержимое ссылки равно атрибуту currentTab, мы знаем, что это ссылка для выделения, поэтому мы просто добавляем туда соответствующий класс CSS
Это самое простое рабочее решение. Конечно, мы могли бы добавить некоторую проверку ошибок, но я хотел показать простой подход к внедрению гибких компонентов в приложения AngularJS.
Резюме
В этом посте я попытался показать, как с помощью нескольких простых шагов мы могли применить правило DRY к нашей html-разметке в приложении AngularJS. И по мере роста вашего приложения вы, вероятно, найдете все больше мест, где дублируется html, что делает его хорошим кандидатом для преобразования его в директиву компонента.