How to speed up AngularJS rendering with big scopes? - javascript

I'm developping an angular app right now for my company, but I reached a point where the app became extremely slow so I tried tunning it by using onetimebind everywhere I can, track by ...but it's faster to load at first but still laggy, it is composed of a pretty much huge nested objects, I've counted the total number of objects, it starts at 680 and can go up to +6000 for normal use of the app, oh yeah I should precise that the app is generating a form and pretty much +90% of the objects in the scope belongs to an input and are updated each time the client click(radio) keyup/change(text).
It also have like 5/6 arrays composed of objects and the array gets bigger/smaller accodring to the clients choice, and that's where it gets laggy, each time I add an object to the array, it takes like a second to render it, so I tried using nested controllers thinking that if the child of an object is updated Angular will render only this child and not all the others, but somehow the app got even slower and laggier :s (it's a bit faster when I use ng-show instead of ng-if but the memory used jumps from ~50Mb to ~150Mb)
I should also precise that the form is in a wizard style, and not all the inputs are displayed at once, the number of inputs that are displayed are between 10%-20% of the total inputs
Has anyone encountred this problem before? does anyone know how to deal with big scopes?

Sad to say, but that's intrinsic of the view rendering in angular.
An update in the model triggers a potential redraw of the entire view. No matter if you have elements hidden or not. The two way data binding can really kill performances. You can consider evaluate if you need to render the view only once, in that case there are optimizations, but I'm assuming that your form change dynamically, therefore a 2 way data binding is necessary.
You can try to work around this limitation but encapsulate sub part of the entire MVC. In this way a contained controllers only update the specific view associated to that scope.
You may want to consider using react (that has as first goal to address exactly your use case)
Have a look at this blog post for a comparison of the rendering pipeline between angular and react Js.
http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/

Related

Creating a React component and states from regex string matches

