<p dir="auto">[ ] bug report<br>
[x] feature request<br>
[ ] support request</p>
<p dir="auto"><strong>What is the motivation / use case for changing the behavior?</strong><br>
Angular is built with Observables and we have <code class="notranslate">async</code> pipe which is great. But it makes a template a little bit noisy. There is standard way to call Observable type variables with <strong>$</strong> in the end of name, so we can simplify a template by omitting the pipe and making variables with <strong>$</strong> recognizable as "asynced" by default.</p>
<div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;my-copmonent [data]=&quot;dataStream$ | async&quot;&gt;&lt;/my-copmonent&gt;
&lt;my-copmonent *ngIf=&quot;!(dataStream$ | async)&quot;&gt;&lt;/my-copmonent&gt;
// into
&lt;my-copmonent [data]=&quot;dataStream$&quot;&gt;&lt;/my-copmonent&gt;
&lt;my-copmonent *ngIf=&quot;!dataStream$&quot;&gt;&lt;/my-copmonent&gt;"><pre class="notranslate"><span class="pl-kos">&lt;</span><span class="pl-ent">my-copmonent</span> <span class="pl-c1">[data]</span>="<span class="pl-s">dataStream$ | async</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">my-copmonent</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">my-copmonent</span> <span class="pl-c1">*ngIf</span>="<span class="pl-s">!(dataStream$ | async)</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">my-copmonent</span><span class="pl-kos">&gt;</span>
// into
<span class="pl-kos">&lt;</span><span class="pl-ent">my-copmonent</span> <span class="pl-c1">[data]</span>="<span class="pl-s">dataStream$</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">my-copmonent</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;</span><span class="pl-ent">my-copmonent</span> <span class="pl-c1">*ngIf</span>="<span class="pl-s">!dataStream$</span>"<span class="pl-kos">&gt;</span><span class="pl-kos">&lt;/</span><span class="pl-ent">my-copmonent</span><span class="pl-kos">&gt;</span></pre></div>
<p dir="auto">I guess there should be some configuration config option to not to brake old style,<br>
or we can look in a template if it has <code class="notranslate">async</code> pipe.</p>
<h1 dir="auto">Requirements</h1>
<ul dir="auto">
<li>[R1] Observables must be cold (ie if there is no subscription then there should be no listeners.)<br>
USE CASE: drag and drop. No need to listen on mousemove if drag has not been initiated.</li>
<li>[R2] It must be possible to execute an expression which computes the action which will be next-ed into the observable.
<ul dir="auto">
<li>USE CASE: ngFor shows a list of todos with a remove button. Clicking the remove should notify the stream not with the DOM event, but with the item to be removed.</li>
</ul>
</li>
<li>[R3] It must be possible to set up observable recipes to run inside/outside of angular zone.
<ul dir="auto">
<li>USE CASE:
<ul dir="auto">
<li>drag and drop. Don't run CD on mousemove.</li>
<li>Throttle; debounce; etc..: Only run CD on subscription which pass throttle, debounce; etc.</li>
</ul>
</li>
</ul>
</li>
<li>[R4] It would be strongly preferable if there was something in the template which would indicate that an observable is listening at a given location.
<ul dir="auto">
<li>USE CASE: it should be easy for the developer/tooling to see where in the source code the event will be processed.</li>
</ul>
</li>
</ul>
<h1 dir="auto">Design</h1>
<ul dir="auto">
<li>Component will annotate fields which will represent Observable Streams which will be created by the Template's ViewFactory. [R4] (This will also determine the parameterization for the Observables. (ie <code class="notranslate">Observable&lt;MyType&gt;)</code>)
<ul dir="auto">
<li>The Framework will set those fields right after constructing the object. We need a new hook (<code class="notranslate">ngOnCreate</code>) which would be called right after setting the fields but before running first change detection (<code class="notranslate">ngOnChanges</code>, <code class="notranslate">ngOnInit</code>).</li>
</ul>
</li>
<li>Create new syntax which will describe [R4]:
<ul dir="auto">
<li>Which events should the template listen on [R4].</li>
<li>How should the action be computed [R2].</li>
<li>Which stream should receive the action<br>
*Split Angular Zone into <code class="notranslate">NgErrorZone</code> for error handling and <code class="notranslate">NgChangeDetectionZone</code> for CD. [R3]</li>
<li>Provide convenience method running code in <code class="notranslate">NgErrorZone</code> and <code class="notranslate">NgChangeDetectionZone</code>. [R3]</li>
</ul>
</li>
</ul>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@Component({
  template: `
    &lt;ul&gt;
      &lt;li *ngFor=&quot;let user of users; let i = index&quot;&gt;
        {{user.name}}
        &lt;button (*click|remove)=&quot;{item: user, index: i}&quot;&gt;X&lt;/button&gt;
      &lt;li&gt;
    &lt;ul&gt;
`
})
class MyComponent implements OnCreate{
  users: User[] = ...;


  // Specify that this will have property injection.
  @Inject()
  remove: Observable&lt;{item: User, index: number}&gt;;


  actions: Observable&lt;Action&gt;;


