Angular external template directive in ng-repeat - angularjs

I have this here block of html, say: <div>{{myVar}}</div>. This block of code is on a lot of pages in a lot of places. The designer from time to time decides to randomly change the way the block looks and what it contains so i have to every page in every place to redo the blocks of code -> BAD!
The solution would be put that block of code in a directive and modify it in one place only. Now said block of code is quite complex markup wise so i'm inclined to put it in a separate page and do a templateURLreference (for ease of editing), but the drawback is that in every place, the directive would sit in an ng-repeat and i'm not sure if angular does a HTTP request for that separate page, where the directive would sit, every time it runs into that directive.
My questions are therefore these:
1) Does Angular do a http request every time it runs into that directive? - is there an option for this not to happen, if it does?
2) What other drawbacks could i run into, if i place it in a separate page?
3) What's the rule of thumb for placing the directive in a separate file.
4) When do you know if you're "overdirectivising" your markup?

Angular fetches the template once and keeps in the cache and serves it from there.
So there is only one http call.
So if you wan't to avoid it you will have to use a string.
And you can do something better here.
Spicify the dummy URL in the directive. And create the template string somewhere.
And in module.run inject $templatecache service and call:
$templatecache.put('url',templateString);
Now your directive will behave its reading from the URL. but you are actually specifying the string at module level.
"This way you can specify all your template string at one place. No need to keep them with directive."

No. It gets the template once, put it in a cache, and gets it from the cache for all subsequent occurrences of the directive. Of course, if the app is reloaded (i.e. the page is refreshed, the template will be reloaded. But even then, unless you explicitely prohibit it, the browser should have it in its own cache.
I don't see any.
I usually do it when the template is too large to be comfortably put inside a JS string literal. Note that you could also put it in a separate file, but embed it in the JavaScript code at build time, if you really don't want an additional HTTP request to load its template.

Related

Delay generating directive until after page is ready and responsive

I'm working on a single-page app where some parts are really slow. They're slow because I'm displaying 400 complex things in a repeater for the user to scroll through. Each thing is generated by a fairly complex directive that does a ton of data binding and expression evaluation, adds one or two click handlers, and displays a couple of images. In some cases, I also need a grayscale CSS filter on those images, but that really seems way too slow.
I have of course already turned most of my data binding into one-time data binding, but simply generating the 400 things for the first time is still slow. It's initially hidden through ng-if, which speeds it up when I'm not showing it, but once I do need to show it, everything waits 10 seconds for that to happen. I would like to load it in advance, but using ng-show instead of ng-if means the loading of the entire app has to wait for this.
What I would like, is to load the rest of the app, and then, while we wait for user input, start creating these 400 things so they're ready once I need to show them. I don't want the user to notice how slow this is.
Problem is, I have no idea how to do this. Any ideas?
Edit: Having thought about this (and discussed this with my wife), I'm seeing two options (that I conceptually understand, at least):
The easy, quick, boring and cowardly solution is to simply not show the 400 things at the same time, but cut them in pieces and show 40 at a time. That should make it a lot quicker, but also less nice, as the user needs to click around to access all the data.
The interesting solution is to write a new ng-repeat that generates the 400 transcluded copies of the template each in their own asynchronous event, so they don't block user interaction. I really like this idea, but there's one big downside: it's an ambitious idea with deep Angular magic, and I don't have much time available.
OK, not seeing your code structure, through Q&A, I'm trying to get clarification. If I understand you correctly, I believe the solution is to process your images asynchronously and remove reliance of creating/processing them on the fly when the view is visible (i.e. via clicking on a button/tab to 'show' the array 'view' and thus triggering the ng-repeat). BTW, this solution assumes the delays are because the images are being processed rather than because they are being shown.
METHOD 1 (less preferred)
To do this, it's best to create an 'ImageDataService' service, where it get's kicked off at application start, and proceeds with building this array and taking whatever time it needs asynchronously without caring what view is showing or not. This service will be injected into the proper view or directive controller--perhaps where the current ng-repeat scope is.
Meanwhile, you also need to change the directives inside your ng-repeat to get the pre-built data from the array provided by ImageDataService rather than actually doing the calculation at view time. If the data for that directive is not ready, the directive will show something like 'loading...' otherwise, the directive will simply show the prebuilt images. This way, it doesn't matter when ng-repeat is triggered (i.e. its view is showing), because it will just show what are processed and ready at that point and the display the rest as 'loading...'.
METHOD 2 (I prefer this)
Alternatively, and maybe better, you can forego creating a pre-processed array in a service as in METHOD 1 and instead modify your directives to process their data asynchronously by invoking a service method that returns an asynchronous promise like this:
(code inside a controller)
var promise = ImageDataService.processImage(directiveData);
promise.then(function() {...set the directive image attributes..})
Needless to say, the ImageDataService.processImage() method needs to return a promise when it is done processing the data. The directive, as usual, will show 'loading...' until then. Also, although ImageDataService no longer needs to pre-populate its array mentioned in METHOD 1, it might be a good idea to save the processed images to a similar array anyway to serve as cache and not reprocess them needlessly. NOTE, in this case you don't need to have processImage() inside a service--but it is really good 'separation of concerns' practice to reserve the work of asynchronous data processing (ajax, etc) within a service which can be injected app-wide rather than within a controller.
Either of these 2 general tacks should work.

Angular translate attribute vs filter

Which of these two is better?
<span translate="key">Key</span>
or
<span>{{'key' | translate}}</span>
They are both good and work fine but in first case I must fill the content of the element.
Using the attribute is better performance-wise, especially if you intend to use translations on elements that will be inside ngRepeats. This is because the way filter work internally.
Every time there is a digest cycle, angularjs reloads all the expressions containing filters. This is because angular can't possibly know if a filter has changed or not. What this means is that, even if the key of the translation has not changed, but some other value on the scope has, angular will look through every translation and translate it again, just to come to the conclusion that they all remain the same.
Attributes are smarter, because the developer of the directive has explicit control over when it should re-render and what watcher should be created.
Edit: And as far as I know, there is no need to fill the content in the first use-case. You can just leave it empty.

How to design the connection between raw backend data and template with AngularJS?

Angular novice here.
I have a REST API of my own creation that supplies data (data that has been sourced from a 3rd party and undergone very little transformation). The "heaviest" endpoint returns over 100 key-value pairs in JSON.
I also have a template that I made by hand that has places for all of this data to go. The thing is, a lot of the data my API returns must undergo light transformations before being stuck in the template.
For example:
a string must have underscores replaced with spaces
an ID must be resolved to both a string and an image (both retrievable from REST API) that get placed in different parts of the template.
This has to be done multiple times per page; the endpoint returns an array, with each entry containing the 100+ key-val pairs.
But I am unsure of how to handle the transforms that need to occur between API and page display. Should I write a controller that grabs this data from an Angular service (that in turn talks to my REST api), and then generates strings (to be placed in the template) that get placed in $scope properties? And then write a directive for each $scope element that I want to be placed in the page, and fill in my template with those directive?
It seems strange to write things this way since I'm not sure how that's different from just using curly braces to directly reference the $scope properties. Should I move more of my work into the directive code and trim down the controller, or what?
In short, I am uncertain of how to apportion the workloads and data among the tools Angular provides. I realize there are a lot of options here, but nothing I've read so far jumps out at me for how to organize this properly. If it seems like there's something I'm missing, feel free to just tell me to hit the books again, or read more on a certain subject of the framework.

Putting presentation data in angular controller?

Got a webapp I'm building in Angular.
This app walks a user to authorizing accounts, presenting specific instructions based on the users choices.
I've implemented this as HTML that is shown or hidden based on values in the model, so for 3 different choices, I have 3 different sets of HTML sections that are similar but with different texts.
In the spirit of DRY, I should instead have one set of HTML sections, and instead switch the text based on the values of the model. This means putting text data inside the model, including small snippets of markup, like anchor and strong tags.
Does putting presentation data into the controller violate the principals of Angular?
There are quite a number of options to avoid repeating code depending on what you are looking to do. The following ideas are things I would consider and use when they make sense (I placed these from simple to complex, so you probably can skip the first few):
ng-bind -- Put it on a span/div. Simple & works to bind the model to the display
ng-switch, ng-if, ng-hide, ng-show -- Work to conditionally show an element
custom directive -- use this when you want to alter the behavior of an element or if you want to alter the dom based on a template. If you use "ng-transclude" the contents of the element you template will be included in the result. This can be very elegant but it works best when you have a single format. I can provide examples but angular's documentation also has excellent examples.
service -- I generally use this just to provide data only. This could be via a restful api and $resource or via $http calls. Either way, I wouldn't recommend doing much more than load/save data here.
$scope method -- In other words:
$scope.myMethod = function(x,y,z) { /* code making decisions based on the model */ }
Then you can call this method from one of the previous either via a prebuilt directive (ng-show, etc) or via a custom directive that manipulates the dom for how you expect it to be.
ng-bind-html -- Last option I know to suggest is to use this directive combined with the $sce service to bind whatever you want to the DOM. If you are binding something with angular code in it - make sure to use the $compile service as well. I generally don't favor this approach except as a last resort because it makes it harder to find where elements in the DOM are coming from and it can make debugging + testing a real pain. That said, these tools wouldn't exist if people didn't need them.
I'm sure that this isn't complete and maybe others have suggestions but that is where I would start. Best of luck!
I would put the text data in a separate angular service. This article gives an example: http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Then if you decided at some point to move it to some other storage, your service would still be the single access point for the rest of the app.

Django REST framework + angular, autmatically appending backed errors to the DOM

I am working on a new project, which includes several standard forms(Login, Registration, etc).
I have a basic client side validation with ng-required, type, and etc.
The problem is that I might get other kinds of errors from the Django REST backend, such as length, unique constraint and others, and those rules might change quite frequently.
The django REST server returns the errors in a JSON string, in shich the key is the field, and the value is the description of the error.
Is there anyway to write something in angular that will automatically append an error next to the invalid element, as a modular unit, that can be reused, so I won't have to add an error container and ng-bind for it per each field in my form?.
Thanks for any assistance.
When you want to write reusable code like this, your best choice is to use directives. You can create a directive named <email></email> and then inside the template, populate it with the input element and display the {{error}} next to it. There are several ways of getting the error into the directive template, but I'd suggest isolate scope and pass the data into the directive. This helps make directives more reusable.
If you've done things correctly then your backend 'Django REST' shouldn't have anything to do with this front-end functionality for the directive. All you need to do is change the data inside the controller and it will automatically change the data in the directive. So it gives a good level of abstraction as well.

Resources