I wonder what's the best practice for a simple page containing a couple of radio buttons and checkboxes and other stuff controlling the visibility of page sections.
I started with something like
<div ng-if="radio1==3 || !button2" ....>
and immediately started to hate it. This page advocates against using functions in the view and actually I'll need the expression later again, so I should probably use something like
<div ng-if="visible1" ....>
and update visible1 accordingly. I tried
watch("radio1", function() {
$scope.visible1 = $scope.radio1 == 3 && !button2;
})
but it didn't work, probably because of the variables being scalars. Am I right?
I replaced $scope.radio by $scope.my_wonderful_model_aggregate.radio and it worked, but it was a bit too much to type.
Adding ng-click="updateVisible1();" to all the buttons works, too, but then I could use jQuery instead. I guess it's the most efficient solution, but speed isn't important here. And it's prone to forgetting and feels non-declarative and pretty redundant.
In my case, writing a single function which updates all the "visible*" variables based on other scope variables is easy and feels good. The function is idempotent, so it's OK when it gets called multiple times, but how can I ensure that it gets called only when needed?
Is there a better solution? I'm looking for best practices.
Related
I would like to know which is preferable in an angularJS app.
in the html, I can have...
a)
<span ng-class="{'car-icon': category === 'CAR' || category === 'SUV','bus-icon': category === 'BUS','bike-icon': category === 'BIKE'}"</span>
or...
b)
<span ng-class="categoryIcon"></span>
where categoryIcon is set in a controller function.
My preference is for b). a) puts logic in the UI which I do not like. I also find it much easier to test controller functions, so another reason for b)
However I'm being told that a) is the way to go because that is what angularJS templating is for. The example is applying CSS styling however I'm getting the same reasoning for setting href's, there is some simple logic required and I'm being asked to do it in the HTML templating vs a controller function.
It's a matter of preference, but I much prefer option b)
<span ng-class="categoryIcon"> with categoryIcon set in the controller.
If you think ahead, what if new categories are added, that html is going to get long, ugly, and error prone. Also, what if you want to use categoryIcon in multiple places in your html in the future. Simply referencing ng-class="categoryIcon" is much better design than copying option a) to multiple places.
I don't know why the other answers here have been down voted, since this is an opinion on best practices and there is no concrete right/wrong answers, but I would prefer solution A in one-off usages, and a solution hinting at B when it has to be repeated in the view.
I tend to think that the view should be able to handle the data model as it sees fit. It's not really business logic that we're talking here, and I don't want to have to jump into the controller code to see what class is being passed.
If you are making use of this conditional all over the place, though, it certainly would make sense to at least create some convenience methods in the controller: isCarSuv(), isBus(), etc. Then the ng-class conditional blocks would be cleaner.
<span ng-class="{'car-icon': isCarSuv(),'bus-icon': isBus(),'bike-icon': isBike()}">
I don't really think a pure B style implementation is the way I would do it.
Just my two cents. Others are sure to disagree.
I always use this variant since it is way cleaner, but i guess this is from developer to developer different
<span ng-class="categoryIcon">
By "is it okay", I'm looking less for opinion and more for solid reason(s) why this should/shouldn't be done.
Example:
$scope.myVar = null;
$scope.myFn = function() {
if ($scope.myVar) return $scope.otherFn();
}
It seems that the scope is for exposing data to the view, but now we're checking it as well.
Lastly, whatever the ruling on this, does using the Controller As syntax change things here? While using the scope this way seems inappropriate, it somehow seems okay when referencing this.
Again, to avoid this being labeled a 'conversation' question that isn't appropriate for Stack, I'm specifically looking for grounded reasoning why this practice is a bad idea.
Of course it's perfectly valid. The scope is there to hold data and functions used by the view, and this data is controlled by the controller. How could the controller control anything without accessing the data?
I'm using the same portion of html code in different parts of a page and
I'd like to avoid duplicating it.
The only things that change are a few ones that can be parametrised.
Reading here, I saw one option could be to use a directive:
http://fdietz.github.io/recipes-with-angular-js/directives/rendering-an-html-snippet-in-a-directive.html
Would this be the best approach?
The snippet of code I'd like to use is not very short.
I at first tried using ng-include with ng-init to set the parameters but
this wasn't a good idea since I was changes them in the scope and so they
where changing everywhere.
(I saw that this works inside of a ng-repeat because then you have a
different scope for it)
Thanks.
Is it bad practice, form-wise or performance-wise, to use directives for 'everything'? I'm beginning to see them as functions for rendering HTML, and like functions, they become useful when they are very discreet and combined.
However, I'm starting to get concerned that this might lead to poor performance or directive-overload. Here's an example of how I would like to use them - I am interested in feedback, especially about performance, etc.
<!-- This could even be wrapped in a my-editing-area-navbar directive -->
<my-navbard heading="{{pg.constants.navHeader}}">
<my-jump-to-page></my-jump-to-page>
<my-divider></my-divider>
<my-undo></my-undo>
<my-redo></my-redo>
<my-divider></my-divider>
<my-clear></my-clear>
<my-accept-and-continue acceptFunction="pg.acceptAndContinue()"></my-accept-and-continue>
<my-divider></my-divider>
<my-reset-utility></my-reset-utility>
</my-navbar>
<my-left-column heading="{{pg.constants.leftColHeading}}">
<my-preview-component></my-preview-component>
<my-debug-utility ng-if="{{pg.showDebug}}"></my-debug-utility>
</my-left-column>
<my-main-content heading="{{pg.constants.mainHeading}}" subheading="{{pg.constants.mainSubheading}}">
<my-message-viewer></my-message-viewer>
<my-content-mainpulator>
<my-content-frobber></my-content-frobber>
<my-dohicky></my-dohicky>
</my-content-mainpulator>
</my-main-content>
</my-page>
In my opinion, almost anything that's small, modular, and reusable should probably go in a directive. Keep it DRY. If it's really specific to the controller/view, there's really no need to abstract it away into its own thing.
I can't say much about performance, but none of the directive heavy apps I've worked on have felt sluggish or anything. And I'm willing to trade what performance loss there might be for the encapsulation and reuse that directives provide.
I see directives just the same way as any other specialized html element (input boxes, text areas, etc), and use them the same way (this makes more sense when you know that angular is heading towards using the Shadow DOM). Just make sure it's coded up well and you shouldn't see performance issues.
I'm trying to create a directive to allow the user to navigate the page with arrow keys by section. But I also want to be able to have those sections be scattered around the dom, and to have this not break when stuff gets added and removed. I can think of several ways to do this, but none of them are satisfactory:
Create a directive with a controller that lets other directives register themselves (and unregister on $destroy). But this will be out of order if I add something in the middle later. Also, I've tried writing it this way, and it seems like way more code than necessary.
Whenever the user hits an arrow key, make an empty array, and $broadcast an event, with a callback for directives to register themselves on that list. Then, once that list is full, advance or go backwards on it. They (should?) come back in the order they're in on the DOM, but I'm not sure since this way seems crazy and hackish.
Mark things that are 'tabbable' with css, and write this the simple way in jquery, something like this: On a new click event, var all = $('.tabbable'), and then do the obvious with that. But I really don't want to do it that way, because it's not 'the angular' way. Not out of some sense of purity, but because I'm building this as part of a larger library of widgets, and I want this functionality to be accessibly to them.
So, is there any way for me to get the scopes of all directives of a certain type, without resorting to weird hacks, or spreading the logic out all over the place?
This is a good question. +1
First, finding all directives or nodes by type goes against the Angular way. The View is the official record in AngularJS, so directives should say what they do and do what they say. Coding some process somewhere to scan for DOM nodes and act accordingly is problematic for several reasons, not the least of which are separation of concerns and testability.
I'm glad to see you're looking at other options, but I agree that the other options you provided are sub-optimal for the very reasons you mentioned. But I have one more. This is one that I've used for a different application, but that required knowledge of scattered DOM nodes.
First, we create a service to manage the state of this component. It's simple. Let's call it SectionsService. Next, we create a directive to register sections. Let's call that section for simplicity. The section directive registers the DOM node's ID (maybe created programmatically to ensure uniqueness) with the SectionsService during its linking phase. Since the DOM is processed (mostly) in order, the nodes added to the SectionsService will also be in order. So the DOM looks something like this (irrelevant stuff omitted):
<div section>...</div>
<!-- other stuff -->
<div section>...</div>
<!-- other stuff -->
<!-- etc. -->
(Though out of scope here, it would not be very difficult to program it in such a way that the order wouldn't matter, but it'd be based on specifics of your app that I don't know.)
Next, you create your triggers, like an arrow key handler. On these events, you simply tell the SectionService to go to the previous/next node in the list. AngularJS comes with a service called $anchorScroll that can be used to emulate the browser's hash-based positioning we're familiar with. You could obviously also use a jQuery plugin to animate the scrolling if you wanted to.
And that's it! A very simply directive, a fairly simple service, and whatever trigger(s) you need. All told, I'd guess less than 100 lines of code including tests. All components are decoupled and easily testable, but still really quite simple. The view remains The Truth. The Angular Way is preserved.
And there was much rejoicing.
I hope this sets you on the right direction, but of course feel free to ask a follow-up question. We can also talk code specifics too if you'd like; as I said, they wouldn't be very complicated.
AngularJS services are singletons and can be required via dependency injection. You could have your directives require a state manager service and call incrementers/decrementers.
Alternatively, a little easier but more brittle, you could keep an array in $rootScope. It's more idiomatic "angular" (but not by much) than a jquery selector global, but probably not the best route if you're building a widget library.