Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide one time binding possibility for better performance #14033

Open
chaouiy opened this issue Jan 20, 2017 · 24 comments
Open

Provide one time binding possibility for better performance #14033

chaouiy opened this issue Jan 20, 2017 · 24 comments
Assignees
Labels
area: core Issues related to the framework runtime core: binding & interpolation Issue related to property/attribute binding or text interpolation core: change detection feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature
Milestone

Comments

@chaouiy
Copy link

chaouiy commented Jan 20, 2017

For many use cases we need to render elements according to first initial value without the need to observe any changes. It would be great if we would have the ability to make one time bindings.

@devoto13
Copy link
Contributor

And you can by not using binding syntax, but using usual HTML attributes.

@tytskyi
Copy link

tytskyi commented Jan 20, 2017

@youneschaoui You can hardcore data in template as per @devoto13 suggestions. Also you could use advanced technics such as https://angular.io/docs/ts/latest/api/core/index/ChangeDetectorRef-class.html and https://angular.io/docs/ts/latest/api/core/index/ChangeDetectionStrategy-enum.html

@kemsky
Copy link

kemsky commented Jan 22, 2017

Its actually makes sense, currently you can do it only on component level (ChangeDetectionStrategy.OnPush).

@pkozlowski-opensource pkozlowski-opensource added area: core Issues related to the framework runtime feature Issue that requests a new feature labels Feb 1, 2017
@mbtakanov
Copy link

mbtakanov commented Jun 9, 2017

I have a text file that holds all strings constants (in two languages) used to display something on the webpage (e.g. "Select item", "Welcome, this is XXX website.", ect.). I would like these strings to be displayed and after that never to be watched for changes. In AnguarJS the double semicolon was great feature. My question is why this was replaced with "only on component level" option in Angular 2? Can someone explain? There must be some explanation that I can't figure out. I really want to understand the logic behind this implementation and eventually I (or Angular Team?!) change the way this works.

@dawidgarus
Copy link

You can create directive that detaches ChangeDetectorRef:

@Directive({
  selector: '[oneTime]',
})
export class OneTimeDirective {
  constructor(template: TemplateRef<any>, container: ViewContainerRef, zone: NgZone) {
    zone.runOutsideAngular(() => {
      const view = container.createEmbeddedView(template);
      setTimeout(() => view.detach());
    })
  }
}

Usage:

Hello <ng-template oneTime>{{name}}</ng-template>!

@kamok
Copy link

kamok commented Nov 12, 2017

@dawidgarus any idea for something like this?

<mat-select [placeholder]='account.employer' [(ngModel)]="account.employer">

I want to set one time binding for placeholder. But I'd still like ngModel to work with 2 way binding. I only need the label to be rendered the "old" employer.

@torch2424
Copy link

Thank you @dawidgarus I'll probably be implementing this later today.

I agree this would be nice to have in Angular as well. Also, I do agree that we could just code this ourselves in our application, but I feel that it would be a great feature to carry over as it gives a lot of ease of use to users.

@dagyrox
Copy link

dagyrox commented Dec 1, 2017

@kamok why not just use a seperate variable to hold the original value;

testOriginal: string;
@input testModel: string;

onNgInit(){
this.testOriginal = this.testModel;
}

'{{testOriginal}}'

@aeslinger0
Copy link

I'm confused by the need for other options when the first reply by @devoto13 seems like the easiest built-in way. Is there a drawback that I'm not aware of to using this?
<a [attr.href]="'mailto:' + myOneTimeBindingUrl">

@devoto13
Copy link
Contributor

devoto13 commented Feb 20, 2018

@aeslinger0 It will set attribute instead of property, but it is still a binding, so CD will keep checking it on subsequent change detection run. What I suggested is that you can hard code value in the template, but it assumes that you know value beforehand, i.e. not load it from somewhere.

<my-fancy-input fanciness="spectacular"></my-fancy-input>

This will check fanciness property only once.

@t03apt
Copy link

t03apt commented Mar 20, 2018

I am also looking for a one time binding.
In addition I'd like to be able to trigger an update for the binding manually.

For example it can be done in Aurelia like this:

title.one-time="getComputedValue() & signal:'update-bindings'"

@trotyl
Copy link
Contributor

trotyl commented Apr 2, 2018

Made a lib implementation of ngNoCheck, demoed at:

https://stackblitz.com/edit/angular-akczef?file=app%2Fdemoes.component.ts

@mleibman
Copy link

mleibman commented Apr 3, 2018

IMHO, this issue is focusing too much on change detection while completely ignoring the rendering speed.

The default implementation of NgForOf uses an IterableDiffer to detect changes to the collection and process them in a way that makes it easy to make efficient incremental changes to the DOM. It does this by building up doubly-linked lists for items, additions, removals, moves, and identity changes. The implementation is pretty efficient, but it is optimized for successive mutations, and not for initial rendering. On the first run or, more importantly, if the collection never changes, there is no need to do any of the processing at all. In case of one-time-bindings, this just ends up doing useless work, increasing memory pressure, and polluting the DOM with comment nodes as insertion markers.

According to my tests, the rendering could be sped up by ~25% if done directly using createEmbeddedView() or by implementing a one-time-binding / immutable version of NgForOf:
https://stackblitz.com/edit/ng2-one-time-binding

@tony-gutierrez
Copy link

Adding to the calls for a simple way to achieve binding to a function to be executed only once. This would be really helpful for simple translation tables.

@mdarefull
Copy link

I'd like to add from the component's user perspective.

