AngularJS - Idiom for watching a "single use" variable? - javascript

I have a select input with a controller that- when a value is selected- wants to pass that value to a directive that will display it. (In this case, draw a shape on a leaflet map).
The issue is I want the value to be "fire and forget"- so they select the shape, the select box resets to empty again immediately, and the directive in charge of drawing receive the shape and draw it.
My initial thought is to have a service that links the two: so when the shape is selected, the select controller updates the service and sets the drawThisShape variable, which is referenced on the directives $scope and being $watched: when it changes, the directive draws the shape. Happy days.
This seems like a lot of effort to go to just to send a variable from one place to another though. And the service gets left in a state of constantly having the "previous" shape on it's model despite it not being needed anymore.
Perhaps I'm being too fussy but I feel like there must be a better way to achieve this?
E: For clarification, the controller is the controller for the select input, the directive I want to pass it to is a directive for a leaflet map instance, they are not linked in any way

Well, I have used the technique you've described in the past. Another way of achieving what you need is to broadcast an event on the $rootScope (or any other parent scope of drawShape directive) from the select input that the drawShape directive is listening for.
See scope event propogation and broadcasting

Related

Using and Communicating with Directive from different Controllers

I would like to create a directive that is similar to a control. It should be able to be instantiated from "anywhere" and I want to call functions on it's scope from a parent scope/controller.
A very easy example would be:
I have a special directive called myContactForm (which can be used in different places and pages). Now this myContactForm, in it's templace, uses the directive myEditControl. myEditControl is, when you look at the template nothing but an input and a button and in the scope has the function "clear()" so you can delete whatever is inside the input and maybe other functions.
Now when something happens in myContactForm (for example a button is pressed or a broadcast is received or whatever) I want to call "clear()" on the myEditControl that's inside it. Just like it was a normal control.
How would that be done?
I know about the require but that only works for parents and I would have to know the Controllername of the Directive that uses myEditControl. Instead I want any View/Controller to be able to use myEditControl. And I cannot require the myEditControl-Controller from myContactForm because it is a child not a parent.
What is the technique used in angular to solve these kind of problems?
My idea was to transmit a callback or "myContactForm"-Scope to the myEditControl with an attribute so the myEditControl can "register" itself on the parent and the parent then saves that controls scope for later reference and calling of functions. This seems very hacky though....

AngularJS : directives newbie

I'm starting my great adventure with angular and wanted to ask a question regarding directives usage, as I am not 100% sure after seeing multiple tutorials.
I want to make a simple app giving you directions:
1) click a button, fire in the controller a function to get current position from navigator geolocation (i think no service is necessary for this, and this can stay in the controller?)
2) after getting the coordinates I have some information about the place, which should be shown to the user, and here is the question: Should there be a directive with template for binding these information from the scope and showing in the dom, or is it enough to use simply "ng-hide" (which is in fact a directive - sic!:)) on a div, fetch the information on a place with a service, bind it with the hidden div, and set "ng-hide" to false to display the dom containing place information.
The "ng-hide" variant seems easy, but is it the proper "angular way" or just bad practice of beginners?
Thank You for your time and help in advance:)
IMHO
You can put it in a service if you want to use that method from different controllers or for clean-code purpose.
I use directives when I want a specific behaviour or a group of controls that repeat along the application. If you are using basic html controls and you just need to display/hide I would use ng-hide.

How can I put content from my index.html into my view.html?

I have something like this plunker. I would like my view to get populated with the mapId that gets passed into the directive. Then I would like to show that view in place of "This is some content".
First how can I pass the mapId into the view and secondly how can I then show that view in the lightbox div?
I know this is kinda vague but I'm new to angular so I don't know what other information is needed here.
You're not going to be able to use the view.html file with the current lighbox code. It would need to be heavily modify to make use of template files and isolate scopes. However, you can use the current code to achieve the same thing with perhaps the addition of a controller to modify the scope.
I've modified the index.html in the plunker so that it displays the mapId value.
Let's go over what angular-lightbox is actually doing:
By returning just a function, the directive is going through the whole compile process and then using the returned function as the linker. The linker then goes on to (depending on the options) add an overlay (the opaque dark background of the lightbox) to the DOM & then add the lightbox active class to div#lightbox. This div is already in the DOM, but hidden due to the CSS, and has been already compiled & rendered by AngularJS so it can't really be changed other than through two-way data binding at the same or child scope level.
What my changes did:
I added a bound scope variable to div#lightbox called mapId and added an ng-click to the buttons that sets the value of mapId to 1, 2, or 3. So when you click on the button, div#lightbox is revealed & the value of the new value of mapId is shown.
Given that the above is probably not what you want to accomplish...
Let's talk about how to go about doing that
First you will need to load view.html into the directive somehow. Either by just having the view.html contents be a string inside the directive or use $templateCache.
You will then need to $compile the HTML from view.html, passing in a new scope that contains the values you want from options, and then append it to div#lightbox.
I would use a modal from Angular-UI bootstrap http://angular-ui.github.io/bootstrap/, and adapt it for my case. I think this is a good starting point.

Angular 1.2.0-rc.3 Directive Priority Change Issue

