Подтвердить что ты не робот

Как создать сложное приложение веб-интерфейса с несколькими представлениями?

Я собираюсь создать сложное приложение, имеющее много разных представлений. Представьте себе, например, решение eshop. Может быть много разных видов:

  • Страница контактов с некоторой статической информацией
  • Регистрационная форма для новых клиентов
  • Вид вашего заказа
  • Список продуктов
  • Подробная информация о продукте
  • ...

Теперь я немного смущен, как создать такое сложное приложение с помощью веб-интерфейса. Я хотел бы иметь HTML-шаблоны для разделов, разделенных в нескольких файлах, и иметь определенную логику для определения, которую нужно отобразить.

Предположим, что я хочу иметь один главный шаблон, содержащий основные вещи, такие как верхний и нижний колонтитулы, тогда у меня много шаблонов контента, они должны быть введены в нужное место внутри основного шаблона.

До сих пор я всегда видел только небольшие примеры с одним шаблоном использования Dart Web UI, поэтому я понятия не имею, как это сделать.

4b9b3361

Ответ 1

Я собрал небольшой пример того, как я это делаю (надеюсь, что в скором времени мы увидим приложение примера с более крупными примерами):

Полный исходный код этого примера см. в gist: Как создать приложение веб-интерфейса с несколькими представлениями в Dart

Основное приложение

  • app.html - Содержит основной макет приложения, создает компонент заголовка и нижнего колонтитула и создает контейнер для представлений.
  • app.dart - обрабатывает события навигации и заменяет вид внутри контейнера представления (см. ниже)
  • app.css

Веб-компоненты

Верхний и нижний колонтитулы

  • header.html - веб-компонент для заголовка
  • footer.html - Веб-компонент для нижнего колонтитула

Просмотров

  • contact.html - веб-компонент для представления контактов
  • contact.dart - Файл Dart, содержащий класс ContactView
  • products.html - веб-компонент для представления продуктов
  • products.dart - Файл Dart, содержащий класс ProductView

Переключение между представлениями

Стандартный способ создавать веб-компоненты заключается в использовании <x-foo></x-foo> в HTML. Поскольку у нас разные взгляды, нам нужно создать веб-компоненты внутри нашего Dart. Для этого мы должны вручную вызвать методы жизненного цикла веб-компонентов. Это не прямолинейно и может быть улучшено в будущем (см. Issue 93, в котором также содержатся некоторые примеры).

Вот как вы можете переключать представления (источник app.dart):

import 'dart:html';
import 'package:web_ui/web_ui.dart';

import 'contact.dart';
import 'products.dart';

void main() {
  // Add view navigation event handlers
  query('#show-contact-button').onClick.listen(showContactView);
  query('#show-products-button').onClick.listen(showProductView);
}

// Used to call lifecycle methods on the current view
ComponentItem lifecycleCaller;

/// Switches to contacts view
void showContactView(Event e) {
  removeCurrentView();

  ContactView contactView = new ContactView()
      ..host = new Element.html('<contact-view></contact-view>');

  lifecycleCaller = new ComponentItem(contactView)..create();
  query('#view-container').children.add(contactView.host);
  lifecycleCaller.insert();
}

/// Switches to products view
void showProductView(Event e) {
  removeCurrentView();

  ProductsView productsView = new ProductsView()
      ..host = new Element.html('<products-view></products-view>');

  lifecycleCaller = new ComponentItem(productsView);
  lifecycleCaller.create();
  query('#view-container').children.add(productsView.host);
  lifecycleCaller.insert();
}

void removeCurrentView() {
  query('#view-container').children.clear();

  if (lifecycleCaller != null) {
    // Call the lifecycle method in case the component needs to do some clean up
    lifecycleCaller.remove();
  }
}

И вот источник app.html:

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>A Complex Web UI Application</title>
    <link rel="stylesheet" href="app.css">

    <!-- import the header and footer components -->
    <link rel="components" href="header.html">
    <link rel="components" href="footer.html">

    <!-- import the view components -->
    <link rel="components" href="contact.html">
    <link rel="components" href="products.html">
  </head>
  <body>
    <header-component></header-component>

    <div id="view-container"></div>

    <button id="show-contact-button">show contact view</button>
    <button id="show-products-button">show products view</button>

    <footer-component></footer-component>

    <script type="application/dart" src="app.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

Примечание. Мне пришлось импортировать компоненты вида с помощью <link rel="components" href="contact.html">, хотя я не ссылаюсь непосредственно на него в файле HTML.

Ответ 2

Вы можете использовать библиотеку route в сочетании с шаблонами, чтобы значительно автоматизировать процесс.

В urls.dart вы определяете маршруты, которые будет обрабатывать приложение. app.dart настроит прослушиватель маршрута. Наконец, app.html будет содержать контейнер страницы, который автоматически переключит компонент страницы (с помощью создания экземпляра шаблона).

При создании этой структуры перетаскивание страницы можно обрабатывать с помощью обычных тегов привязки вместо вызова пользовательских функций для изменения страницы.

Чтобы добавить новую страницу, вам нужно будет сделать следующее:

  • Добавить новый маршрут в urls.dart
  • Создайте новый WebComponent в папке pages/
  • Добавить новый условный шаблон для страницы в app.html

Ниже вы можете увидеть пример приложения, которое обрабатывает домашнюю страницу и страницу контактов:

urls.dart:

library urls;

import 'package:route/url_pattern.dart';

final homeUrl = new UrlPattern(r'/');
final contactUrl = new UrlPattern(r'/contact');

app.dart:

import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'package:route/client.dart';
import 'urls.dart' as urls;
import 'package:web_ui/watcher.dart' as watchers;  

// Setup the routes to listen to    
void main() {
  var router = new Router()
  ..addHandler(urls.homeUrl, showPage)
  ..addHandler(urls.contactUrl, showPage)  
  ..listen();
}

// Change the page that we are on
void showPage(String path) {
  watchers.dispatch();
}

app.html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Sample app</title>
    <link rel="stylesheet" href="app.css">

    <!-- import the pages -->
    <link rel="components" href="pages/xhomepage.html">
    <link rel="components" href="pages/xcontactpage.html">
  </head>
  <body>

    <!-- You could put a header here if you want !-->

    <!-- Templates take care of automatically switching the page !-->
    <div class="pages">    
      <template instantiate="if urls.homeUrl.matches(window.location.pathname)">
        <x-home-page></x-home-page>
      </template>
      <template instantiate="if urls.contactUrl.matches(window.location.pathname)">
        <x-contact-page></x-contact-page>
      </template>
    </div>

    <!-- You could put a footer here if you want !-->

    <script type="application/dart" src="app.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

Изменить: Я удалил шаг, на котором app.dart должен определить свои собственные страницы. Вместо этого шаблоны проверяют, соответствует ли URL-адрес URL-адресу UrlPattern, определенному в urls.dart. Это должно упростить некоторые вещи.

Ответ 3

Я создал элемент Polymer <bind-view>, который создает и добавляет элемент вида в зависимости от текущего маршрута. Элемент работает с пакетом route_hierarchical.
Подробнее см. BWU Polymer Routing на GitHub.

Конфигурация маршрута выглядит как

library bwu_polymer_routing_example.route_initializer;

import 'package:route_hierarchical/client.dart' as rt;
import 'package:bwu_polymer_routing/module.dart';

class RouteInitializer implements Function {
  void call(rt.Router router, RouteViewFactory views) {
    views.configure({

      'usersList': routeCfg(
          path: '/users',
          view: 'user-list',
          defaultRoute: true,
          dontLeaveOnParamChanges: true,
          enter: (route) => router.go('usersList', {})),
      'user': routeCfg(
          path: '/user/:userId',
          view: 'user-element',
          dontLeaveOnParamChanges: true,
          mount: {
        'articleList': routeCfg(
            path: '/articles',
            view: 'article-list',
            defaultRoute: true,
            dontLeaveOnParamChanges: true,
            mount: {
          'article': routeCfg(
              path: '/article/:articleId',
              view: 'article-element',
              bindParameters: ['articleId', 'userId'],
              dontLeaveOnParamChanges: true,
              mount: {
            'view': routeCfg(
                path: '/view',
                defaultRoute: true,
                dontLeaveOnParamChanges: true),
            'edit': routeCfg(
                path: '/edit',
                dontLeaveOnParamChanges: true)
          })
        })
      })
    });
  }
}

<app-element> содержит элемент <bind-view>, местозаполнитель, в который добавляется представление, настроенное для текущего маршрута. Представления могут быть вложенными. Любое представление может содержать элемент <bind-view>. Это позволяет создавать иерархическую структуру представления без особого шаблона.

<!DOCTYPE html>

<link rel='import' href='../../../../packages/polymer/polymer.html'>

<link rel='import' href='../../../../packages/bwu_polymer_routing/bind_view.html'>
<link rel='import' href='user_list.html'>
<link rel='import' href='user_element.html'>
<link rel='import' href='article_list.html'>
<link rel='import' href='article_element.html'>

<polymer-element name='app-element'>
  <template>

    <bind-view id='app-element'></bind-view>

  </template>
  <script type='application/dart' src='app_element.dart'></script>
</polymer-element>

Файл app_element.dart содержит код инициализации маршрутизатора

class AppModule extends Module {
  AppModule() : super() {
    install(new RoutingModule(usePushState: true));
    bindByKey(ROUTE_INITIALIZER_FN_KEY, toValue: new RouteInitializer());
  }
}

@CustomTag('app-element')
class AppElement extends PolymerElement with DiContext {
  AppElement.created() : super.created();

  @override
  void attached() {

    super.attached();

    initDiContext(this, new ModuleInjector([new AppModule()]));
  }
}

Пакет также содержит некоторые вспомогательные миксины для добавления функции вложения зависимостей (DI) в элементы Polymer, такие как микширование DiContext, используемое здесь. Инъекция конструктора не может использоваться с Полимером, но события являются хорошей заменой.

DiConsumer mixin позволяет запросить экземпляр из DI с помощью этого простого кода

@CustomTag('article-list')
class ArticleList extends PolymerElement with DiConsumer {

  @observable String userId;

  @override
  void attached() {
    super.attached();

    // The two lines below show how to request instances from DI
    // but they are redundant here because 
    // route parameters are assigned to attributes of the view automatically
    // when the view is created or when the values change
    var di = inject(this, [RouteProvider /* add more types here as needed */]);
    userId = (di[RouteProvider] as RouteProvider).parameters['userId'];
  }
}