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

rebind #42

Open
romanesko opened this issue Dec 17, 2013 · 65 comments
Open

rebind #42

romanesko opened this issue Dec 17, 2013 · 65 comments

Comments

@romanesko
Copy link

Is it possible to force refresh of bindonce item?

@dominicglenn
Copy link

I had this usecase just today actually.

By removing the following line:
https://github.com/Pasvaz/bindonce/blob/master/bindonce.js#L170

And adding scope.bindonceController = bindonceController; to the following line:
https://github.com/Pasvaz/bindonce/blob/master/bindonce.js#L188

I can call $scope.bindonceController.runBinders(); whenever I want to manually update the DOM. It would be really helpful if the code could be changed to support this.

@Pasvaz
Copy link
Owner

Pasvaz commented Jan 16, 2014

I'm sorry to let you wait, I have to push some updates, one of them includes the possibility to refresh the binders by user request, I think that would close this issue. I just need a bit of time (very rare thing right now for me) to clean it and push it.

@dominicglenn
Copy link

As a follow up to this; I'm guessing you're redoing a lot of the binders themselves here as well?

This like the bo-class binding, or the bo-if need modifying to add or remove elements in either case, not just do the positive effect or nothing? (I'm modifying it myself at the moment for this purpose exactly).

@dominicglenn
Copy link

Probably should; I'm just doing it at work atm and not sure how they'd feel about me doing that. But I'll change it on my own and try submit a PR.

@rileylark
Copy link

I also have this use case and suggested #50 . The API could be bo-rebind="whateverEventName". bo-rebind would listen $on $rootScope for whateverEventName and call bindonceController.runBinders() from @dominicglenn 's suggestion.

What do you all think? I'm going to work on this for my own project this week - would others like this api?

cc @Pasvaz

@Pasvaz
Copy link
Owner

Pasvaz commented Jan 28, 2014

Soon (I think this weekend) I'll publish two new directives, one is onewatcher and the other is bo-refresh-on, bo-refresh-on works exactly as you thought, it uses $on to listen on the channel declared in the attribute boRefreshOn and it runs the binders again.
There is another way to achieve this result, in my experimental version I use also a setter to attach a refresh function to the scope, something like bo-refresh-with="refreshMe" so when you call $scope.refreshMe() bindonce runs the binders. If I can test it enough I could publish also bo-refresh-with, let's see.

@rileylark
Copy link

Perfect, that's exactly what I had in mind. Thanks! Let me know if you can use help either implementing or testing.

@Pasvaz
Copy link
Owner

Pasvaz commented Jan 28, 2014

thank you @rileylark, I appreciate it.

@rileylark
Copy link

I've got some huge $watches dragging me down and will need to start work on this soon. I'll be happy to offer a PR but don't want to redo work you've already done. @Pasvaz, do you have a branch in progress I can extend?

@Meligy
Copy link

Meligy commented Feb 13, 2014

Hey,

I love the idea of using events, and the idea of one watcher will make a huge appeal to many people.

Is this planned for any time soon?

Thanks a lot.

@Pasvaz
Copy link
Owner

Pasvaz commented Feb 13, 2014

I'll try to push my branch today, I'm sorry I can't do it as soon as I thought but I'm facing some deadlines.

@rileylark
Copy link

Hey, no worries, bindonce will save me a ton of time even without this feature. I'd appreciate seeing your branch for this issue even incomplete - I'm sure I'll at least learn about your architecture by seeing where you've started, and maybe I can even help finish the implementation!

@Pasvaz
Copy link
Owner

Pasvaz commented Feb 14, 2014

I pushed the branch rebind-debug https://github.com/Pasvaz/bindonce/blob/rebind-debug/bindonce.js , it works but it's still embrional, I need to test it extensively and to rewrite/clean the code.
It uses the directive refresh-on="message" and one-watcher to refresh the binders. Feel free to play with it but don't use it in production.

@Pasvaz
Copy link
Owner

Pasvaz commented Feb 17, 2014

@rileylark it would be nice if you and the others could be test the rebind-debug branch extensively in order to evidentiate the bugs or memory leaks so I can change or confirm the way it works.

@rileylark
Copy link

Thanks a bunch - I played with it some this weekend and will have some serious time this week.

@rileylark
Copy link

Hah, I hadn't seen your last comment when I posted. Yes, I'll be happy to send you some of my tests. Is the new demo folder a good place to put new uses?

I didn't have angular set up in the same build setup that you have, so in my initial test page I just included it from the CDN.

@Pasvaz
Copy link
Owner

Pasvaz commented Feb 17, 2014

feel free to modify the demo as you want or to create new ones, that branch is supposed to be a work in progress so it is quite wild. At the moment, the first approach was to emulate angular and so to destruct and rebuild each bo-directive, the next step will be to improve it and reuse elements instead of removing them in order to speed up the whole process.

@rileylark
Copy link

@Pasvaz I just sent you a PR with a simple demo for bo-src and bo-style.

I am not sure that $broadcast and $emit are going to be sufficient for my project. I am making a slide editor, with infrequently-changing slides. Each slide is represented twice: once in a long list of all of the previews, and again in the main editor area.

screen shot 2014-02-18 at 7 43 09 pm

These are sibling scopes, and each of my slides should have its own rebind-on event, so I think I would have to $emit the change up to $rootScope and then $broadcast them back down. Blegh!

I have an alternative idea to using the $scopes as event busses: a complementary bindonceNotify service that I can inject into any required code. My client code can call bindonceNotify('customUpdateEventName'), and your rebind-on api can register on a back end of the service. That way we can ignore the $scopes altogether, avoid muddying up the $on handlers, and have a kind of private channel for rebind notifications.

What do you think? I can submit another PR for this if you think it's a worthwhile direction to explore.

@rileylark
Copy link

I went ahead and sketched it out in #60

@bugwelle
Copy link

Any further progress on rebind? :)
I need this in an app I'm currently developing on :)

