Render value without data-binding
Asked Answered
M

5

86

In AngularJS, how can I render a value without 2-way data binding? One may want to do this for performance reasons, or even rendering a value at a given point in time.

The following examples both use data binding:

<div>{{value}}</div>

<div data-ng-bind="value"></div>

How do I render value without any data binding?

Menstruation answered 13/9, 2013 at 15:44 Comment(6)
whats your input and output . plz explainConcerning
Your examples are actually one-way data binding (model changes -> view updates). ng-model gives you two-way data binding: model changes -> view updates, view changes -> model updates.Aeropause
updated. sorry i meant I want no data-binding at allMenstruation
I don't think this question is terrible or deserved a downvote. It's actually really common to want to disable data binding to prevent unnecessary watches.Stylist
@Stylist if this question is about disabling data binding then it's a duplicate because i have asked that question here. #18240668Methodology
UPDATE: anyone reading this article will probaly find this video extremely useful. youtube.com/watch?v=zyYpHIOrk_YMenstruation
M
140

Angular 1.3+

In 1.3, Angular has supported this using the following syntax.

<div>{{::message}}</div>

As mentioned in this answer.


Angular 1.2 and below

This is simple and doesn't need a plugin. Check this out.

This small directive will easily accomplish what you are trying to achieve

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope ) {
            setTimeout(function() {
                $scope.$destroy();
            }, 0);
        }
    }
});

You can bind once like this

<div bind-once>I bind once - {{message}}</div>

You can bind like normal

<div ng-bind="message" bind-once></div>

Demo: http://jsfiddle.net/fffnb/

Some of you may be using angular batarang, and as mentioned in the comments if you use this directive the element still shows as binding when it is not, I am pretty sure this has something to do with the classes that are attached to the element so try this, it should work (not tested). Let me know in the comments if it worked for you.

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope, $element ) {
            setTimeout(function() {
                $scope.$destroy();
                $element.removeClass('ng-binding ng-scope');
            }, 0);
        }
    }
});

@x0b: If you have OCD and you want to remove the empty class attribute do this

!$element.attr('class') && $element.removeAttr('class')
Methodology answered 13/9, 2013 at 16:57 Comment(14)
im yet to test the plugin, but I would assume the AngularJS chrome tools would not show the bind-once element as a binding, where as your example does. Interesting approach tho, I will test both approaches soon.Menstruation
See this - it shows both as bindings dl.dropboxusercontent.com/u/14037764/Development/stackoverflow/…Menstruation
Without a doubt that is because if the ng-binding class that you can easily removeMethodology
In the setTimeout remove the ng-binding and ng-scope classesMethodology
This is great and much simpler than the bindonce plugin. I added an ability to wait for a condition before destroying the scope and it's really helpful. thanks.Timehonored
Wouldn't it be better to use $timeout to make it testable?Traipse
@RobertKoritnik I can't see any advantages using $timeout, it will be doing more operations that I don't think are needed, of course you can do though, if you wishMethodology
For those who want to remove a redundant empty class attribute then add this: if ($element.attr('class') === '') { $element.removeAttr('class'); }Lyons
@Lyons cheers, I've done that before, it does get annoying I'll add a reference in the answer.Methodology
How does this work for deferred objects on a scope? The bind-once is destroying the scope before the property I want to bind-once is resolved.Preraphaelite
@Preraphaelite that defeats the object of "bind once", if your property isn't resolved then the first bound value will be undefined, you're going to need a different solution for a different problemMethodology
@Connor I disagree. For example, I'm receiving a video object ($scope.video) from a REST API and I want one-time binding of the video title ($scope.video.title). Even if I resolve the promise BEFORE adding it to the scope in the controller, I still have to declare ng-bind="video.title" bind-once on the DOM. Now, before the promise gets resolved, video.title is undefined, and the scope gets destroyed before video.title is defined. A solution I have for this is to wrap the elements in some type of loading/init flag, ng-if="someLoadingFlag", but it's a poor pattern.Preraphaelite
@Connor - the "$scope.$destroy" will not remove the $watch (on angular 1.2). Any ideas how this could be fixed? see #27400922Soapsuds
After struggling for a while, I made a Question regarding the Batarang thing: #32012504Elephantine
G
49

It looks like Angular 1.3 (starting with beta 10) has one-time binding built in:

https://docs.angularjs.org/guide/expression#one-time-binding

One-time binding

An expression that starts with :: is considered a one-time expression. One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value (see value stabilization algorithm below).

Godden answered 4/6, 2014 at 16:45 Comment(5)
This answer again and again. I can't praise you enough Karl! I highly recommend the agressive usage of this feature wherever it makes sense.Biarritz
Wow I am really glad I scrolled down. I'm going to ask Connor to reference this in his accepted answer.Piemonte
I have a table / list with 2000 lines and using the one-time-binding operator my app gets extremely slow when first showing / rendering the list. So slow, that the browser asks me two or three times if I want to stop executing the script!Isoagglutination
@billy-g Can you post a jsfiddle or plunker illustrating the issue?Absorbent
@James Daily: Here is the "normal" case plnkr.co/edit/rCRP0T5fSgNIllx7F27y and here the "one-time expression" case plnkr.co/edit/Rd5VBVjkcX3sTJYGypUr but... I can not reproduce it there. Anyway, it isn't faster with the "one-time expression" and I have to do more investigation to find why it happens in my environment (I use 1.3 beta 18 of angularjs)Isoagglutination
S
20

Use the bindonce module. You'll need to include the JS file and add it as a dependency to your app module:

var myApp = angular.module("myApp", ['pasvaz.bindonce']);

This library allows you to render items that are bound only once — when they are first initialized. Any further updates to those values will be ignored. It's a great way to reduce the number of watches on the page for things that won't change after they are rendered.

Usage example:

<div bo-text="value"></div>

When used like this, the property under value will be set once it is available, but then the watch will be disabled.

Stylist answered 13/9, 2013 at 16:0 Comment(3)
I was about to write an answer "write your own directive ...", but it looks like someone has already done that for us, nice.Aeropause
Bindonce is useful enough that it could be included as a built-in optional library, like $resource.Stylist
this is what i was looking for, however I was expecting something like this to be built into angular!Menstruation
M
7

Comparison between @OverZealous and @Connor answers :

With the traditional ngRepeat of angular : 15s for 2000 rows and 420mo of RAM (Plunker)

With ngRepeat and the module of @OverZealous : 7s for 2000 rows and 240mo of RAM(Plunker)

With ngRepeat and the directive of @Connor : 8s for 2000 rows and 500mo of RAM (Plunker)

I made my tests with Google Chrome 32.

Manifold answered 5/5, 2014 at 8:11 Comment(3)
Would be nice to also have angular-once compared. Thanks.Adrien
@Adrien : I planned to do the tests when a stable build of AngularJS 1.3 be published.Manifold
Thanks, don't forget to include angular-once package (I've posted it as an alternative option here).Adrien
A
5

As an alternative, there is angular-once package:

If you use AngularJS, have performance issues and need to display lots of readonly data, this project is for you!

angular-once was actually inspired by bindonce and provides similar once-* attributes:

<ul>
    <li ng-repeat="user in users">
      <a once-href="user.profileUrl" once-text="user.name"></a>
        <a once-href="user.profileUrl"><img once-src="user.avatarUrl"></a>
        <div once-class="{'formatted': user.description}" once-bind="user.description"></div>
    </li>
</ul>
Adrien answered 10/9, 2014 at 1:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.