How do I change attributes on directive element's parent - angularjs

I started with a directive that restricted to be used as an attribute, and when used, it would add ng-mouseover to the element it was applied. It seems to be a trivial task, you just need to $compile it, right? Wrong, it will work only until you decide to have an isolated scope. here's jsbin that explains what I'm talking about
I couldn't solve that problem, anyway.
Now, maybe it's possible to solve this dilemma by making the directive restrict:'E' and transclude whatever is inside of it. Works? Sure. jsbin
But now, I realized that I need to apply those mouse events not to the element itself but to its parent. As in the first case where I struggled to add attributes and keep the content without having scopepocalypse, once again I'm having similar headaches.
Can you guys show me one simple example how to make a directive with isolated scope that adds ng-mouseevent attributes to its parent?
Sometimes I feel stupid for deliberately overcomplicating my own life. Maybe I just need to attach good old event handlers to the parent element right from inside of directive's controller? Yes, seems like an easy fix, but that would be conceptually wrong, and againsts angular's traditions, isn't that right?

The angular way would be to never dynamically attach directives to parents if you're implementing the compile or link functions. The compile and link process is a one way street - meaning parents are compiled before children. Sure you can try to go against traffic, but doing so is difficult, risky, and more trouble than it's worth. By adding directives to a parent, and then trying to recompile it, you'll probably end up recompiling directives multiple times, or worse, introducing a subtle memory leak. It's a difficult road, I'd advise against it. It is not the angular way.
Instead of asking yourself, how do I add directives to my parents, ask yourself, how do I add directives to my children? Child directives can communicate to parent directives through the 'require' property. Usually, this is flexible enough to handle most situations.
To answer your second question, it is perfectly acceptable to hook into JavaScript events within a directive definition (how do you think ng-mouseover was implemented?). If you do, you should do so inside your link function.

Related

Interpolate a one-time binding manually