So I have this code example that uses Angular 1.2 RC2 and everything works fine, you click on the handle to toggle the display of the content and the controller and directive have seperate scopes as intended:
http://plnkr.co/edit/e3XAZuhSMAxmkWzKKM39?p=preview
Now I upgraded to Angular RC3 yesterday and now the functionality does not work as it stands in the plunker, I get the error the specific requires generic which is can't find. Going through the change log, I though this might have to do with this breaking change:
$compile: due to 31f190d4, the order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute
To fix this they either suggest converting the post linking to a pre linking (which I can do since my post linking needs access to the scope which is not available in the pre linking) or to decrease the priority of the directive. So this plunker does that and functionality does work:
http://plnkr.co/edit/arP3aruH8HEdiwFg6mWp?p=preview
However there is a major issue and that is now because specific has a higher priority, the isolate scope that generic needs is no longer being created so now contentVisible is on the controller scope which is not good.
Now I could just move the scope: {} from the generic directive to the specific directive however it should be possible to use the generic directive by itself and if I did it would attached to whatever scope it is part of and not its own (which would make it impossible to have multiple instance of this directive, which is way it needs its own scope).
The only thing I can think of is to add a directive, called something like isoScope, make sure it has a very high priority, and have it define scope: {}. Then if I need to use generic by itself, I just have to make sure to also add the isoScope directive to make sure it has an isolate scope. Like this:
http://plnkr.co/edit/1NYHpUcPFWEbAzvkCeRH?p=preview
Now I am hoping there is a better way to accomplish what I am looking for without the isolateScope directive. Am I missing a way of that this without that?
UPDATED EXAMPLE
So here is another plunker that includes hopefully better examples of what I am trying to convey (still has virtually no styles but should not be needed to get the point across):
http://plnkr.co/edit/KtRMa1c9giDrhi1Rqyho?p=preview
I have 3 directives:
expander
notification
isolateScope
The expander directive only adds functionality to be able expander and collapse content, nothing more. This functionality is something that should be able to be used alone or with another directive (which is why it has a controller).
The notification directive is used to display notification however since we don't want to display the notifications all the time, we use it with the expander directive so that the user can toggle the display of the actually notifications (similar to how stackoverflow.com does it in the top left).
While I imagine the expander would most likely be used with another directive it should be possible to use alone and that is where the isolateScope directive comes into play. Since the expander directive adds data to the scope and you may want to have multiple expanders on the same page, it needs to have an isolate scope in order to work. Now on a users profile page you have have data like developer key and address that you don't really need to display all the time so lets have the user control that. I have the isolate scope to be able to control both of those independently because without the isolate scope, both of them would be on the same scope and be controlled by the same instance on contentVisible.
I just don't see anyway with how directives now run in 1.2.0 RC3 to be able to accomplish this without that isolateScope directive (though I would be happy to be proven wrong).
I have updated your code so that it does what I think you want (at a minimum this works the way your old code does, but under rc3 as you wanted): http://plnkr.co/edit/nsq4BGAih3lfNmS2mLP7?p=preview
But I made quite a few changes and a significant architectural change so let me know if this moves away from the spirit of what you're trying to achieve.
I think the gist of the issue was that your two directives (generic and specific) were tightly coupled around contentVisible which created a complex dependency that resulted in you having to very carefully manage invocation timing. My approach was to decouple the two directives- encapsulating contentVisible within generic. This allows generic and specific to instantiate fully independently. So you're not dependent on any invocation timing. And thus the directive priority change no longer has any impact on this code. So, one big win with the solution I propose is it should be robust against further changes by the Angular team.
Specifically, I moved the template in to the same directive (generic) as the controller which manages contentVisible . This way the controller that changes contentVisible lives on the same scope as the template which uses it.
Now specific just calls over to the required: generic controller to toggle visibility (effectively as a setter function).
I also moved the ng-class assignment into the template in order to encapsulate that change within one place (the template) and so you don't need jquery or a link:/compile: on generic.
This is a regression. A fix is in the works: https://github.com/angular/angular.js/issues/4431
I have problem, looks very close to your. So if anything will change want to be notified.
My task: I have contact, that could be shown in defferent ways (very common task), but difference between views is in templates, whereas help functions and preparations are same, so I need generic directive for all views.
What I found:
1. in rc2 it works fine in rc3 unstable
2. in rc3 it could work same only when template is inline, but not when it is templateUrl (even if cached)
So I created two planks rc2 version and rc3 version.
Hope this will help.

What's the angular way to handle redraws?

I'm making a charting application that allows you to create graphs using a drag and drop interface.
I have highcharts and I'm using the highcharts-ng directive.
This directive watches the title, options, and series. And when a person makes a change, I process them and make changes to the options object. Then highcharts-ng redraws the chart.
The problem I'm finding is that I change a few properties in a row such as options.xAxis and options.yAxis, and whenever I do this the application is lagging a bit because it's launching a redraw for every change.
So what would be the angular way to approach this, while still being efficient?
A potential solution I thought of was to add a flag to the highcharts-ng directive, and have it trigger whenever it's changed. And then just change it after I'm done processing the data. Another potential solution is to listen for a certain event inside the highchart-ng directive, and then trigger the redraw whenever that event is received. But these solutions seem/feel a bit hacky to me.
Angular does its own dirty checking and (ideally always, not but really) rewrites the Angular-controlled sections of the DOM whenever their corresponding view models change. I think that this behaviour is so fundamental to Angular that if you don't like it, you'd either better work around it, or use a different databinding framework.
The workaround I'd recommend is basically what you described in your first option: a view model inside the view model. Have a private variable inside the directive's scope which tracks the changes you're interested in, the ones which happen more frequently than you want to redraw. Then when you're ready to redraw (you'll need your own logic for what determines "ready"...time? a particular kind of change? a particular threshold of change?), update the real view model by setting your private variable back to its original field on the real view model.
Code sketch:
// (inside the directive)
var _options = $scope.options;
// ...
// rapidfire updates happen; save them to _options rather than $scope.options
// ...
// now you're ready to redraw:
$scope.options = _options; // angular now knows $scope is dirty and triggers the redraw

Categories