Angular всегда прав

Posted on: April 4th, 2012 by Spade No Comments »

Одно из наиболее интересных веяний в области разработки вэб-приложений на данный момент это изменение архитектуры MVC на стороне сервера в форму Services – Controllers на стороне сервера + MVC на стороне клиента. Суть в том, что сервер более никак не влияет на представление на стороне клиента. Все что от него требуется, это отдать данные в некотором виде (JSON, и уже реже, XML) – то есть сформировать модель для архитектуры Client side MVC, первый её уровень. Это дает ряд преимуществ. Самое смешное и часто повторяемое – что дизайнерам наконец-то будет удобнее писать разметку HTML, хотя мы то знаем что как бы далеко не зашли в своей простоте техноголии верстки, дизайнерам это мало поможет – хорошо если они когда-нибудь  научатся хотя  бы правильно оформлять файлы Photoshop.

Из более важных – действительно, компонент Представление, теперь может быть изменен абсолютно не затрагивая остальных компонентов. Они могут разрабатываться отдельно друг от друга, в разных командах, в разных проектах. Более того, часто компонент Представления, так как состоит из HTML, Javascript, Css раздается из отдельного CDN, как статика, по другому адресу или на другом сервере. Другое интересное преимущество – единый интерфейс выдачи данных для внешних клиентов – будь это скин собственного сайта или API запрос к вашему сервису. Довольно часто API интерфейс хороших сервисов не содержит полный функционал доступный через сайт, но при такой архитектуре эта проблема автоматически решается.

Отметим, что при таком подходе, конечно, возникает ряд проблем, которые нужно отдельно преодолевать, к примеру, обработка запросов к закрытым ресурсам, аутентификация, или выдача страниц для поисковых ботов (у которых нет Javascript движков). Тем не менее они все решаемы.

Один из фреймворков, реализующих этот подход – angularjs. Он разработан командой из гугла (собственными силами), и под простым лозунгом Superheroic Javascript framework. Основные принципы:

  • Декларативные html шаблоны
  • Двусторонняя привязка данных к контроллерам
  • Реализация DI

Для ознакомления можно просмотреть ролик от centresource.com – как они использовали фреймворк для своих целей.

Пример

Рассмотрим его работу на простом примере: с сервера отдается список пользователей по асинхронному запросу в формате JSON, и наш фреймворк выводит эти данные в виде таблицы. На серверной стороне у нас Spring. С помощью Content Negotiating и присутствия библиотеки Jackson в classpath наши объекты автоматически преобразуются в JSON при отдаче. Для простоты не будем вводить анализ по accept type, на случай если клиенту нужны XML или другие форматы, а всегда будем выдавать JSON.

@Controller
public class UserController {
 
    @Autowired
    private IUserService service;
 
    @RequestMapping(value = "/users")
    public @ResponseBody
    List getUsers(@RequestParam(value = "nick") String nicknamePart) {
        return service.getUsers(nicknamePart);
    }
}

Далее рассмотрим сам HTML UI шаблон:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:ng="http://angularjs.org">
<head>
    <title>Angular test</title>
    <script src="http://code.angularjs.org/angular-0.9.12.js" ng:autobind></script>
    <script src="/static/js/controllers.js"></script>
</head>
<body ng:controller="UserListCtrl">
<table border="1" ng:init="filterByNick()">
    <tr>
        <th>Nickname</th>
        <th>First Name</th>
        <th>Last Name</th>
        <th>E-mail</th>
        <th>Country</th>
    </tr>
    <tr>
        <td><input type="text" name="nick" ng:change="filterByNick(nick)"/></td>
        <td/><td/><td/><td/>
    </tr>
    <tr ng:repeat="obj in users">
        <td>{{obj.nick}}</td>
        <td>{{obj.firstName}}</td>
        <td>{{obj.lastName}}</td>
        <td>{{obj.email}}</td>
        <td>{{obj.country}}</td>
    </tr>
</table>
</body>
</html>

В корневом элементе вводим требуемое пространство имен: xmlns:ng=http://angularjs.org. Подключаем два скрипта – один содержит собственно фреймворк (если вам нужно просто побаловаться – разработчики предоставляют вам эту возможность, без необходимости скачивания скрипта). Второй – определение наших контроллеров. В элементе body мы вводим контекст нашего контроллера UserListCtrl – ng:controller=”UserListCtrl”. То есть все элементы и функции будут выполняться в его пространстве имен (если, конечно, мы их предварительно там объявим). Далее идет таблица с данными, полученными с сервера. С помощью <tr ng:repeat=”obj in users”> мы организуем цикл для вывода свойств объектов User. Фигурные скобки используются в angularjs-разметке для вывода. Файл контроллеров controllers.js

function UserListCtrl($xhr) {
 
    this.nick = '';
 
    var self = this;
    this.filterByNick = function() {
      $xhr('GET','/users?nick='+self.nick, function(code, response){
          self.users = response;
      });
    };
}

В самой таблице у нас есть поле фильтра, чтобы выводить пользователей только с удовлетворяющими нас никнеймами. Для этого у нас есть функция-метод котроллера filterByNick. В самом контроллере мы привязали поле фильтра к члену-переменной: this.nick = ”;

Чтобы изначально вывести нефильтрованные данные у нас есть команда ng:init=”filterByNick()” – команда для выполнения инструкций инициализации. Работа самого фильтра реализуется командой ng:change=”filterByNick(nick)”.

В итоге получили таблицу, содержимое которой меняется в реальном времени, без перезагрузки, как только мы меняем фильтр-строку.

Leave a Reply