Сообщений 11    Оценка 60        Оценить  
Система Orphus

NemerleWeb

Часть 1

Авторы: Маслаков Михаил Сергеевич
Шарон Константин Анатольевич
Опубликовано: 13.03.2015
Исправлено: 10.12.2016
Версия текста: 1.0
Уникальный веб-фреймворк
Как это работает?
Покажите мне код
У меня уже есть готовый сайт на C#
Почему Nemerle и NemerleWeb?
Список задач
Заключение
Ссылки

Уникальный веб-фреймворк

NemerleWeb– это фреймворк для создания одностраничных Web-приложений (Single Page Application – SPA) на основе паттерна проектирования MVVM. NemerleWeb который транслирует код, написанный на Nemerle, в смесь JavaScript и HTML, обеспечивает: двустороннюю привязку данных, прозрачное дуплексное общение с сервером, статическую типизацию с настоящими подсказками и ещё много чего другого.

Как это работает?

Разработчик описывает модель мредставления (ViewModel) на компилируемом языке Nemerle. В ней он описывает

Благодаря макросам, код на этом языке получается очень лаконичным, фактически, мы только описываем логику приложения.

Это выгодно отличает наше решение от библиотек на чистом JavaScript.

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

  <input value="$Name" />

Достаточно внутри атрибута поставить первым символом $, и атрибут будет вычисляться автоматически, на основании последующего кода.

В этом коде, кстати, разрешены практически любые выражения – главное, чтобы эти выражения уместились в результирующем HTML.

Дальше из созданной модели генерируются *.js-файлы и шаблоны, и всё это дело вставляется в вашу статичную до того страницу.

Описание модели – это один файл внутри обыкновенного ASP.NET MVC проекта.

Покажите мне код

Код простейшей модели выглядит примерно так:

[Unit]
publicclass Page
{
  Name : string = "world";

  [Html]
  public View() : string
  {
    <#
      <input value=”$Name” />       
      <div>Hello, $Name</div>
    #>
  }
}

Смотреть:Результат, Исходный код

Из этого будет сгенерирован JavaScript “класс” Page и HTML шаблон Page_View, который включает в себя примитивы для автоматической двусторонней привязки данных.

Атрибутом [Unit] помечаются классы, из которых нужно создавать client side код, а [Html] те методы, которые мы хотим превратить в шаблоны.

У меня уже есть готовый сайт на C#

Если у вас есть готовый сайт, к которому не хочется приплетать Nemerle, вы можете создать отдельный проект на Nemerle, модель из которого вставить в основной сайт на C# таким образом:

      public ActionResult PageWithModel() 
{
  return View("Index", (object)NemerleProject.MyModel.Render());
}

Статический метод Render будет автоматически сгенерирован компилятором для любой модели, которую вы создадите. В этот метод, кстати, можно передавать параметры, с которыми потом будет вызван конструктор на стороне клиента.

Почему Nemerle и NemerleWeb?

Дело в том, что Nemerle обладает очень развитой инфрастуктурой для создания макросов. Разработчику позволено вмешиваться во многие стадии компиляции и по необходимости вносить свои изменения.

Именно метапрограммирование позволило создать фреймворк с очень лаконичной структурой и с практически неограниченными возможностями. А так как Nemerle к тому же статически типизирован, мы бесплатно получаем Intellisense и базовую проверку на опечатки.

Список задач

Для демонстрации возможностей я выбрал типичный список задач (ToDo list), но с расширенными возможностями. Наш список будет автоматически сохранять изменения на сервер, при этом оповещая других клиентов об обновлении посредством SignalR.

Задачи будут отсортированы по приоритету с помощью LINQ. Им, кстати, можно пользоваться прямо из шаблона.

Для общения сервера с клиентом мы используем SignalR, который уже является стандартом для подобного рода задач в ASP.NET.

      // Вся логика в классе с атрибутом [Unit] будет транслирована в JavaScript, 
      // кроме внутреннего класса Server, из которого будет сгенерирован
      // ASP.NET MVC Controller