I'm moving the frontend of a project I'd written primarily with php and jquery over to react as a learning exercise. So far it just seems to be making everything a bit more convoluted but I'm hoping I'll see the benefits soon. Right now it's just good to be familiarising myself with something far more current than what I was using. But I digress...
There's one particular situation I'm translating at the moment that feels simple to do in jquery but feels like I'm having to choose between a bunch of cumbersome or not-recommended solutions with react, such as dangerouslysetinnerhtml
A user can input their own list, as raw text, which the app parses using regex to highlight quantities and items, illustrated above as highlighted by rose and green. New lines are parsed as list elements, highlighted in peach. The parsed quantities can be scaled using a range input, shown above the list. The example UI shows an example after parsing a raw string of text.
Previously - jquery
When I was using jquery I replaced regex matches with <span> elements containing data attributes to create new elements I could target. The quantity scaling function in the app could now reference the original quantity of a list item at any time using a data attribute.
e.g. I need 10 cars is parsed to become <li>I need <span class='highlight quantity' data-count='10'>10</span> <span class='highlight item' data-item='cars'>cars</span></li>
With jquery, whenever the input range is dragged, I just target all elements on the page that have the quantity class, reference their data-count and replace the span value with the current range value multiplied by that data-count. This worked well.
Now - React
I'm getting my head around react components. Referencing the illustration, above, I have a component for dialogs (as there will be other dialogs too), a component for the range input of dialogs, a component for the list, and a component for list items.
As before, when I drag the input range, I want the quantities on the page to change to reflect the new scale set by a user. When picking up react I became familiar with component states and props and thought these would solve my problem beautifully. If I had a scale state I could pass it through the components and anything in the hierarchy could reference it. As I understood it, the hierarchy was important and it was important to hold the state at the top so that lower-level components could be 'fed' it, if that's a good way of putting it. This appeared to be best practice.
But I'm having a tough time getting it to jump through hoops. Maybe I've misunderstood?
Two uncertainties
1
If I've understood the hierarchy of states correctly, I'm setting the state initially inside the App() function, like so:
function App() {
const [scaleValue, setScaleValue] = useState();
const handleScaleChange = (childData) => {
setScaleValue(childData);
}
return (
<React.Fragment>
/~~~/
<UserList rawstring={userText} scale={scaleValue} />
/~~~/
<Dialogs scale={scaleValue} setscale={setScaleValue} parentCallback={handleScaleChange}/>
</React.Fragment>
);
}
But I must use a callback function to change the state as the dialog range I'm using is nested a few levels down in components - passing it back to the state in the parent App() function and then back down to all the nested components using props. It felt much easier to just target elements with a matching class name in jquery and I'm not yet seeing the advantage of having components using states. I thought being able to target list item components with quantity states would have advantages in React that were more apparent.
2
Perhaps the main thing that has me stumped on how to proceed. In jquery, when I was parsing a raw string provided by the user, into html that I could target with specific classes, I just used some regex functions to return html that could be inserted into the dom. Example of the html is per the earlier example:
I need 10 cars is parsed to become <li>I need <span class='highlight quantity' data-count='10'>10</span> <span class='highlight item' data-item='cars'>cars</span></li>
With react, I parse the same string to the same html fine, but I've no idea how to handle it from there, logically? If I return it and insert it, it is treated as raw text and renders the html tags on page. I can use dangerouslysetinnerhtml and it shows fine, but I've loud and clear got the message that this is heavily discouraged. I want to use best practices.
In fact, I likely don't want to be returning quantities wrapped in targetable classes. I gather that I want to be able to assign states to a list item component. I have absolutely no idea what's the best approach (logically) to do this with react. I could return an array from the regex parsing but this feels needlessly complex compared to replacing a matched sub string with a wrapped version of itself.
I feel at this point I may be approaching the translation wrong (or my understanding of using the component system and states) as this feels so much more convoluted than the jquery approach I was using.
not-recommended solutions with react, such as dangerouslysetinnerhtml
That's not what dangerouslySetInnerHTML is about. It's about the fact that an unsanitized string containing HTML/JS could pose a security threat (XSS mostly). In other words, in React you have to explicitly use dangerouslySetInnerHTML if you need to be unsafe, but it's your responsibility to protect yourself, hence the name. Doesn't mean it's not recommended, just tells you to be extra careful.
the hierarchy was important and it was important to hold the state at
the top so that lower-level components could be 'fed' it, if that's a
good way of putting it.
Indeed, at first it might look overly complicated for no good reason. Today.
A few months from now if you'll have to go through that code again, or worse, if someone else will have to, this structure enforcing rules and structure makes it far more easier to make changes. You can simply right click on the "callback" function and "go to definition" in your IDE of choice and see where it's defined, instead of hoping it's not hidden in some JS file or relying on search when the function is named "handleClick" in a project with one thousand click handlers.
It felt much easier to just target elements with a matching class name in jquery
Of course it was, anyone with minimal experience in a large scale project knows this. There always are trade-offs. You need to judge if it makes sense to loose simplicity for the sake of maintainability based on the scale of the project.
With react, I parse the same string to the same html fine, but I've no idea how to handle it from there, logically?
You are mixing parsing with generating HTML. You were parsing the string and generating HTML. With React you have to parse the string and generate JSX. If your code was separating these two concerns then it would have been a lot clearer what the difference is.
Again, if you are the only one working on this and know you'll never touch it again, why bother separating these two parts? On the other hand if you do expect to touch that piece of code later or know there will be other people working on it then the smart option would be to split it so that you can properly unit test it.
I can use dangerouslysetinnerhtml and it shows fine, but I've loud and clear got the message that this is heavily discouraged. I want to use best practices.
No, that's not the point. If you use dangerouslySetInnerHTML then how are you going to interact with those highlighted numbers? document.querySelectorAll? That is indeed against the purpose of React. I think my answers above make it clear what a more suitable approach would be.
feels needlessly complex compared to replacing a matched sub string with a wrapped version of itself.
And it is because you don't understand why someone would use React. You think React is a replacement for jQuery and it is not. It is a way of writing JavaScript applications so that you don't end up with a pile of code no one can follow. Which is a problem that only exists if you eventually end up with a lot of code. Your application has what, 100 lines of JavaScript? Why are you even using jQuery?
My advice is:
Understand that people that make deliberate choices don't choose React because it's fashionable but because it solves some problems.
Figure out what those problems are. I've listed a big one, which is maintainability and that structure and separation of concerns are part of the solution.
Decide if you care about any of the problems React helps solve in any of the projects you work (or will work) on.
If you do care about these problems then put yourself in the appropriate state of mind. React is not meant to replace jQuery, find a project that would benefit from using React.
Understand that React is not the one and only tool you'll ever need.
Some more advice:
definitely have a look at useReducer and then (if you need the extra juicy bits) Redux for state management. All beginner tutorials try to stay away from them but IMO React is an empty shell without proper state management.
React is a clean way of turning state into HTML/CSS. The philosophy is that any action (e.g. a click on a "add a new row" button) should produce a state mutation that translates into manipulating some data, NOT the DOM. Your JSX is one representation of that data! And React does a great (fast) job at manipulating the DOM to reflect the new JSX representation. So start thinking your application in terms of input-output, data structures and actions/reducers that manipulate these data structures.
force yourself to unit test (or, better, TDD) your actions and reducers. Completely decoupled from JSX and DOM! Your entire application logic should be testable in Node CLI. This to me (that you can completely separate logic from presentation) is a major win (and is the base of React Native too).
have a look at https://storybook.js.org/ - this one is another one of those fantastic tools (not only for React) that will completely change your development experience.
be wary of using "component libraries". Most "complicated" things turn into child's play when you really understand how to design them correctly.
be prepared to spend 3x the time you spent before on implementing really simple stuff for a while, until you familiarize yourself with the core concepts and philosophy.