ComponentA might require some initial data to be configured with, such data might not be always a string, but can be as simple as a true/false value. If we use the first suggestion of @devoto13 then we will only be able to pass string values, even worst if we need to pass an entire object. In some situations, it can even be "dangerous" to modify that property because the component might not be designed to support changing on its input, leading to repetitive checks and runtime errors.

Now, from the component's user perspective, it will still be a normal input binding, and, by definition, they establish a one-way binding with the property they are bound to and it won't be clear for the user which
@inputs are treated like "initialization parameters" and which one are actual bindings. Leading to bloated documentation and maintainability issues.

These situations have been very common at my project and are the actual reasons I'm writing this.

@andrei-tatar
Copy link

I'm curious what impact has @dawidgarus solution on performance... Does it improve it? Or the directive overhead cancels any benefits? Did anyone get a chance to test it?

@mlc-mlapis
Copy link
Contributor

mlc-mlapis commented Aug 22, 2018

@tony-gutierrez ... it's possible to use a pure pipe like this for one-run binding ... and re-run only when binded value has been changing ... {{ templateValue | fn:componentMethodRef:fnArgument }} ... where templateValue would be for example the language component property, with dynamic values like en, de, ...

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
	name: 'fn',
	pure: true
})
export class FnPipe implements PipeTransform {

	// A template syntax: {{ templateValue | fn:componentMethodRef:fnArgument }}
	// ... should be transformed to: null.componentMethodRef(templateValue, fnArgument)
	transform(
		templateValue: any,
		fnReference: Function,
		...fnArguments: Array<any>
	): any {

		fnArguments.unshift(templateValue);

		// The function reference will NOT BE INVOKED IN THE COMPONENT CONTEXT.
		// This means that the function reference won't have a "this" context
		// during execution, unless the function is pre-bound to the component.
		return(fnReference.apply(null, fnArguments));
	}
}

@antonpegov
Copy link

Oh. common!

@elarbee
Copy link

elarbee commented May 15, 2019

Also could use this feature. Working on an app which roughly follows a flux pattern and have components that need to accept and emit data in different formats.

My workaround is not super elegant but works fine for my purposes.

 // Only allow one way binding here
  @Input()
  set tags(tags: Tag[]) {
    if (!this.tagsSet) {
      this._tags = tags;
      this.tagsSet = true;
    }
  }
  tagsSet = false;
  _tags: Tag[];

@mhintzke
Copy link

mhintzke commented May 29, 2019

I also have a use case that I'm sure many others do as well that would make this viable. In my application we render relative timestamps like 2 h ago, 13 min ago, 47 s ago, etc. You can accomplish this easily enough by performing Date.now() - timestamp and converting to the appropriate unit. The problem is due to multiple digest cycles Date.now() is variable among cycles and causes the UI to change the values at random times. If I had the ability for a one time binding then I would have more control over these values functional resolution.

Currently I am using a custom pipe to perform such a transformation of the Date model, but I will instead have to feed my models into a helper function and use a new property altogether to hold the difference value which accrues more engineering time and additional view models that previously would not be needed

@pkozlowski-opensource pkozlowski-opensource added core: binding & interpolation Issue related to property/attribute binding or text interpolation core: change detection labels Mar 10, 2020
@georgelviv
Copy link

georgelviv commented Apr 13, 2020

I would like to have the possibility to specify from the parent component property binding, inputs will never change, but in others, I want still to have these options. For example, I have some component like this

// Command-Bar.component.ts
@Input() actionButtonsList: ActionBtn[]

And I have 3 possible ways how do I want to use this component

  • App1 has dynamic action button lists, (user with some triggers can change them)
  • App2 receives for example from API list of actions buttons, and will never change
  • App3 has reusable App3CommandBar, which has static action buttons lists

I have an idea of how to reach this effect, with implementing something like this:

// Command-Bar.component.ts
@Input('actionButtonsList') : ActionBtn[] | string
set actionButtonsList(value) {
 if (typeof value === 'string' and validJsonOption(value)) {
  _actionButtonsList = JSON.parse(value)
 }
}

// Staitc
<command-bar  actionButtonsList="staticActionBtns">

// Dynamic
<command-bar  [actionButtonsList]="dynamicActionBtns">

But seems like this is not a very pretty solution

@angular-robot angular-robot bot added the feature: under consideration Feature request for which voting has completed and the request is now under consideration label Jun 4, 2021
@grg-kvl
Copy link

grg-kvl commented Jul 4, 2021

Any updates?

@nawlbergs
Copy link

nawlbergs commented Aug 25, 2021

I have a major angularJs application still in production, with a slow baby-step process of moving into modern angular. This is one of the items Im keeping an eye on. Our clients can label basically everything... based on a config loaded at application init... which will not change after initial template render. It was really easy to implement in angularJs. If its not a concern with how change detection works in in modern angular.. great.. but it seems like not having to deal with the 100's of bindings on our pages after load... would save on performance. Maybe not.. either way.. i eagerly await the recommended way to deal with this issue. Thanks! (keep up the great work)

ps - we do almost of the binding thro a pipe... eg {{ 'someLabel' | appConfig }}

@petebacondarwin petebacondarwin added this to Inbox in Feature Requests via automation Oct 1, 2021
@petebacondarwin petebacondarwin moved this from Inbox to Needs Discussion in Feature Requests Oct 1, 2021
@eneajaho
Copy link
Contributor

It can be something like vue's v-once: https://v3.vuejs.org/api/directives.html#v-once

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: core Issues related to the framework runtime core: binding & interpolation Issue related to property/attribute binding or text interpolation core: change detection feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature
Projects
No open projects
Feature Requests
Needs Project Proposal
Development

No branches or pull requests