How could I interpolate a one-time binding manually?
Here is my use case.
I have a list of a lot of elements. The elements just sets of controls. I get the data for the elements from the BE. Then I would like to leverage the AngularJS interpolation and set the controls` values. But I would like to avoid the interpolation of all the elements every time I change only a single control.
So, I use a one-time binding to set the values, but then when I change them they are not updated.
The expected result in the example is that once something is typed in the control, the respective one-time binded value is updated. Also, the one-time binding should remain there, since in my real case there will be a lot of controls and I do not want to strain performance when only one control is interacted with. A simplified example of mine can be found here (in my real case I have not only inputs, but checkboxes as well, but I believe that the approach should be the same for them).
After a research I found out that a directive should be used in this case. But I was not able to find a simple to understand example. So, could someone post it here, please?
Different from a directive solutions are welcome here as well.
First to understand your problem: Why do you want to avoid the angular change detection here? The only reason that comes to my mind would be heavy performance problems.
In one case here, there were one or two thousand of inputs, all with a binding. The application was a little sluggish, but still usable. We accepted this, as we had to re-implement all the binding manually otherwise.
If your binding is rather simple (no validations, conversions, etc.) and the values are not shown on other places while being edited, maybe it would be a way for you to use an AngularJS directive on a native HTML Input element that just listens to its onChange event to save changed data and gets updated programmatically whenever you know it changed from outside (if it does at all).
(Not talking about better using Angular, which has a much tighter grip on change detection ;-) ).

Backbone.js app design principles: extending views vs initializing child views

Long story short, let's say one app has multiple pages with:
a form
a list
pagination
each page may require (now or in the future) custom actions to be implemented
My question is, witch is the preferred Backbone way of handling this and why (please argument) ?
Define, a pagination view, a pagination collection, a search model, search view, etc, and initialize each one as a child view in all the necessary pages. This means we will have to append child view elements into the 'master' element, and handle all the communication between these in all necessary pages.
Define a pagination view (with it's own pagination collection and search model) and extending it across all the necessary pages. This does mean that we will have to make use of template partials (for forms, pagination, etc) and bypasses the need of handling communication between child views while also removes the need of appending/removing child view elements.
Please add your way of handling these cases if not found above, remember to argument.
My personal opinion would be 2. And that is because it removes a lot of hustle with communication between child views and it makes everything much more easier to read just by extending classes, instead of having to 'manually' init child views. It also gives one the option to rewrite behavior per page when needed.
I think #2 is a poor choice.
It's a very good idea to keep templates as simple as possible. They are basically just the markup that's generated for some input object. In order to get that object to the template, in MV* frameworks you have Views, that can either pass a model to the template or send some formatted data to the template (I prefer this where possible).
Partials just create markup. You'll still have to handle events, updates to the DOM and rendering inside the view. If you only use one view it will have to handle a lot of things, something associated with poor maintainability and a more bug prone codebase.
You'll either have a lot of code in the views, or you'll end up with a lot of mixins or doing a lot of inheritance - and I have no idea which is worse.
Big things are a lot harder to test and to reason about. Avoid doing big things.
I think that another big problem with the template partials approach is the fact that you cannot rely on type information (something like interfaces), on the object that ends up in the template. It's probably easy to make it work when you have a partial or two that you just created, but, in time this information will get lost, leading to a bad development experience.
You'll need to make sure views unrelated to your changes are kept updated with the partial changes you just made for a feature.
Keep in mind that software is never done. Things always change.
Instead of thinking about relationships between models you'll have another complex challenge that you need to handle: the coupling of views through partials.
The alternative is a lot better. Composing specialized views is a good approach because each has it's own internal, smaller state and just it notifies listeners when some action takes place. Nobody cares about what's going on there until something happens and then you just get concrete data.
Going with #1 helps you deal with complexity in your application while allowing you to reuse them in other contexts.

AngularJS: How to combine drag and drop with mouseenter/mouseleave (without JQuery-UI)

Here I found an Angular-style way to drag & drop that works well across scopes (which -I discovered- not all third-party modules do). Traversing scopes is relevant to my little project, as it involves a recursively nested list. You can find my JSFiddle here. Note that you can pick up a list element, but dropping is not implemented. What is implemented is a directive to fold out a list item if you hover your mouse over it for more than 1.2 seconds.
What I am trying to achieve is to have this foldout directive work only when the user is dragging a list item, but not in 'normal' (non-dragging) mode, i.e. the reverse of the current behaviour. As it stands, the list items do not respond to any mouse movement at all during dragging. I suppose this has to do with the preventDefault() calls in the ang-drag-drop module. Moreover, my Angular directive delay-foldout doesn't know about the drag mode; how do you tell it that the user is dragging an item?
My reason for not using JQuery-UI is that I think it's rather overkill for this simple effect, and I suspect it must be doable without it. Unfortunately my JS / JQuery skills are limited, and I'm new to AngularJS. So far, experimenting with the module only messed things up 'beyond all recognition' (and to be frank I don't have a clue where to start changing the code). I therefore decided to put the module in the fiddle, so someone can hopefully point out what should be changed in order for the delay-foldout directive to work as intended.
Please do not consider this question criticism on the developer of the module, I think he has done quite a good job. It's just that I'm trying to use it in a way it wasn't originally built for, and hoping to learn something in the process.

Is this an acceptable process in Angular-JS

ng-model="$parent.$parent.$parent.something"
Is there a better way to write this? I am inside serval ng-repeats.
Unless you have isolated scopes, you should just be able to reference your something property directly. Scopes inherit their parent properties.
ng-model="something"
EDIT: there are some gotchas around this. Take a look at https://github.com/angular/angular.js/wiki/Understanding-Scopes

Developing widgets in one view in Angular.js

I’d appreciate if you could share you view/experience.
Suppose you have a view which contains several widgets that share some of the data (fetched from a server). For example we might have a tree, list and breadcrumbs widgets in the same view, naturally a name of a single item can be displayed in more than one widget at the same time.
My question is what is the best practice of developing such views?
Specifically:
Are the widgets totally independent? (The easiest to implement, but suffer from performance problems)
If the widgets are dependent, do they communicate through:
A single model (introduces tight coupling between widgets and prevents further code evolution)
Events (lose coupling but error-prone due to lose contract, less explicit code)
Any other way
Provided those widgets have their own controllers and scopes, how do you propagate the change notifications from the URL (or any other event) to all of them?
For example if you wanna see an entity with a specific ID using URL routing, do you have a top-most view controller that is responsible to catch this change and notify all the widgets about it using some in-house mechanism, or do the widgets catch the event independently?
I guess all these questions are somehow related so feel free to answer them in whatever form/order you like.
Thanks.
Are the widgets totally independent?
I think that is too broad of a question for us to answer, as that really depends on what the widgets/directives are doing. Are you asking if they should be using isolate scopes? See also When writing a directive in AngularJS, how do I decide if I need no new scope, a new child scope, or a new isolated scope?
do they communicate through...
Again, too broad, sorry... it depends on what the directives do. Besides the ways you already listed, you could also communicate via
a service, which is probably what I'd use if I had more than two directives that needed to communicate
require: 'controllerNameHere'. With the require approach, you would define methods on your controllers using this instead of $scope. This method is limited to essentially one-way communication though: from the directive that has require to the directive that it is requiring. E.g., on the AngularJS home page, the pane directive requires the tabs directive. This allows the pane directive to call methods on the tabs directive's controller, but the tabs directive can not call methods on the pane directive's controller. (For more on this, see 'this' vs $scope in AngularJS controllers)
how do you propagate the change notifications
That depends on the type of scopes your directives have. If you are using scope: true for your directives, you don't have to propagate anything. They can all $watch some parent scope property (and because of the way JavaScript prototypal inheritance works, all of the directives can see the parent scope properties). If you are using scope: {...}, then you can use '=' or '#' to define local directive scope variables and use $watch to watch them.
If you are concerned about performance with $watches (since they are evaluated at least once every digest cycle), then events might be more appropriate.
Another thing to consider: do you want your directives to know about URLs or just scope properties? Using scope properties would likely make your directives more reusable.

Resources