@r3m0t
Copy link

r3m0t commented Feb 28, 2014

@archer96 I've made an Angular fork that does this, it is at:

https://github.com/r3m0t/angular.js/tree/digest_limit

It is built at:

https://github.com/r3m0t/bower-angular tag: 1.2.14-build.local+sha.1289c58 or v1.2.15-build.local+sha.9660989

An example is at:

https://gist.github.com/r3m0t/9271790

Watch out, it works very differently from pasvaz.bindonce. You can use ng- for everything instead of bo-, but inside the new-once directive all the directives are affected.

@Pasvaz
Copy link
Owner

Pasvaz commented Feb 28, 2014

guys I'll complete it asap, right now I'm busy dealing with some deadlines. @archer96 could you test the rebind branch, it's not complete but some feedback could surely help.

@bugwelle
Copy link

bugwelle commented Mar 1, 2014

Hello,

I tried using that branch but there is one tiny "bug" that is annoying. Firstly my code:

<body ng-controller="MainCtrl">
<button ng-click="AddItem()">Add Item</button>
<button ng-click="RemoveFirstItem()">Remove First Item</button>
<ul>
    <li ng-show="!items">Items are not loaded, yet!</li>
    <li ng-show="items && !items.length">No items available!</li>
    <li bindonce="items" refresh-on="'rinfresca'" ng-repeat="item in items">
        <span bo-text="item.title"></span><br>
        <span bo-text="item.description"></span>
    </li>
</ul>
</body>

here my javsacript code:

var app = angular.module('bindoncedebug', ['pasvaz.bindonce']);

app.controller('MainCtrl', function ($scope, $q, $timeout) {

var count = 0;

$scope.AddItem = function () {
    $scope.items.push({
        title: 'Item ' + (++count),
        description: 'this is item number ' + count
    });
    $scope.$broadcast('rinfresca');
};

$scope.RemoveFirstItem = function () {
    $scope.items.shift();
    $scope.$broadcast('rinfresca');
};

$timeout(function () {
    $scope.items = [];
}, 1500)

$timeout(function () {
    $scope.items = [
        {
            title: 'Item 1',
            description: 'this is item number 1'
        },
        {
            title: 'Item 2',
            description: 'this is item number 2'
        },
        {
            title: 'Item 3',
            description: 'this is item number 3'
        }
    ];
    count = $scope.items.length;
}, 3000)

});

Rebind works well when putting refresh-onand bindonce on ul but when putting it on li it get following error when trying to remove an element:

TypeError: Object #<HTMLLIElement> has no method 'removeWatcher'
    at HTMLLIElement.ctrl.destroy (file:///Users/__myName__/github/local/bindonce/bindonce.js:176:12)
    at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:27:208
    at r (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:7:386)
    at Object.$c.c [as handle] (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:27:190)
    at cc (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:23:426)
    at Da (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:23:57)
    at r.remove (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:140:169)
    at Object.R.(anonymous function) [as remove] (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:141:414)
    at Object.leave (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:143:309)
    at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js:187:25 

I'd like to use refresh-on on list elements as seen in this example.

Regards,
Andre

Pasvaz added a commit that referenced this issue Mar 3, 2014
workaround issue #42
@Pasvaz
Copy link
Owner

Pasvaz commented Mar 3, 2014

@rajdeepg, please try it now

@rajdeepg
Copy link

rajdeepg commented Mar 3, 2014

Hi Pasvaz,

I just tried the latest and no longer getting the 'removeWatcher' error but also not seeing my ng-repeat update it self.

Here is the code I am using:

In My controller:
var index = $scope.storageItems.indexOf(items[0]);
$scope.storageItems[index].Name = items[0].Name;
$scope.$broadcast('refreshStorageItemList');
$scope.$apply();

I am updating the Name field in the item and hoping that it refreshes in my grid but it is not refreshing? Is there something I am doing wrong?

If you can share some sample code on how this should work that will be great?

Thanks,

Raj

@fidoboy
Copy link

fidoboy commented Mar 15, 2014

+1 :) Following this interesting feature...

@fidoboy
Copy link

fidoboy commented Aug 23, 2014

😄 i'm counting the days… bind once directive is really amazing and having the rebind feature… it could be perfect!! I hope that this could be available soon…

@thynctank
Copy link

U know another way to sort of do this is to wrap your bindonce directive inside something else that will change, like a top-level object with infrequent watch triggers. When it does change, all the DOM children are refreshed, even if they are then "static" again after that.

@maruf89
Copy link

maruf89 commented Sep 4, 2014

+1

@jtomaszewski
Copy link

In the meantime - check out this: https://github.com/pcw216/angular-watch-when . Seems like to be a complete solution for that problem! Haven't got time yet to check that in a real app, but looks interesting.

@johny
Copy link

johny commented Oct 6, 2014

+1

4 similar comments
@Irto
Copy link

Irto commented Oct 20, 2014

+1

@gindulis
Copy link

+1

@tateman66
Copy link

+1

@nadavsinai
Copy link

+1

@dipcore
Copy link

dipcore commented Nov 20, 2014

Take a look on this directive http://kent.doddsfamily.us/kcd-angular/#/kcd-recompile

@dipcore
Copy link

dipcore commented Nov 21, 2014

Here is my interpretation of 'kcd-recompile' directive (it uses angular 1.3 and broadcast events) http://jsbin.com/pigofojogo/2/edit?html,js,output

@mica16
Copy link

mica16 commented Dec 5, 2014

Is this feature currently under development? or is it advised to try alternative like kdc-recompile? I tried this latter with Angular 1.3, can't manage to make it work when wrapping an ng-repeat...
Thanks :)

@trythrow
Copy link

+1 What happen to this? any idea when it will be available? Did anyone succeed with a workaround?

@jtomaszewski
Copy link

Well, I'll just recommend you switching to angular 1.3 and to plugins mentioned above, they work perfectly.

@JoaquimMadeira
Copy link

Here is the 0.3.1 rebind-debug branch working for Angular 1.3, i have made a plunk.

http://plnkr.co/edit/wc4QNoB3nCpMdkWxrJlU?p=preview

@dnozay
Copy link

dnozay commented Mar 11, 2015

one workaround I was found somewhere: http://jsbin.com/tifukitesu/1/edit?html,js,output
a directive that listens to an event and replaces the element after reapplying the template; the event is triggered by a $broadcast which could place where you need it.

@floribon
Copy link

kdc-recompile does what it says, i.e. recompile the whole HTML, which is pretty heavy. Instead, it would be way more performant to only recompute the values, such as ng-bind or ng-class. The directive referenced by @dnozay unfortunately relies on the same principle: recompiling the HTML.

I'm playing with ngWatchWhen right now, which seems promising, however it is actually increasing my number of watchers rather than decreasing it, so not convinced by it yet.

I've never used bindonce refresh-on but the idea sounds terrific. This is a big deal for the angular world and I'm surprised there's no real solution to it.

@dnozay
Copy link

dnozay commented Mar 13, 2015

how about maintaining a structure similiar to / copy of scope.$$watchers which only gets iterated through on certain event?

@dipcore
Copy link

dipcore commented Mar 13, 2015

You can use directives which recompile HTML, but use it with small HTML blocks only, or you get page blinking.

Let's say you have a list, so you can make:

<div ng-repeat="item in ::items" refresh-on-event="refresh-row-{{::$index}}">something</div>

And if you want to refresh a single row, just use: $scope.$broadcast('refresh-row-' + row_index);
So basically split your page on small logical sections and refresh it per section.

@floribon
Copy link

I cannot. In my case I am manipulating a spreadsheet a-la-Excel. The table is fixed, but all values are dynamically recomputed as the user scrolls.

Only when he scrolls I want to recompute all these values. For all the other actions, their watchers are uselessly executed on every digest cycle. However when I actually scroll, all the cells are refreshed, which is way faster currently than recompiling the table.

I would bet that the compile phase is most of the time the bottleneck of a ng-repeat, unless some inner bindings are poorly designed.

@dipcore
Copy link

dipcore commented Mar 13, 2015

Maybe I didn't get you, but why you cannot recompile each cell? I mean to put in each cell of your table:

<span refresh-on-event="refresh-values">{{value[i][j]}}</span>

i,j - row and column numbers
so you will need generate a static table structure using ng-repeat, and after just update (fill) array values[][] and broadcast an event "refresh-values" to refresh all cells?

@floribon
Copy link

I'm pretty sure it would be about the about the same performances to recompile every cell or all of them at once. And this cannot be faster than just refreshing the text.

I have set up a JSfiddle here: http://jsfiddle.net/2tzb11um/1/

Set the one-time binding and change HERE-kcd-recompile to kcd-recompile. There's some bug that I don't understand so I couldn't benchmark. However if you put kcd-recompile in the <table> element you will clearly see that it's way slower than regular bindings.

@dipcore
Copy link

dipcore commented Mar 13, 2015

Look on this "zero-watchers" example I made:http://jsbin.com/fuzanotaho/1/edit?html,js,output

ps "watchers" directive is just helper, do not use it in benchmarks

@floribon
Copy link

Thanks. Now use 50 rows and 50 columns in your example and compare the performances with my jsfiddle using watchers: it is a lot slower. Compiling means rewritting the DOM, vs just changing an attribute/text, shouldn't be a surprise that it is slower.

@dipcore
Copy link

dipcore commented Mar 13, 2015

Right, it's slow. I believe you need to use a custom directive to render each cell on incoming event: http://jsbin.com/konuzudada/1/edit but now I'm not sure if it's faster

@dipcore
Copy link

dipcore commented Mar 13, 2015

Just tried 150x50 elements, seems a bit faster, so you can use a service as a data provider for the directive, and broadcast refresh event with some parameters to get data from the service and render it.

@floribon
Copy link

Well for this example that may be ok but, let's be honest, a full jQuery, no-angular alternative would almost feel simpler. I don't want to reinvent angular, i.e. ng-class, ng-bind-html, etc.

The ideal solution would be to keep the one time bindings of angular 1.3+ (::) with some ng-refresh-on that would retrigger the watchers on this event. I believe that's why the rebind-debug branch is trying to do, as well as ngWatchWhen, but the latter don't seem to address the issue. Now I need to try bindonce, which would be the least to do since we are discussing on this directive.

@JoaquimMadeira
Copy link

floribon, why don't you use the rebind branch of bindonce? I've put online a version working for Angular 1.3 and an adjustment in the bo-class and bo-if directives.

http://plnkr.co/edit/wc4QNoB3nCpMdkWxrJlU?p=preview

@kasperlewau
Copy link

shameless 'commercial' ahead


I've been tinkering on a continuation of fast-bind[1] and fast-bind[2] for 1.3 and I feel fairly happy with the current state of things.

Have a look at bind-notifier if you need to refresh your binds but not recompile the entire DOM node.

Usage:

  <div bind-notifier="{ keyOne:expr1, keyTwo:expr2 }">
    <span>{{:keyOne:spanOneExpression}}</span>
    <span>{{:keyTwo:spanTwoExpression}}</span>
    <span>{{:keyOne:keyTwo:spanThreeExpression}}</span>
  </div>

Values can also be rebound by broadcasting ($$rebind:: + key).

note: I haven't tested this thoroughly in conjunction with ng-bind; any and all help/contributions/opinions would be greatly appreciated : )

note2: ng-bind + bind-notifier appears to work just fine, apart from a minor discrepency that resides in the angular core (angular/angular.js#11716)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests