I am using Bootstrap UI in my angular application. I have a tooltip in the html page which works fine. I noticed that after the tooltip is displayed and I move my mouse out, the Ui-bootstrap-tpls.js fires a method called "hideTooltipBind" which in turn calls $apply and it triggers the filters in that scope to reload.
Lets say I have 10 filters in the scope which is filtering an array of 100 each. Everytime a tooltip is displayed, all my filters are forced to reload again. How can I avoid this?
I am using
//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js
jquery-2.0.3.js
ui-bootstrap-tpls-0.11.0.js
I have attached the screenshot of the Call Stack
You can utilise some form of one-time binding. There are multiple options out there:
bind-once by pasvaz
angular-once by tadeuszwojcik
watch-fighters by abourget
fast-bind by me (fork off of Karl Seamons work)
There are some differences to the four (unrelated to your question at hand however):
bind-once is the most popular one seeing the most active development. Requires two directives to do the job (bindonce and bo-*).
angular-once is the minimalist of the four (don't quote me on that).
watch-fighters doesn't handle promise based data.
fast-bind has a notifier system for semi-static bindings, using the event bus in Angular.
Assuming you'd start leveraging either one of them, your bindings could look something like this:
<div bindonce="someData">
<span bo-bind="someData.text | yourFilter"></span>
</div>
<span once-text="someData.text | yourFilter"></span>
<span set-text="someData.text | yourFilter"></span>
<span bind-once="someData.text | yourFilter"></span>
This way, your filters would not reevaluate on Angular calls to $digest. If you are filtering a collection in your view (<li ng-repeat="coll | filter"></div>), I'd suggest you move those filters to the controller to reduce the amount of calls to the filters themselves.
Related
New to angular, I have array of responses with below structure
{
type:typeid,
title:"99695 procedure id",
description :"procedure 99265 related to repair of system 56 with combination of 98745 procedure"
}
I need to identify numbers with specific pattern and make them clickable elements[not hyperlinks, better understanding purpose I made hyperlinks here], description field from API response should be formatted like below.
procedure 99265 related to repair of system 56 with combination of 98745 procedure
Using regex I had replaced matching patterns in text with <span style='cursor:pointer'><a>matchednumber</a></span> so subsequently when angular component rendering 'description' field all numbers are getting highlighted as clickable elements.
Problem
I need to register/trigger events like mouse over or click events. How can I bind events on above elements. I tried life cycle methods
ngAfterViewInit on component, ngAfterContentInit on directive
but they need some 'template' to be defined first to get element reference to bind events(correct me if I'm wrong here)
Going through lot of articles/examples but not able to get solve. Can you please suggest how can dynamically register events on 'plan html' content rendering from angular component.
I am writing a tree control using Angular2 and ng2-dragula (based on dragula). I am currently using something very similar to the example nested repeat example here. I have no problem loading in my list and getting drag and drop to work as expected. What I need to do is click a button, go back to the http service get fresh data, then update data on page. I know that I can use javascript to find the id and update that way but it doesn't seem like the correcy way to handle it.
Here is my component code - All this does is just over write the view data and completey undo any drags changed etc (im sure that is by design). I have tried a bunch of different different ways to use dragulaModel, but no matter what the whole component is re-rendered no matter what happens. I need to be able just to update the child text
I'm new to angular and want to make sure i follow the correct patterns
item.component.ts
loadItems(){
this._itemsService.getIems();
}
reloadIems(){
this._itemsService.getItems();
}
item.component.html
<div *ngIf="items">
<div class="holder">
<div *ngFor="let item of items | async">
<div (click)="checkCollapsed(item.text)" >
<div *ngIf='item.children' [dragula]='"first-bag"'>
<div *ngFor='let child of item.children' class="item">
<span class="handle">+</span><span id='{{child.id}}' [innerHtml]="child.text"> </span>
</div>
</div>
</div>
</div>
</div>
</div>
Edit
If I add a .subscribe to loadItems() then update this.items manually like this.items[0].children[0].text = "1000" in reloadItems() it works as i would like. Should I be manually updating the whole object this way? Seems like a hack
**Edit 2 **
I managed to get it to work by subscribing in the loadItems() then comparing the 2 objects in reloadItems, then making any necessary changes there. I think due to the subscribe it is autoupdating. Can anyone confirm if im doing it wrong/right (working plnkr here, leaving out the angular dependencies)
Apologies, the title is horrifically worded.
Background
I have an angular app which I am using as a static display. There is no user interaction so most is based on timeouts. Page is loaded, after X amount of time based on X elements, the page reloads.
Issue
I want to show/hide only portions at at time. For example, one hundred results and I show 10, wait ten seconds, then show the next ten. Again, no user input though.
I am having trouble sorting the elements and hiding/displaying. From my understanding, I believe it is best to use a filter for this similar to pagination with buttons but then how do I trigger that automatically?
I use a JavaScript function to handle fade outs and window reset but I am lost.
I need something like..
<div ng-repeat="item in filtered = items | filter:search | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit"> -->
Solution
The limitTo and startFrom directives worked perfectly, in my controller I setup a $interval using the angular service and iterated "pages"..
startFrom:currentFlowerPage*flowerPageSize | limitTo: flowerPageSize
If you upgrade to AngularJS v1.4.0 or higher, you can use the limitTo filter to get the behavior you want.
<li class="visible" ng-repeat="x in items | limitTo:entryLimit:entryLimit*currentPage">
you can manipulate entryLimit and currentPage with$timeout as you please to play around with the page number and page size.
AngularJS v1.4.0 limitTo filter doc
Let's suppose a list of 1000 items displayed with infinite scrolling.
Each item displays: a person's firstName, lastName, and mood. (to make it simple)
Initially, I didn't want to listen for updates.
So the great angular-bindonce directive or even better: angular 1.3 one-binding feature made the trick.
Now, I created a pull-to-refresh component, allowing to refresh the whole items.
However, as binding once, (and not reloading the page) my whole list didn't take the updates in account.
Using angular-bindonce, I have this currently:
<div bindonce ng-repeat="person in persons track by person.id">
<span bo-text="person.firstName"></span>
<span bo-text="person.lastName"></span>
<span bo-text="person.currentMood"></span>
</div>
The pull-to-refresh triggers this function:
$scope.refresh() {
Persons.getList(function(response)) {
$scope.persons = response.data; //data being an array
}
}
Question is:
Is there a way to refresh all the data ONLY when the pull-to-refresh is triggered?
In this case, I would be able to keep this one-binding that would greatly improve performance when dealing with huge lists of persons.
Until now, I'm forced to....use two-way binding, the natural way of Angular works.
More generally, how to deal with huge lists with infinite scrolling that needs to be updated only when some events are triggered?
Get angular-bind-notifier.
Use native bindings (with a somewhat modified syntax) and setup your markup like so:
<div ng-repeat="person in persons track by person.id" bind-notifier="{ eventKey:watchedExpression }">
<span>{{:eventKey:person.firstName}}</span>
<span>{{:eventKey:person.lastName}}</span>
<!-- or with ng-bind if you prefer that -->
<span ng-bind=":eventKey:person.currentMood"></span>
</div>
Now, whenever the value of watchedExpression changes - a $broadcast will be sent down through the childscope created by bind-notifier and tell every binding with the :key:expr syntax to re-evaluate.
If you need to, you can also send the $broadcast manually in the following format:
$scope.$broadcast('$$rebind::' + key) // where 'key' === 'eventKey' in the example above.
refresh-on directive could do the trick, found a reference HERE:
<div bindonce="persons" refresh-on="'refresh'" ng-repeat="person in persons track by person.id">
<span bo-text="person.firstName"></span>
<span bo-text="person.lastName"></span>
<span bo-text="person.currentMood"></span>
</div>
Instead of trying to work around not using two-way binding but still have all of its benefits there is more likely and easier solution. You say that there are 1,000 rows, are all 1,000 rows with the viewport / visible to the user at once?
I would assume not, so I would suggest using a buffered view for the list of items. Buffering the rows would mean that the rows that are not visible have no bindings but still take up space in the DOM so the scroll bar is always accurate.
The one major caveat of buffering is that all rows should be the same height, no variable height rows.
Here are some virtual scrolling / buffering directives to take a look at:
https://github.com/EnzeyNet/VirtualScroll
https://github.com/stackfull/angular-virtual-scroll
https://github.com/kamilkp/angular-vs-repeat
I am trying to understand some slightly odd behavior I am seeing in a page I am making using KnockoutJS. An observable array seems to get duplicate items every time I clear and reapply bindings. The quickest way to understand the problem is to look at this JSFiddle demo. Just click any edit button several times, and watch this list grow!
The heart of the code for this demo is in the following method:
var _bindItemDetail = function (jsonData) {
//clear existing bindings
ko.cleanNode($("#itemdetails").get(0));
// observables in selected item.
_viewModel.SelectedItem(ko.mapping.fromJS(jsonData));
// Apply Bindings
ko.applyBindings(_viewModel.SelectedItem, $("#itemdetails").get(0));
};
The essence of what I am trying to achieve is to create a list and details page in one. The list JSON is fetch on initial page load, and the detail JSON is fetched and bound to the "detail" html whenever an edit link is clicked.
In addition to solving the problem, I am trying to understand the behavior, and learn some lessons about how to clean up stale resources properly when using knockout.
Thanks for any help
The problem is that in your _bindItemDetail function, you are reapplying the bindings on your modified view where you already had replicated the elements.
var _bindItemDetail = function (jsonData) {
//clear existing bindings
ko.cleanNode($("#itemdetails").get(0));
// observables in selected item.
_viewModel.SelectedItem(ko.mapping.fromJS(jsonData));
// Apply Bindings
ko.applyBindings(_viewModel.SelectedItem, $("#itemdetails").get(0));
};
ko.cleanNode() merely removes bindings from the elements, it doesn't revert the view back to its initial state. In general, you should only ever call ko.applyBindings on a set of nodes once. Doing it more than once, is just asking for problems.
Frankly you're not really making good use of knockout. The majority of your code is using jquery to handle all the low-level details. The point of using knockout is to not have to worry about those lower level details.
I've adjusted your fiddle a bit to make better use of knockout with less emphasis on using jquery.
In the view:
Used the click binding to handle your Edit click events.
Used the with binding to conditionally show the editor fields. The stopBindings handler is not needed.
In the view model:
Added the click handler editClicked to the view model.
Removed jquery event bindings.
Removed the ko.cleanNode/ko.applyBindings combo you had when binding items. You shouldn't do that and you just don't need it, knockout will handle all that for you.
Updated fiddle