Limitation of Angular and browsers with regards to data loaded

I would like to know what is the maximum data that angular framework can handle. Say, I am displaying a chart using angular and some charting framework like chartjs. I'd like to know up to how many data can the browser display properly, with slowness, or up to when it crashes.
Your question has no simple answer, but I will try to flatten it and give a simple answer, or at least simple things to consider...
Angular (at runtime), like many other frameworks is simply JavaScript,
So let us reduce the question to "Limitation of JavaScript and browsers with regards to data loaded",
JavaScript has no upper limit of memory or storage it can handle,
I've seen JavaScript applications that require more than 15GB of RAM,
and they performed well too.
So assume data size itself is not an issue (unless your application is poorly implemented, leaking memory or just not very efficient, of course).
The main challenge as I see it, is displaying and manipulating the information
without causing unnecessary delay or unresponsiveness.
Displaying the information - let's say you have a list (or a table) containing 1,000,000 possible gifts which you then want to display for the user to select.
Adding the list items to the document one by one is tempting, but will require the browser to repaint after each addition (causing a delay or full unresponsiveness until finished), another way is adding the elements to some DOM element (denoted by N) still being kept in memory, then adding all elements corresponding the list items to the element N (still, just an in memory operation), finally adding N to the document containing the entire list - the will be a much better solution for displaying the large amount of data.
Manipulating the information - displaying is indeed not enough. you would like to move, drag, sort and filter the data being displayed. And as mentioned before, it is a bad idea removing many elements directly from DOM. You should instead remove container from the document's DOM to memory, manipulate the data in it, and then add the container right back to the document. Angular does this kind of magic for you.
(Toggling the 'display:none\block' css attribute of many elements has a similar blocking effect as I recall).
A good practice is implementing an application/page showing only the amount of data that can be processed by a human at a single glance. The rest of it should be considered in the application data-layer, in memory, and should be loaded to display given the appropriate need or request.
To conclude, you can deal with huge amounts of data as long as you provide a mechanism that efficiently filter the displayed information.
I hope it helps...
for further reading:
Slow and fast ways of adding elements to DOM
A question emphasizes the lack of memory limit used by JS
CSS display attribute performance
A good discussion about the reasons for slow DOM
About using HTML5 correctly - old but still true
Once the DOM creation procedure is understood - it much easier to display data without affecting performance / user experience

ng-model works with ng-repeat but not with collection-repeat [duplicate]

