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">
Related
I have two custom date picking directives
One for a single day
One for selecting a range
About 50% of the code in each is duplicate code(mostly populating the month with days and selecting a single day), if not very similar. I am trying to stay as DRY as I can, but having issues figuring out how to approach this.
Option 1
My initial thought is to have a calendar service that can hold some of the shared functions that can then be used in either directive? Is this considered bad convention?
Option 2
Otherwise I thought of having a calendar E directive with all the html/initial values such as the days in the given month and the ability to change months. Then create two A directives, on for each that carries on the functionality?
Options 3
?????
I feel I can hash out option 1 quickly, and it will work. Option 2 sounds like a more elegant solution to me, I'd rather write some sexy elegant code(which seems kind of stupid.) - But in the end I would prefer to take the best approach.
Option 3, I'm all ears if you have other ideas
I think the best approach is to use Services for the shared logic.
And for the directive behavior you should use nested directives.
Best approach here would be to isolate that 50% code (or as much as you can) and put it into a service or factory. Personally I prefer Services.
app.service("ServiceName", function(injectors, ...)
{
// code
})
Then setup your directives to use controllers (not link), and inject that service into the controllers:
...
controller: function($scope, ServiceName, $otherInjectors ...)
{
// call your ServiceName.methods() as needed
}
...
Another option is to use angular.extend on an existing controller, but I think that's likely overkill for what you're looking to do... which is basically simplify your 2 nearly identical directives into basic logic (service) and then UI-specific stuff in the individual controllers.
Hope that helps!
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 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.
I am new to Backbone, and I have found myself writing views which rely on values of the options object to function properly. In other words, my options are mandatory. That doesn't sound right, so I'm wondering if perhaps I'm misusing the options parameter, and if so, what's a better way of doing things?
Dependency injection is a not bad practice at all. It's actually common practice especially dealing with Views. It also makes it easier to unit test your view. So using the options to get some required attributes to build your View is completely OK. Maybe a you could share some examples of what you think is wrong?
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.