In this blog post I will talk about the changes coming in Angular 2 that will improve its support for functional programming.

Angular 2 is still in active development, so all the examples are WIP. Please focus on the capabilities and ideas, and not the API. The API may change.

WHY FUNCTIONAL PROGRAMMING

Imagine an application being a nested structure of components. And it is written in the way that

  • A component depends only on its bindings and its direct children.
  • A component directly affects only its direct children.

For example, the highlighted Todo component should not be able to affect any other component except for the input elements.

If we write our application in this way, we get the property:

We can effectively reason about every single component without knowing where it is used. In other words, we can reason about a component just by looking at its class/function and its template.

We want to limit the number of things we need to look at to make a change. And we want to make changes predictable and controlled. This is the goal, and functional programming is just the means of achieving it.

DATA-ORIENTED PROGRAMMING

Using a mutable domain model goes against our goal. Say we have a mutable domain model that backs up my component. And the component updates the model. Some other component, somewhere else, that happened to point it, will be updated.

Instead we should model our domain using dumb data structures, such as record-like objects and arrays, and transform them using map, filter, and reduce.

Since Angular does not use KVO, and uses dirty-checking instead, it does not require us to wrap our data into model classes. So we could always use arrays and simple objects as our model.


class TodosComponent {
constructor(http:Http) {
this.todos = []; http.get("/todos").
then((resp) => {
this.todos = resp.map((todo) =>
replaceProperty(todo, "date", humanizeDate))
});
} checkAll() {
this.todos = this.todos.map((todo) =>
replaceProperty(todo, 'checked', true));
} numberOfChecked() {
return this.todos.filter((todo) => todo.checked).length;
}
}

Here, for example, todos is an array of simple objects. Note, we do not mutate any of the objects. And we can do it because the framework does not force us to build a mutable domain model.

We can already write this kind of code in Angular today. What we want is to make it smoother, and take advantage of it performance wise.

USING PERSISTENT DATA STRUCTURES

For example, we want to improve the support of persistent data structures.

First, we want to support even most exotic collections. And, of course, we do not want to hardcode this support into the framework. Instead, we are making the new change detection system pluggable. So we will be able to pass any collection to ng-repeat or other directives, and it will work. The directives will not have to have special logic to handle them.

<todo template=”ng-repeat: #t in: todos” [todo]=”t”></todo> // what is todos? array? immutable list? does not matter.

Second, we can also take advantage of the fact that these collections are immutable. For example, we can replace an expensive structural check to see if anything changed with a simple reference check, which is much faster. And it will be completely transparent to the developer.

CONTROLLING CHANGE DETECTION

Functional programming limits the number of ways things change, and it makes the changes more explicit. So because our application written in a functional way, we will have stronger guarantees, even though the JavaScript language itself does not provide those guarantees.

For example, we may know that a component will not change until we receive some event. Now, we will be able to use this knowledge and disable the dirty-checking of that component and its children altogether.

A few examples where this feature can come handy: * If a component depends on only observable objects, we can disable the dirty-checking of the component until one of the observables fires an event. * If we use the FLUX pattern, we can disable the dirty-checking of the component until the store fires an event.

class TodosComponent {
constructor(bingings:BindingPropagationConfig, store:TodoStore) {
this.todos = ImmutableList.empty();
store.onChange(() => {
this.todos = store.todos();
bingings.shouldBePropagated();
});
}
}

FORMS & NG-MODEL

Our applications are not just projections of immutable data onto the DOM. We need to handle the user input. And the primary way of doing it today is NgModel.

NgModel is a convenient way of dealing with the input, but it has a few drawbacks:

  • It does not work with immutable objects.
  • It does not allow us to work with the form as a whole.

For instance, if in the example below todo is immutable, which we would prefer, NgModel just will not work.

<form>
<input [ng-model]="todo.description">
<input [ng-model]="todo.checked">
<button (click)="updateTodo(todo)">Update</button>
</form>

So NgModel forces us to copy the attributes over and reassemble them back after we are done editing them.

class TodoComponent {
todo:Todo;
description:string;
checked:boolean; actions:Actions;
constructor(actions:Actions){
this.actions = actions;
} set todo(t) {
this.todo = t;
this.description = t.description;
this.checked = t.checked;
} updateTodo() {
this.actions.updateTodo(this.todo.id,
{description: this.description, checked: this.checked});
}
}

This is not a bad solution, but we can do better than that.

A better way of dealing with forms is treating them as streams of values. We can listen to them, map them to another stream, and apply FRP techniques.

Template:

<form [control-group]="filtersForm">
<input control-name="description">
<input control-name="checked">
</form>

Component:

class FiltersComponent {
constructor(fb:FormBuilder, actions:Actions) {
this.filtersForm = fb({
description: string, checked: boolean
}); this.filtersForm.values.debounce(500).forEach((formValue) => {
actions.filterTodos(formValue);
});
}
}

We can also take the current value of the whole form, like shown below.

Template:

<form #todoForm [new-control-group]="todo">
<input control-name="description">
<input control-name="checked">
<button (click)="updateTodo(todoForm.value)">Update</button>
</form>

Component:

class TodoComponent {
todo:Todo; actions:Actions;
constructor(actions:Actions) {
this.actions = actions;
} updateTodo(formValue) {
this.actions.updateTodo(this.todo.id, formValue);
}
}

SUMMARY

  • We want to be able to reason about every component in isolation. And we want changes to be predictable and controlled. Functional programming is a common way of achieving it.
  • Data-oriented programming is a big part of it. Although Angular has always supported it, there are ways to make it even better. For example, by improving support of persistent data structures.
  • When using functional programming we have stronger guarantees about how things change. We will be able to use this knowledge to control change detection.
  • The current way of dealing with dealing with the user input, although convenient, promotes mutability. A more elegant way is to treat forms as values, and streams of values.
    • Guest • 3 months ago

      Gibberish. All of this looks like gibberish.

    • 10
    • Reply
    • Share ›
    •  
       
      •  
         
        Julian Drake Guest • 10 days ago

        Agree. Two years working everyday with Angular 1.x and this feels like a completely different language/framework. Why not slowly introduce these V2 features every release over a couple of years instead of invalidating everyone's experience overnight and making all existing components redundant? This might go down in history as an example of how to destroy a grassroots community by pandering to a computer science elite. Who is to say they won't throw all this it out and start again with Angular 3.0?

      • Reply
      • Share ›
      •  
         
      •  
         
        Tim Kindberg Guest • 2 months ago

        I have a feeling his posts are not for a completely general audience and that there are quite a few specific prerequisites to being able to fully understand them; namely, being familiar with ES6 syntax, being moderately familiar with the angular 2 design docs, and perhaps being somewhat involved or actively following the angular 2 development.

        I wouldn't take his posts as some sort of Angular 2 sneak peek or tutorial, but rather as mini white papers that are exploring the challenges and topics that the Angular 2 team themselves are running up against.

      • Reply
      • Share ›
      •  
         
    •  
       
      Gil Birman • 3 months ago

      Victor, I've been using angular for over a year and I have experience with FP and immutable data using Flux in React (seehttps://github.com/gilbox/vizo... and https://medium.com/@gilbox/how.... Yet, as ngDeveloper mentioned, your code examples are extremely difficult to understand. I think a big part of the confusion is that I can't seem to find any information about the angular 2.0 template syntax you're using. I know you said that the API is going to change, but there are too many new constructs here to allow me to see the forest from the trees. What is control-group and new-control-group? What is #todoForm? What is FormBuilder? Can you provide any additional information and/or links?

    • 1
    • Reply
    • Share ›
    •  
       
    •  
       
      Edward Beckett • 18 days ago

      Ahh... yeah... this 'feel's a lot like Java 8 ... loving it so far...

    • Reply
    • Share ›
    •  
       
    •  
       
      Matthew Cooper • 2 months ago

      I don't understand this

      set todo(t) {
      this.todo = t;
      this.description = t.description;
      this.checked = t.checked;
      }

      Where is set todo being called?

    • Reply
    • Share ›
    •  
       
      •  
         
        Victor Savkin Mod Matthew Cooper • 2 months ago

        The change detection will call "set todo" when updating the todo property of this component.

      • Reply
      • Share ›
      •  
         
        •  
           
          Sekib Omazic Victor Savkin • 2 months ago

          There is no ChangeDetector in TodoComponent, so how the change detector knows it has to call "set todo"? Ist this a sort of convention that a component needs a "set" method(s)?

        • Reply
        • Share ›
        •  
           
          •  
             
            Victor Savkin Mod Sekib Omazic • 2 months ago

            Imagine you have something like this <my-component [todo]="t"> in your template.

            What it means is that when the binding `t` changes, Angular will set the new t on my-component (myComponent.todo = newT). And if you override the setter, it will be invoked.

            So todo is not special, and there is no convention. You have to explicitly declare what properties you want to be updated (i.e., to bind them) in your template.

            We are working hard on getting some basic docs out there.

          • Reply
          • Share ›
          •  
             
            •  
               
              Sekib Omazic Victor Savkin • 2 months ago

              To me (myComponent.todo = newT) means I need either "set todo()" in myComponent or there is a "todo" attribute in myComponent (which is fine, as I have <my-component [todo]="t"> in my template). Can I have a method like "updateTodo(newT) { this.todo = newT}" in myComponent and use it in template e.g. <my-component [updatetodo]="t"> ?

            • Reply
            • Share ›
            •  
               
              •  
                 
                Victor Savkin Mod Sekib Omazic • 2 months ago

                The way you should think about it:
                - my-component has the todo property
                - The binding does not invoke the function, it just updates the property. The fact that the property can have a setter does not change this.

                When you write you code:
                - You should avoid having setters.
                - If you do have to have them, they should contain only simple transformations and be idempotent.

                If you want to write <my-component [updatetodo]="t"> you have to have a setter `set updatetoto`.

              • 1
              • Reply
              • Share ›
              •  
                 
    •  
       
      Oto Dočkal • 2 months ago

      So, can we now dirty-check only bindings in one component instead of all the bindings in app?

    • Reply
    • Share ›
    •  
       
    •  
       
      tristanstraub • 3 months ago

      Nice overview of how it will look. Very much what like what React/Flux seems to be, but with angular templating. I hope this approach will help push the benefits of functional programming within my dev team, now that we're already on the angular bandwagon.

    • Reply
    • Share ›
    •  
       
      •  
         
        Victor Savkin Mod tristanstraub • 3 months ago

        Thank you.

        >> Very much what like what React/Flux seems to be.

        Similar to React you do not have to go with the functional approach if you do not want to. The idea is not to make it as painless as possible. So the teams that prefer this style of programming will be able to use it.

        >> I hope this approach will help push the benefits of functional programming within my dev team

        One of the things I am thinking about these days is what kind of tooling we can provide to make it easier for the teams who are not very experienced with FP. I have a few vague ideas, but nothing concrete.

      • 1
      • Reply
      • Share ›
      •  
         
      •  
         
        Guest tristanstraub • 3 months ago

        Hopefully it has more documentation than Flux lol

BETTER SUPPORT FOR FUNCTIONAL PROGRAMMING IN ANGULAR 2的更多相关文章

  1. Functional Programming without Lambda - Part 1 Functional Composition

    Functions in Java Prior to the introduction of Lambda Expressions feature in version 8, Java had lon ...

  2. Functional programming idiom

    A functional programming function is like a mathematical function, which produces an output that typ ...

  3. Beginning Scala study note(4) Functional Programming in Scala

    1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...

  4. Functional Programming without Lambda - Part 2 Lifting, Functor, Monad

    Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...

  5. a primary example for Functional programming in javascript

    background In pursuit of a real-world application, let’s say we need an e-commerce web applicationfo ...

  6. Functional programming

    In computer science, functional programming is a programming paradigm, a style of building the struc ...

  7. Java 中的函数式编程(Functional Programming):Lambda 初识

    Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...

  8. 关于函数式编程(Functional Programming)

    初学函数式编程,相信很多程序员兄弟们对于这个名字熟悉又陌生.函数,对于程序员来说并不陌生,编程对于程序员来说也并不陌生,但是函数式编程语言(Functional Programming languag ...

  9. Functional Programming 资料收集

    书籍: Functional Programming for Java Developers SICP(Structure and Interpretation of Computer Program ...

随机推荐

  1. test20181025 Color

    题意 分析 自己的想法 可以莫队+平衡树. 对每个颜色维护一颗平衡树,然后移动莫队端点的时候在平衡树中查询. 区间加操作容易实现. 单点修改转化为平衡树的插入删除. 感谢Z前辈的指导. 时间复杂度\( ...

  2. pthread访问调用信号线程的掩码(pthread_sigmask )

    掩码: 信号掩码 在POSIX下,每个进程有一个信号掩码(signal mask).简单地说,信号掩码是一个"位图",其中每一位都对应着一种信号.如果位图中的某一位为1,就表示在执 ...

  3. 在 Linux redis 验证交互连接过程中遇到 redis Could not connect to Redis at 127.0.0.1:6379: Connection refused 的解决方法

    Could not connect to Redis at 127.0.0.1:6379: Connection refused 1.找到redis.conf 并修改 daemonize no 为 d ...

  4. 写给C#程序员的javascript说明: 各类型变量和prototype

    在javascript中存在类似的私有变量 公有变量和静态变量 私有: var AA=function(){ var aa="im private"; }; 私有变量通过闭包访问. ...

  5. Oracle使用startup与startup force启动的区别

    1. startup 就是正常启动数据库,没什么好说的. 2. startup force 是shutdown abort + startup的组合,即强制关闭数据库+ 正常启动数据库,想快速重启数据 ...

  6. Azkaban任务流编写

    在Azkaban中,一个project包含一个或多个flows,一个flow包含多个job.job是你想在azkaban中运行的一个进程,可以是Command,也可以是一个Hadoop任务.当然,如果 ...

  7. Spring容器初始化数据(数据库)BeanPostProcessor的应用

    1.目的:在Spring启动的时候加载在数据库保存的配置信息,一方面杜绝随意修改,一方面方便管理 2.BeanPostProcessor是Spring提供的一个方法通过implements方式实现 会 ...

  8. mysql-3 数据表的创建、增删改查

    1.创建数据表 通用语法:CREATE TABLE table_name (column_name column_type); CREATE TABLE IF NOT EXISTS `csj_tbl` ...

  9. CentOS–root密码忘记的解决办法

    一.重启系统,如图:GRUB: 在引导装载程序菜单上,用上下方向键选择你忘记密码的那个系统键入“e”  来进入编辑模式.   2.接下来你可以看到如下图所示的画面,然后你再用上下键选择最新的内核(这里 ...

  10. [saiku] 通过管理台配置用户、schema和数据源

    上一篇讲到了如何下载和安装saiku [http://www.cnblogs.com/avivaye/p/4877680.html] 本文简介下saiku用户的配置操作和需要注意的点 一.添加用户 S ...