i got a little bit confusion about ng-repeat and collection-repeat
ng-repeat
<ion-item ng-repeat="item in items" >
...
</ion-item>
collection-repeat
<ion-item collection-repeat="item in items">
...
</ion-item>
the above both lines are doing well and same.
Please let me know if any difference there. also i have 1000 items, so which one is better for this scenario? and how's the rendering speed and what about the performance status ?
collection-repeat is not part of angularjs. As far as I can see it's coming from Ionic Framework. As documentation says collection-repeat was created to be used instead of ng-repeat as it might work better with large number of items.
collection-repeat allows an app to show huge lists of items much
more performantly than ng-repeat
Collection repeat is Ionic's buttery-smooth solution for scrolling huge lists. collection-repeat can be used for large list of items and it has high performance when process huge data set.It renders into the DOM only as many items as are currently visible.This means that on a phone screen that can fit eight items, only the eight items matching the current scroll position will be rendered.
ngRepeat part of the angular Core module. The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item, and $index is set to the item index or key.
Based on the use case you can use any of the options. However, if you use ionic framework for mobile then I would suggest to go for Collection repeat.
Further, If you need to use ng-repeat, it is recommend to process data in the server side and request part by part. This way you can expect high performance and less processing in the client side. Another option is store the large data set in the client browser and then process data using that data set.
In case of 1000 elements I would strongly recommend the collection-repeat. It's more flexible, and it renders as many items from collection as many can fit the screen. the rest is loaded dynamically, you can even use 200k collection and it will go smooth as well. ng-repeat renders the collection items until it's done, so it will give you much worse performance.
So collection-repeat it is my friend.
Good luck with your development!
Note: collection-repeat is not a part of angular core, look here for more : http://ionicframework.com/docs/api/directive/collectionRepeat/
From what I'm understand is ng-repeat is suitable to use for small numbers of item and collection-repeat is very suitable to use for huge numbers of item. Its actually about the performance when it comes to view/render in a device. For more understanding you can see this video, it really help me to understand both of it.
In Simple Word -
Ionic has a collection-repeat directive that you can use, instead of ng-repeat, when you need to display very large lists.
How it works?(Deep)
The module is divided into two components, $repeatFactory (a service where helper functions are defined) and collectRepeat (a directive where most of the logic lives).
The most important takeaway from the factory is the repeatManager method. It is the contructor that is instantiated at the beginning of the link function of the directive and is responsible for maintaining the state of the scrolling view.
On creating a new instance of repeatManager, it's also necessary to generate a new object (or map) to provide as reference for all the items that have already been transcluded, rendered, and whose scope has been updated. The enclosing parent element must also be registered so that we can later change it's height to the height of all the items in the collection.
Next, a watch is set on the collection with an anonymous function set to be invoked whenever the collection changes. This is where the bulk of the logic resides. It's split into three steps.
Transclude and render the first element of the collection so that the height and other properties of each individual item may be registered, the height of the parent div adjusted, and the size of the viewport calculated
With the size of the viewport determined, loop over and render the n number of items that will fit inside the viewport (plus a few extra for smoothness).
Set a 'scroll' event listener on .scroll-content to return the scrollHeight of the first element of the collection and render only the necessary elements.
There are a few additional tidbits (also known as comments) available in the code below. It should help alleviate some confusion if you have trouble following the above synopsis. The module is also available on github.
GitHub
Differences - Working Example
Code Here

lots of backbone views - performance issues?

tl;dr: I wonder if having lots (100+ for the moment, potentially up to 1000/2000 or more) of backbone views (as a cell of a table) is too heavy or not
The project I'm working on revolves around a planning view. There one row per user that covers 6 hours of a day, each hour splitted in 4 slots of 15mn. This planning is used to add "reservations" when clicking on a slot, and should handle hovering of the correct slots, and also handle when it is NOT possible to make a reservation - ie. prevent user click on an "unavailable" slot.
There is many reasons why a slot can't be clicked on: the user is not available at this time, or the user is in a reservation; or the app needs to "force" a delay slot between two reservations. Reservations (a div) are rendered in a slot (a cell of a table), and by toying with dimensions, hovers the right number of slots.
All this screen is handled with backbone. So For each slot I'm hovering on, I need to check wether I can do a reservation here or not. As of this moment, I use this by toying with the data attributes on the slots : when a reservation object is added, the slots covered are "enhanced with (among others) the reservation object (the backbone view object).
But in some cases I don't quite have a grasp on now, it mixes up, and when the reservation view is removed, the slots are not "cleaned up" : the previous class is not reset correctly. It is probably something I've done wrong or badly, but this is only going to get heavier; I think I should use another class of Backbone views here, but I'm afraid the number of slots and thereof of views objects will be high and cause performance issue. I don't know mush about js perf so I'd like to have some feedback before jumping on that train. Any other advice on how to do this would be quite welcomed too.
Thanks for your time. If this is not clear enough, tell me, I'll try and rephrase it.
We have a pretty complex backbone.js app with potentially thousands of views at a given time. Our bottlenecks are mostly in memory leaks from views that have not been properly removed or eventdriven rendering that re-renders views needlessly. That said, the actual number of views doesn't seem to affect much. Backbone views are pretty lightweight, so as long as you don't have too much events bound to them, it's not that big of a problem.
You might run into performance issues, but the views probably isn't the problem.
What you might want to do though, if you do use thousands of views is to stash up the initial rendering into one big ´.html()´-call. You can do this by giving each view-element an id based on the view's cid and then you concatenate the view's html strings and afterwards you use setElement on each view to find its element again.
Maybe this helps a litte bit :
https://stackoverflow.com/a/7150279/1067061
btw.: i like your avatar ;)
backbone handles everything is the least performant way for the browser. If it's not fast enough you need to stop creating and destroying templates. Caching a template is almost no help at all.
Instead of calling the render function against, save out a variable to the jquery selector and when the model changes use the cached selector to update the DOM. This will give you the largest jump in performance off the bat.
$title = null,
init = function(){ $title = this.$el.find('h1'); },
onTitleChange = function(event){ $title.html(event.value) }
I'm working on a backbone view that is performant. It can render a collection with 1,000,000 models at over 120FPS. The code is up on Github. https://github.com/puppybits/BackboneJS-PerfView There are a lot of other optimizations that can be done. If you check out the PrefView it has comments on most of the optimizations.

Swap out views with Backbone?

I've looked around but have yet to find a great solution the the following problem:
I have a Backbone View tied to an el on the page that is a container element for what I'll call a "sidebar" in the traditional sense (for explanation's sake). This sidebar element's inner-html needs to change completely depending on the route. However, the position on the page never changes, and will always fill this container.
Now, for an initial prototype, I had a 1:1 relationship between this container and the view placed in it (I only coded up one route). Now however, said view needs to change based on the route as I've mentioned.
Being that these different views have entirely different html markup, response to events, etc... I had thought that I'd make sense to have a higher level view that'd swap in sub-views. Of course, one solution that would work would be to handle everything in the same view, but the necessary logic would be cumbersome (and pretty damn unwieldy).
Currently, here's what I'm thinking (and have partially implemented):
Have a top-level view for this page element.
Depending on the route, swap in the necessary sub-view.
These subviews are rendered with dust.js, where the .html template for just this component on the page is lazy-loaded via AJAX, compiled, and cached. Subsequent renders just consist of calling the cached "compiled" function with a new context.
Additionally, I was going to initialize and cache each of the subview Views within the top-level View, such that I'm only instantiating, setting up event handlers, etc.. once.
Then, depending on the route, look up the associated subview View (cached), and swap it in in-place of the current view.
Now, as I've mentioned, I have this mostly coded up and... semi-working. However, what I'm struggling with is how to have each of these subviews exist independently and handle the swapping, but keep all of the event handlers and current state to continuing to live whether the component is currently displayed or not.
Basically:
How to avoid completely destroying / re-instantiating subviews each time they're required. Maybe this operation isn't as expensive as I'm thinking it is and I should simply do the latter.
Being that the top-level View (the "manager", if you will) is tied to the container $el, how to swap in the subviews.
I'm sure there's a simple, elegant solution. I just haven't found it yet, unfortunately.
As far as point 1 is concerned I don't think it is too expensive to let the view be created each time.
For point 2 - I would recommend using Backbone.Marionette https://github.com/derickbailey/backbone.marionette. It has the concept of a Layout which lets you define different regions of your app and render/manage them individually.
I would recommend Backbone.Marionette not just for point 2 but for the way in which it allows you to manage interaction is in my opinion much better than standard Backbone.

Categories

Resources