[Unit]
publicclass ReactiveToDo
{
  // Макрос [Dto] помечает все поля как public mutable.// Также добавляет макроатрибут [Record], который создаёт конструктор,// принимающий значения всех полей
  [Dto] publicclass Task { Name : string; IsDone : bool; Priority : string; }

  // Все переменные в типе с атрибутом [Unit] находятся на клиенте за исключением переменных класса Server, которые будут на сервере.// В Nemerle по умолчанию все поля неизменяемые в отличии от C#.// Нам нужны изменяемые поля, поэтому мы помечаем их модификатором mutable.mutable _tasks = List.[Task]();
  mutable _todoName = "New task";
  mutable _todoPriority = "high";
  
  publicthis() 
  {
    // Поле server генерируется автоматически, если в классе Unit// присутствует класс Server (см. ниже)// К параметрам метода Load() добавляется callback,// который принимает результат.// Так как параметров у Load нет, то callback - это единственный параметр.// Методы сервера возвращают объект типа XMLHttpRequest// Благодаря этому возможно узнать статус запроса и отменить вызов._ = server.Load(tasks => SetTasks(tasks));
  }
    
  // Мы будем вызывать этот код с сервера с помощью SignalR,// поэтому выделяем такую простую логику в отдельный метод
  SetTasks(tasks : List[Task]) : void 
  {      
    _tasks = tasks;      
  }
    
  Add() : void
  {
    _tasks.Add(Task(_todoName, false, _todoPriority));
      
    SaveToServer();
      
    _todoName = "Task #" + _tasks.Count;
    _todoPriority = "high";
  }
  
  SaveToServer() : void
  {
    // Метод Save принимает параметр List[Task], к которому автоматически генерируется // функция обратного вызова с результатом.// На данный момент нам с этим результатом делать нечего, // поэтому просто выведем его в консоль, для наглядности// Здесь window.console.log это обычный вызов JavaScript функции._ = server.Save(_tasks, status => window.console.log(status))      
  }
    
  // О примитивах привязки данных мы расскажем впоследствие более подробно.// Можете обратить внимание, что внутри шаблонов работает LINQ, // который реализован с помощью linq.js// <# #> - аналог C# строк вида @””, но позволяет писать обычные кавычки.// Также эти строки поддерживают рекурсию: <# <# a #> #> .
  [Html]    
  public View() : string
  {
    <#
      <table class="reactive-todo-table">
        <tr>
          <th>Priority</th><th>Task</th><th>Status</th>
        </tr>
        <tr $foreach(task in _tasks.OrderBy(t => t.Priority))>
          <td>$(task.Priority)</td>
          <td>$(task.Name)</td>
          <td><input type="checkbox"event-change="$SaveToServer"checked="$(task.IsDone)" /></td>
        </tr>
      </table>
        
      <div>
        <input value="$_todoName" />
        <select value="$_todoPriority">
          <option>high</option>
          <option>low</option>
        </select>
        <button click="$Add">Add</button>
      </div>
    #>
  }
      
  // Из этого класса будет сгенерирован контроллер ASP.NET MVC// Весь маппинг между клиентом и сервером делается автоматически,// поэтому параметры и возвращаемое значение это те же типы, которые// мы используем на стороне клиента.// Атрибутом [SignalR] помечаются те типы Server,// в которых мы будем пользоваться макросами broadcast или signal (см. ниже)
  [SignalR]
  publicclass Server
  { 
    // Эта переменная находится на сервере.// Статическая переменная дает нам в некотором роде БД в памяти.staticmutable _db : List[Task] = List();

    staticthis()
    {
      _db.Add(Task("Write article", false, "high"));
      _db.Add(Task("Fix website bugs", false, "high"));
      _db.Add(Task("Add new functionality", false, "low"));
    }    
      
    public Load() : List[Task]
    {
      // Nemerle позволяет не писать return для возвращения значения// Такой синтаксис позволяет реализовать идею "Все есть выражение"
      _db        
    }
     
    // Методы сервера всегда возвращают значение.// В будущем будет позволенно объявить метод возвращающий ‘void’.public Save(tasks : List[Task]) : string
    {
      _db = tasks;
        
      // Вот он весь SignalR. Как обычно, все неудобные подробности // для нас генерирует макрос. Мы же просто пользуемся готовым// полем client, у которого есть все методы присутствующие на // стороне клиента.// Опять же маппинг автоматический.// Макрос broadcast вызовет метод SetTasks у всех клиентов. // В противопоставление ему есть макрос signal, который вызовет// метод только у текущего пользователя и ни у кого более
      broadcast client.SetTasks(_db);
        
      //Возвращать нам нечего, поэтому просто отвечаем:"ok"
    }
  }
}

Смотреть:Результат, Исходный код

ПРИМЕЧАНИЕ

Примечание: Откройте страницу в двух вкладках одновременно, чтобы увидеть, как данные обновляются автоматически.

Заключение

На этом первую статью пора завершать.

В следующих частях расскажем про:

Ссылки

Сайт проекта: www.nemerleweb.com

Репозиторий: https://github.com/NemerleWeb/NemerleWeb


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 11    Оценка 60        Оценить