  ngOnCreate() {
    // An example of building up a recipe.
    // Notice that we are making sure that Rx operators have no CD.
    this.actions = noChangeDetection(() =&gt; remove.map(() =&gt; ...));
  }
}"><pre class="notranslate">@<span class="pl-smi">Component</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
  <span class="pl-c1">template</span>: <span class="pl-s">`</span>
<span class="pl-s">    &lt;ul&gt;</span>
<span class="pl-s">      &lt;li *ngFor="let user of users; let i = index"&gt;</span>
<span class="pl-s">        {{user.name}}</span>
<span class="pl-s">        &lt;button (*click|remove)="{item: user, index: i}"&gt;X&lt;/button&gt;</span>
<span class="pl-s">      &lt;li&gt;</span>
<span class="pl-s">    &lt;ul&gt;</span>
<span class="pl-s">`</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span>
<span class="pl-k">class</span> <span class="pl-smi">MyComponent</span> <span class="pl-k">implements</span> <span class="pl-smi">OnCreate</span><span class="pl-kos">{</span>
  <span class="pl-c1">users</span>: <span class="pl-smi">User</span><span class="pl-kos">[</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> ...<span class="pl-kos">;</span>


  <span class="pl-c">// Specify that this will have property injection.</span>
  @<span class="pl-smi">Inject</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
  <span class="pl-c1">remove</span>: <span class="pl-smi">Observable</span><span class="pl-kos">&lt;</span><span class="pl-kos">{</span><span class="pl-c1">item</span>: <span class="pl-smi">User</span><span class="pl-kos">,</span> <span class="pl-c1">index</span>: <span class="pl-smi">number</span><span class="pl-kos">}</span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>


  <span class="pl-c1">actions</span>: <span class="pl-smi">Observable</span><span class="pl-kos">&lt;</span><span class="pl-smi">Action</span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>


  <span class="pl-en">ngOnCreate</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-c">// An example of building up a recipe.</span>
    <span class="pl-c">// Notice that we are making sure that Rx operators have no CD.</span>
    <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">actions</span> <span class="pl-c1">=</span> <span class="pl-en">noChangeDetection</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">remove</span><span class="pl-kos">.</span><span class="pl-en">map</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> ...<span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<ul dir="auto">
<li>ViewFactory creates an instance of Observable which then is property injected into component
<ul dir="auto">
<li>Observable names and types (Type parameters) are declared in the component</li>
<li><code class="notranslate">@Inject()</code> identifies that the observable has to be generated in the <code class="notranslate">ViewContainer</code>.</li>
<li>Need a <code class="notranslate">ngOnCreate()</code> hook to create recipes.</li>
</ul>
</li>
<li>Need <code class="notranslate">noChangeDetection()</code> method for  going in and out of CD. Separate Design Doc TBD.</li>
<li><code class="notranslate">(*click|remove)="{item: user, index: i}"</code> Placeholder syntax, see discussion later. This is used to generate cold event registration for DOM events. The expression is used to compute the value which will be next ed into the observable.
<ul dir="auto">
<li>Notice that even though there are zero or more instance of places where we listen into DOM events, there is always only one observable instance given that name.</li>
</ul>
</li>
</ul>
<h1 dir="auto">Syntax Discussion</h1>
<p dir="auto">NOTE: The above syntax is used only as a placeholder</p>
<p dir="auto">The Syntax Requirements:</p>
<ul dir="auto">
<li>The syntax needs to encode three items:
<ul dir="auto">
<li><code class="notranslate">event</code>: The DOM event</li>
<li><code class="notranslate">stream</code>: The destination stream</li>
<li><code class="notranslate">actionExp</code>: The action object</li>
</ul>
</li>
<li>Given that we need to encode three things and that <code class="notranslate">key="value"</code> only takes two by default we have to figure out how to encode three things into two. The choices are:
<ul dir="auto">
<li><code class="notranslate">event+stream="actionExp"</code>: [favorite]
<ul dir="auto">
<li>This format combines event and stream on the LHS.</li>
<li>Both event and stream are just symbols (ie foo) not complex expressions (ie <code class="notranslate">{name: foo + 2}</code>) and so they can be on the LHS.</li>
<li>Event is already on LHS in <code class="notranslate">(event)="stmt"</code> so it should stay there.</li>
<li>There is already a precedence for compound LHS in <code class="notranslate">style.width.px="123"</code>.</li>
<li>Example: <code class="notranslate">(*click|remove)="actionExp"</code>.
<ul dir="auto">
<li>The <code class="notranslate">*</code> is preferred (not necessary) as it hints that it is cold (not always listening)</li>
<li>The <code class="notranslate">(...)</code> should be retained to be consistent with normal event listeners</li>
</ul>
</li>
</ul>
</li>
<li><code class="notranslate">event="stream+actionExp"</code>: [probably not]
<ul dir="auto">
<li>Micro Syntax has precedence for combining multiple formats on the RHS (but it requires special symbol on the LHS to activate it.)</li>
<li>Microsyntax is shorthand for an expanded canonical form. Here there would be no canonical form.</li>
<li>Example: <code class="notranslate">(*click)="actionExp into remove"</code>
<ul dir="auto">
<li>The <code class="notranslate">*</code> is required as it hints that it is cold (not always listening) and that it is different from normal listener.</li>
<li>The <code class="notranslate">(...)</code> should be retained to be consistent with normal event listeners</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code class="notranslate">stream-action="actionExp" stream-event="event"</code>: [strongly against]
<ul dir="auto">
<li>Require two key-value pairs.</li>
<li>This format is just very wordy and cumbersome.</li>
<li>Puts event on the RHS whereas it is on LHS in <code class="notranslate">(event)="stmt"</code>.</li>
</ul>
</li>
</ul>