disclaimer: статья является ответом на критику ( которая обрушилась на хабре ), раскрывая потенциал IML на примере популярного приложения ToDo MVC.
Получил тумаков
Критика – это слово крайне мягкое по отношению к дискуссии, которая возникла к моей предыдущей статье, потому что это было больше похоже на избиение в котором были крайне не приятные комментарии ( на фото топ бредовых ), но также и объективные:
- Код на AngularJs не фонтан — сложно опровергнуть, хотя все они были с официального сайта и популярного руководства
- Слабые примеры – упор был на задачи, а не на сценарии, но соглашусь что более комплексное решение более полно раскрывает потенциал ( я предложил некоторые наши проекты, которые открыты на open source, но они остались без внимания )
- Не знаешь AngularJs ? – по понятным причинам это очень сильно задело разработчиков AngularJs
- Топик JS – это серьезная ошибка, потому что не используя asp.net mvc, сложно понять прелести типизированных TextBoxFor и других расширений.
Почему ToDo ?
В комментариях предложили попробовать реализовать “Todo MVC” в качестве доказательства возможностей IML и сейчас мы посмотрим что из этого получилось. Во первых demo версия, которая имеет одно отличите от тех, что представлены для js framework, в том, что в качестве хранилища используется не local storage, а база данных, а также исходный код, который мы будем разбирать далее в посте. В процессе реализации я строил всю логику ( расчеты подвала, скрытие элементов и т.д. ) на клиенте, хотя на реальных задачах, проще ( иногда необходимо ) обновлять “точечно” элементы, которые имея IML код, знают, как себя вычислить и отобразить.
Code review
Стиль повествования в этот раз будет не сравнение одного решения с другим (иначе объем материала будет большим ), а обзор кода, который получится при реализации приложения todo. Я упоминал выше о том, что в реализации IML присутствует и серверная часть, но в целях уравнивания решаемых задач для более объективного сравнения, сфокусируемся только на клиентской части.
Из чего состоит
Код был разделен на 3 View
- Index — основная страница ( и по факту единственная для браузера )
- Todo_List_Tmpl – шаблон для построения центрального списка
- Todo_Footer_Tmpl – шаблон для построения подвала с показателями
Index ( состоит из трех элементов )
Форма для добавления TODO
@using (Html.When(JqueryBind.Submit)
.DoWithPreventDefault()
.Submit(options =>
{
options.Url = Url.Dispatcher()
.Push(new AddTodoCommand { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId) });
})
.OnSuccess(dsl =>
{
dsl.WithId(containerId).Core().Trigger.Incoding();
dsl.Self().Core().Form.Reset();
})
.AsHtmlAttributes()
.ToBeginTag(Html, HtmlTag.Form))
{
@Html.TextBoxFor(r => r.Title, new { placeholder = "What needs to be done?", autofocus = "" })
}
примечание: сразу ожидаю фразы в стиле “Да, ну это не серьезно, кода в разы больше, надо везде копировать, посмотри как люди делают !!!” — на что у меня есть аргумент в лице C# extensions, который позволяет оборачивать конструкции IML. Далее в статье будет приведены альтернативные варианты решения задач ( также repository на GibHub с переработанным кодом ) с применением C# extensions
Что к чему ?
- When(JqueryBind.Submit) — указываем целевое событие
- DoWithPreventDefault — поведение события ( отменяем обработчик браузера )
- Submit — отправляем форму через ajax
примечание: пару замечаний по представленной реализации:- Url куда отправляется форма задается в опциях ( а не через атрибут action у form )
- ClientId можно вынести форму, как Hidden, который по InitIncoding проставит значение из Cookies, что бы вызывать Submit без параметров
- OnSuccess — выполняем после удачного завершения submit
- Trigger Incoding to containerId – запускаем весь IML код для элемента Container ( описание ниже )
примечание: можно применять более одного When, что позволяет подвязаться на разные события ( с разным IML кодом ), поэтому trigger Incoding запускает все цепочки.
- Form reset – сбрасываем значение элементов формы
- Trigger Incoding to containerId – запускаем весь IML код для элемента Container ( описание ниже )
- AsHtmlAttributes — собираем IML код в удобный для asp.net mvc формат ( RouteValueDictionary )
- ToBeginTag — упаковываем полученные атрибуты в тэг form ( принцип работы как Html.BeginForm )
примечание: можно использоватьHtml.BeginForm(“action”,”controller”,Post,iml.AsHtmlAttributes())
Форма для добавления TODO ( альтернативный вариант )
@using (Html.Todo().BeginForm(setting =>
{
setting.TargetId = containerId;
setting.Routes = new { ClientId = Selector.Incoding.Cookie(CookieManager.ClientId) };
}))
{
@Html.TextBoxFor(r => r.Title, new { placeholder = "What needs to be done?", autofocus = "" })
}
примечание: кода стало меньше и что самое главное, теперь можно расширять ( валидация, redirect после submit и т.д. ) метод, под нужды конкретного проекта.
public class BeginFormSetting
{
public string TargetId { get; set; }
public object Routes { get; set; }
}
public BeginTag BeginForm(Action configure)
{
var setting = new BeginFormSetting();
configure(setting);
var url = new UrlHelper(HttpContext.Current.Request.RequestContext);
return this.helper.When(JqueryBind.Submit)
.DoWithPreventDefault()
.Submit(options =>
{
options.Url = url.Dispatcher()
.Push(setting.Routes);
})
.OnSuccess(dsl =>
{
dsl.WithId(setting.TargetId).Core().Trigger.Incoding();
dsl.Self().Core().Form.Reset();
})
.AsHtmlAttributes()
.ToBeginTag(this.helper, HtmlTag.Form);
}
примечание: код знаком большинству asp.net mvc разработчиков, но стоит отметить то, что вместо “обычных” параметров, мы передаем анонимный метод, который принимает класс настроек.
Container
@(Html.When(JqueryBind.InitIncoding | JqueryBind.IncChangeUrl)
.Do()
.AjaxGet(Url.Dispatcher()
.Query(new
{
ClientId = Selector.Incoding.Cookie(CookieManager.ClientId),
Type = Selector.Incoding.HashQueryString(r => r.Type)
})
.AsJson())
.OnSuccess(dsl =>
{
string urlTmpl = Url.Dispatcher()
.Model(new GetTodoByClientQuery.Tmpl { FooterId = footerId })
.AsView("~/Views/Home/Todo_List_Tmpl.cshtml");
dsl.Self().Core().Insert.WithTemplateByUrl(urlTmpl).Html();
dsl.WithId(footerId).Core().Trigger.Incoding();
})
.AsHtmlAttributes(new { id = containerId })
.ToDiv())
Что к чему ?
- When(JqueryBind.InitIncoding | IncChangeUrl) — указываем целевые события
- InitIncoding – срабатывает при первом появлении элемента на странице ( не важно ajax или обычно )
- IncChangeUrl – срабатывает при изменение hash
- Do — поведение события
- AjaxGet — указываем url, на который будет выполнен ajax запрос
- ClientId – получаем значение из cookies
- Type – получаем значение из Hash Query String
- OnSuccess — выполняем после удачного завершения AjaxGet
- Insert data to self by template – вставляем полученные данные из запроса ( json ) через template ( Todo_List_Tmpl ниже ) в текущий элемент.
примечание: template можно получить через любой доступный Selector, например раньше основным был Jquery.Id, но загрузка по ajax предпочтительней
- Trigger incoding to footerId – запускаем весь IML код для элемента footer ( описание ниже )
- Insert data to self by template – вставляем полученные данные из запроса ( json ) через template ( Todo_List_Tmpl ниже ) в текущий элемент.
- AsHtmlAttributes — собираем IML код и задаем значение containerId ( guid ) атрибуту Id
примечание: использование guid в качестве Id гарантирует уникальность элемента на странице, особенно актуально для single page application
- ToDiv — упаковываем полученные атрибуты в тэг div
примечание: ToDiv это C# extensions над RouteValueDictionary, поэтому без труда можно написать свой нужный вариант
Container ( альтернативный способ )
@Html.Todo().Container(setting =>
{
setting.Id = containerId;
setting.Url = Url.Dispatcher()
.Query(new
{
ClientId = Selector.Incoding.Cookie(CookieManager.ClientId),
Type = Selector.Incoding.HashQueryString(r => r.Type)
})
.AsJson();
setting.Tmpl = Url.Dispatcher()
.Model(new GetTodoByClientQuery.Tmpl { FooterId = footerId })
.AsView("~/Views/Home/Todo_List_Tmpl.cshtml");
setting.DependencyId = footerId;
})
примечание: если в будущем надо будет добавить block ui или другие действия, то теперь это можно делать централизованно
public class ContainerSetting
{
public string Id { get; set; }
public string Url { get; set; }
public string Tmpl { get; set; }
public string DependencyId { get; set; }
}
public MvcHtmlString Container(Action configure)
{
var setting = new ContainerSetting();
configure(setting);
return helper.When(JqueryBind.InitIncoding | JqueryBind.IncChangeUrl)
.Do()
.AjaxGet(setting.Url)
.OnSuccess(dsl =>
{
dsl.Self().Core().Insert.WithTemplateByUrl(setting.Tmpl).Html();
dsl.WithId(setting.DependencyId).Core().Trigger.Incoding();
})
.AsHtmlAttributes(new { id = setting.Id })
.ToDiv();
}
Footer
@(Html.When(JqueryBind.None)
.Do()
.Direct(new FooterVm
{
AllCount = Selector.Jquery.Class("toggle").Length(),
IsCompleted = Selector.Jquery.Class("toggle").Is(JqueryExpression.Checked),
CompletedCount = Selector.Jquery.Class("toggle")
.Expression(JqueryExpression.Checked)
.Length(),
}))
.OnSuccess(dsl =>
{
string urlTmpl = Url.Dispatcher()
.Model(new TodoFooterTmplVm
{
ContainerId = containerId
})
.AsView("~/Views/Home/Todo_Footer_Tmpl.cshtml");
dsl.Self().Core().Insert.Prepare().WithTemplateByUrl(urlTmpl).Html();
})
.AsHtmlAttributes(new { id = footerId })
.ToDiv())
- When(JqueryBind.None) — указываем целевые события
- None – When позволяет указать любое пользовательское событие, как строку “MySpecialEvent”, но практика показал, что для многих сценариев хватает одного.
- Do — поведение события
- Direct — можно рассматривать как action заглушка, который не выполняет действий, но может работать с данными
- AllCount – получаем кол-во объектов с классом “toggle”
примечание: можно воспользоваться расширением Method ( вместо Length ), чтобы вызвать любой jquery метод, а также написать C# extensions над JquerySelectorExtend
- IsCompleted — проверяем на наличие отмеченных объектов с классом “toggle”
примечание: кому не хватит возможностей готовых jquery selector, то можно воспользоватся Selector.Jquery.Custom(“your jquery selector”)
- CompletedCount – получаем количество отмеченных объектов с классом “toggle”
- AllCount – получаем кол-во объектов с классом “toggle”
- OnSuccess — выполняем после удачного завершения AjaxGet
- Insert prepare data to self by template — вставляем подготовленные ( prepare ) данные из Direct через template ( Todo_Footer_Tmpl ниже ) в текущий элемент
примечание: prepare перед тем как вставить данные выполняет селекторы, которые находятся в полях.
- AsHtmlAttributes — собираем IML код
- ToDiv — упаковываем полученные атрибуты в тэг div
Todo List Tmpl
Разметка шаблона для построения списка todo
@using (var template = Html.Incoding().Template())
{
<ul>
@using (var each = template.ForEach())
{
@using (each.Is(r => r.Active))
{ @createCheckBox(true) }
@using (each.Not(r => r.Active))
{ @createCheckBox(false) }
<li class="@each.IsInline(r=>r.Active,"completed")">
<label>@each.For(r=>r.Title)</label>
</li>
</ul>
}
примечание: исходный код больше ( удаленна логика элементов ), чем представлен на пример, но это сделано для удобства объяснения template
Что к чему ?
- Html.Incoding().Template() – открываем контекст ( в рамках using ) построения template
- template.ForEach() — начинаем перебор ( в рамках using ) элементов
- using(each.Is(r=>r.Active)) — предыдущий вариант условий был в “одну линию”, но часто бывает что надо выполнить более сложные действия.
- createCheckBox — анонимная C# функция для создания checkbox ( описание ниже )
- each.IsInline(r=>r.Active,”completed”) — если поле Active true, тогда возвращаем “completed”
примечание: также имеются IsNotLine и IsLine.
- each.For(r => r.Title) – выводим значение поля Title
примечание: все обращения к полям происходят на основе указанной модели ( да, я опять о типизации )
Другие элементы
Button del
@(Html.When(JqueryBind.Click)
.Do()
.AjaxPost(Url.Dispatcher().Push(new DeleteEntityByIdCommand
{
Id = each.For(r => r.Id),
AssemblyQualifiedName = typeof(Todo).AssemblyQualifiedName
}))
.OnBegin(r =>
{
r.WithSelf(s => s.Closest(HtmlTag.Li)).Core().JQuery.Manipulation.Remove();
r.WithId(Model.FooterId).Core().Trigger.Incoding();
r.WithId(toggleAllId).Core().Trigger.None();
})
.AsHtmlAttributes(new { @class = "destroy" })
.ToButton(""))
Что к чему ?
Button Del ( альтернативный вариант )
@Html.Todo().Verb(setting =>
{
setting.Url = Url.Dispatcher().Push(new DeleteEntityByIdCommand
{
Id = each.For(r => r.Id),
AssemblyQualifiedName = typeof(Todo).AssemblyQualifiedName
});
setting.OnBegin = dsl =>
{
dsl.WithSelf(s => s.Closest(HtmlTag.Li)).Core().JQuery.Manipulation.Remove();
dsl.WithId(Model.FooterId).Core().Trigger.Incoding();
dsl.WithId(toggleAllId).Core().Trigger.None();
};
setting.Attr = new { @class = "destroy" };
})
примечание: OnBegin принимает Action, что позволяет легко масштабировать ваш extensions внедряя в него IML. ( далее будут ещё примеры )
public class VerbSetting
{
public string Url { get; set; }
public Action<IIncodingMetaLanguageCallbackBodyDsl> OnBegin { get; set; }
public Action<IIncodingMetaLanguageCallbackBodyDsl> OnSuccess { get; set; }
public object Attr { get; set; }
public string Content { get; set; }
}
public MvcHtmlString Verb(Action<VerbSetting> configure)
{
var setting = new VerbSetting();
configure(setting);
return this.helper.When(JqueryBind.Click)
.Do()
.AjaxPost(setting.Url)
.OnBegin(dsl =>
{
if (setting.OnBegin != null)
setting.OnBegin(dsl);
})
.OnSuccess(dsl =>
{
if (setting.OnSuccess != null)
setting.OnSuccess(dsl);
})
.AsHtmlAttributes(setting.Attr)
.ToButton(setting.Content);
}
примечание: по скольку Verb использует в нескольких сценариях, то можно легко делать опциональные параметры проверяю их на null, а так же задавать значения по умолчанию
Checkbox Completed
var createCheckBox = isValue => Html.When(JqueryBind.Change)
.Do()
.AjaxPost(Url.Dispatcher().Push(new ToggleTodoCommand
{
Id = each.For(r => r.Id)
}))
.OnBegin(dsl =>
{
dsl.WithSelf(r => r.Closest(HtmlTag.Li))
.Behaviors(inDsl =>
{
inDsl.Core().JQuery.Attributes.RemoveClass("completed");
inDsl.Core().JQuery.Attributes.AddClass("completed")
.If(builder => builder.Is(() => Selector.Jquery.Self()));
});
dsl.WithId(Model.FooterId).Core().Trigger.Incoding();
dsl.WithId(toggleAllId).Core().Trigger.None();
})
.AsHtmlAttributes(new {@class="toggle" })
.ToCheckBox(isValue);
примечание: в рамках razor страницы можно использовать анонимные C# функции или Razor helper, что позволяет агрегировать однотипные задачи.
Что к чему ?
- When(JqueryBind.Change) — указываем целевое событие
- Do — поведение события
- AjaxPost — указываем Url, на который делаем ajax запрос
примечание: AjaxPost и AjaxGet это “именованная” версия Ajax, который имеет много дополнительных настроек
- OnBegin — выполняем до начала действия ( AjaxPost )
- Remove class on closest LI – удаляем класс “completed” у ближайшего LI
- Add class on closest LI if self is true — добавляем класс “completed”
примечание: пока в IML не реализована возможность else, но в версии 2.0 планируется
- AsHtmlAttributes — собираем IML код, а также устанавливаем значение “toggle” атрибуту class
- ToCheckBox — упаковываем полученные атрибуты в тэг input[type=checkbox]
Filter by type todo
@{
const string classSelected = "selected";
var createLi = (typeOfTodo,isFirst) => Html.When(JqueryBind.InitIncoding)
.Do()
.Direct()
.OnSuccess(dsl =>
{
var type = Selector.Incoding.HashQueryString(r => r.Type);
if (isFirst)
dsl.Self().Core().JQuery.Attributes.AddClass(classSelected).If(s => s.Is(() => type == ""));
dsl.Self().Core().JQuery.Attributes.AddClass(classSelected).If(s => s.Is(() => type == typeOfTodo.ToString()));
})
.When(JqueryBind.Click)
.Do()
.Direct()
.OnSuccess(dsl =>
{
dsl.WithSelf(r => r.Closest(HtmlTag.Ul).Find(HtmlTag.A)).Core().JQuery.Attributes.RemoveClass(classSelected);
dsl.Self().Core().JQuery.Attributes.AddClass(classSelected);
})
.AsHtmlAttributes(new { href = "#!".AppendToHashQueryString(new { Type = typeOfTodo }) })
.ToLink(typeOfTodo.ToString());
}
<li> @createLi(GetTodoByClientQuery.TypeOfTodo.All,true) </li>
<li> @createLi(GetTodoByClientQuery.TypeOfTodo.Active,false) </li>
<li> @createLi(GetTodoByClientQuery.TypeOfTodo.Completed,false) </li>
примечание: ещё один пример реализации анонимных функций в рамках razor view
Что к чему ?
- When(JqueryBind.InitIncoding) — указываем целевое событие
- Do — поведение события
- Direct – ничего не выполняем
- OnSuccess — выполняем после удачного завершения
примечание: для Direct нет отличия между OnBegin или OnSuccess, но OnError и OnBreak работают, так же как и для остальных- var type – объявляем переменную, которую потом будем использовать в выражениях
- add class to self if IsFirst is true And type is Empty – добавляем класс, если текущий элемент является первым и в type пустой
- add class to self if type is current type – добавляем класс к текущему элементу если type равен аргументу typeOfTodo
- When(JqueryBind.Click) — указываем целевое событие
- Do — поведение события
примечание: мы не отменяем поведение ссылки, потому что нам нужно, чтобы браузер обновил location
- Direct — ничего не выполняем
- remove class – удаляем класс selected у всех A, которые находятся в ближайшем UL
- add class to self — добавляем класс selected текущему элементу
- AsHtmlAttributes — собираем IML код, а также устанавливаем атрибут href
Filter by type todo ( альтернативный способ )
<li>
@Html.Todo().LiHash(setting =>
{
setting.IsFirst = true;
setting.SelectedClass = classSelected;
setting.Type = GetTodoByClientQuery.TypeOfTodo.All;
})
</li>
Безусловные плюсы !
В чем же плюсы IML, я постарался раскрыть в прошлой статье, но было не убедительно, поэтому попробую ещё раз:
- Типизация – конечно каждый смотрит через свою призму на типизацию, кто-то считает что приходится писать больше кода ( это верно ), другим не хватает гибкости, которая присуща не типизированным языкам, но IML это прежде всего C#, поэтому те разработчики, которые его выбрали, я думаю по достоинству оценят этот плюс.
- Мощные extensions – в статье я привел несколько, но на практике их на много больше, чтобы подкрепить свои слова приведу ещё пару:
- Drop down
@Html.For(r=>r.HcsId).DropDown(control =>
{
control.Url = Url.Action("HealthCareSystems", "Shared");
control.OnInit = dsl => dsl.Self().Core().Rap().DropDown();
control.Attr(new { @class = "selectInput", style = "width:375px" });
})
примечание: OnInit принимает Action, что позволяет легко масштабировать ваш extensions внедряя в него IML.
Dialog@Html.ProjectName().OpenDialog(setting =>
{
setting.Url = Url.Dispatcher()
.Model<GroupEditProviderOrderCommand>()
.AsView("~/Views/ProviderOrder/Edit.cshtml");
setting.Content = "Edit";
setting.Options = options => { options.Title = "Edit Order"; };
})
примечание: для большей гибкости можно использовать Action в качестве поля, например setting.Options это Action.
Список можно продолжать бесконечно, но основная идея в том, что IML позволяет выполнять любые задачи, а html extensions решает проблему с повторным использованием.
- Ещё мощнее extensions
- Grid — полностью построенный на IML ( в ближайшие время будет документация )
@(Html.ProjectName()
.Grid<CTRPrintLogModel>()
.Columns(dsl =>
{
dsl.Template(@<text>
<span>@item.For(r=>r.Comment)</span>
</text>)
.Title("Comment");
const string classVerticalTop = "vertical_top";
dsl.Bound(r => r.Time).Title("Time").HtmlAttributes(new { @class = classVerticalTop });
dsl.Bound(r => r.Version).Title("Type").HtmlAttributes(new { @class = classVerticalTop });
dsl.Bound(r => r.PrintDate).Title("Date");
dsl.Bound(r => r.Comment).Raw();
})
.AjaxGet(Url.RootAction("GetCTRPrintLogModel", "CTR")))
- Tabs
@(Html.Rap()
.Tabs<Enums.CarePlanTabs>()
.Items(dsl =>
{
dsl.AddTab(Url.Action("RedFlags", "PatientRedFlag"), Enums.CarePlanTabs.RedFlags);
dsl.AddTab(Url.Action("Goals", "IncGoal"), Enums.CarePlanTabs.SelfCareGoals);
dsl.AddTab(Url.Action("Index", "IncAppointment"), Enums.CarePlanTabs.Appointments);
}))
примечание: любой разработчик знакомый с html extensions может построить такой элемент под нужды своего проекта
- Grid — полностью построенный на IML ( в ближайшие время будет документация )
- Работа с hash – в этой статье была рассмотрена только на уровне IncChangeUrl, но у нас есть:
- Hash.Fetch – проставляет значения из hash в элементы ( sandbox )
- Hash.Insert/Update — проставляет значения в hash из элементов
- Hash.Manipulate – позволяет тонко ( set/ remove by key ) настроить текущий hash
- AjaxHash — это аналог Submit, но не для form, а для Hash.
- Работа с Insert – для реализации TODO мне не пришлось применять, но в реальных проектах повсеместно
- Insert Generic– все примеры выше были построенные на одной модели, но часто бывают сценарии, когда полученные данные являются “контейнером”, для этих целей в Insert есть возможность указывать с какой частью модели мы работаем через For, а также template для каждой свой.
Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("FetchComplex", "Data"))
.OnSuccess(dsl =>
{
dsl.WithId(newsDivId).Core().Insert.For<ComplexVm>(r => r.News).WithTemplateByUrl(urlNewsTmpl).Html();
dsl.WithId(contactDivId).Core().Insert.For<ComplexVm>(r => r.Contacts).WithTemplateByUrl(urlContactsTmpl).Html();
})
.AsHtmlAttributes()
.ToDiv()
- Insert Generic– все примеры выше были построенные на одной модели, но часто бывают сценарии, когда полученные данные являются “контейнером”, для этих целей в Insert есть возможность указывать с какой частью модели мы работаем через For, а также template для каждой свой.
- Работа с validation ( сервер, как клиент ) — в многих js framework есть инструменты для валидации, но IML, как упоминалось не однократно имеет интеграцию с сервером и поддержка любых validation engine ( FluentValidation, стандартный MVC) без написания дополнительного кода.
- Код command
if (device != null)
throw IncWebException.For<AddDeviceCommand>(r => r.Pin, "Device with same pin is already exist");
- Код view
.OnError(dsl => dsl.Self().Core().Form.Validation.Refresh())
примечание: обработчик OnError должен быть прикреплен к элементу, который вызывает action ( submit, ajaxpost or etc )
- Код command
- Меньше скриптов – с увлечением проекта js framework требует написания множества js файлов, но IML имеет фиксированный ( плагины не в счет ) набор библиотек
- Типизированные template – я о типизации в целом, но для построения шаблонов это особенно важно
- Замена template engine — выбирайте любой, а синтаксис тот же
- Готовая инфраструктура – IML это часть Incoding Framework и в отличии от js framework у нас полная ( сервер / клиент / unit testing ) инфраструктура для разработки проектов, которая тесно интегрирована между собой.
Заключение
При реализации todo на IML я придерживался правила: меньше обновлений странице, то есть пересчитывал все на клиенте, но практика ( наших проектов ) показывает, что чаще узким местом бывает именно сервер, потому что многие действия не возможны или не предпочтительны на клиенте, напримерНевозможны ( по причине производительности ):
- Paginated – если в базе сотни тысяч записей, то такой объем не правильно передавать на клиента
- Order – та же причина
- Where — та же причина
Не предпочтительны могут быть расчеты, такие как сложные вычисления ( общую сумму заказов с учетом налога ) на основе значений полей, удобней будет отправить на сервер запрос ( с данными полей ) и результат вставить.
Опять IML ))В рамках IML вычисления можно решить следующими способами:- Одиночное значение
var val = Selector.Incoding.AjaxGet(url);
dsl.WithId(yourId).Core().JQuery.Attributes.Val(val);
- Набор данных
dsl.With(r => r.Name(s => s.Last)).Core().Insert.For<ContactVm>(r => r.Last).Val();
dsl.With(r => r.Name(s => s.First)).Core().Insert.For<ContactVm>(r => r.First).Val();
dsl.With(r => r.Name(s => s.City)).Core().Insert.For<ContactVm>(r => r.City).Val();
Можно долго рассказывать про возможности IML ( и Incoding Framework ), но статья и так получилось большой, поэтому, те кто захочет продолжить изучать наш инструмент смогут найти материалы в сети. Я понимаю, доказать то, что IML способен решать задачи не хуже популярных js framework крайне сложно, но в следующих статьях будет обзор реализаций autocomplete, Tree View, grid и других сложных задач, которые продемонстрируют ещё больше возможностей.
P.S. Как всегда рад критики и замечаниям )))
- Drop down
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий