Directive that shows a sample depending on what value the user is highlighting - angularjs

I'm stuck with Angular. I have a directive that shows a list and when the user hovers over an item, I want to show a preview of the item, with the preview being given by the directive user.
Some tricks though... I want the user to be able to filter the list using an input [which is easy on it's own] and there is some basic styling surrounding the list that I would like the directive to handle, like adding the checkboxes that well be watched to create the model for the directive.
I want the directive user to simply be able to write:
<preview-list list='unfilteredlist'>
<div>
<h1><blink>{{title}}</blink></h1>
<h2><marquee>{{html extrodinaire}}</marquee></h2>
</div>
</preview-list>
I tried using ng-transclude, but it uses a sibling scope and I've been looking for work arounds and I can't find any. The only ones I found involved writing the entire template in javascript, which honestly I can't believe people think that's an acceptable solution.
Any solutions, or is this actually completely impossible in Angular?

As i see it you have two options :
Create a preview box for each member in your list and toggle visibility on hover. This is great if you have only a few values and the preview box is heavy.
Create a transcluded directive in which - the main scope will hold the list and the currently hover element. The sibling scope will hold the preview container. Once the selected value changes the preview box will update (according to your bindings) and only thing left to do is position it.
transclude is hard at first but it pays off.
Hope this helps.

Related

Attach additional functionality to parent controller in Angular

I have a dropdown element directive to display in essence a styled dropdown list with additional capabilities.
My dropdown controller has a function called openDropdownItems that get's executed when the list should be displayed.
Then I also have another attribute directive called setInViewWhen that provides an expression when the element should be scrolled into view when condition is true.
<x set-in-view-when="something.item === selectedItem">
This is just an example of some X element with my attribute directive applied.
The thing is that I would like my dropdown list items (LIs namely) to have this directive on them so when user navigates over them using a keyboard, it would automatically scroll them in view when they get passed visible viewport. Whether these items are displayed within a scrollable container or as a whole in a list longer than the browser viewport isn't really relevant.
The main idea is for scrolling to follow dropdown list selection. Whether that should scroll the main window.
The problem
I can make my setInViewWhen directive completely independent but that means that I would have to search for the closest scrollable container whenever condition changes on an item. This seems to be quite a bit of processing that I would like to avoid to repeat (I need to traverse the DOM upwards, checking each node's calculated stylesheet property OverflowY + some additional checks.
This basically seems redundant because whenever I get the closest scrolling ancestor all sibling elements with the same directive could reuse the calculation result.
Question 1
How can I share this knowledge between sibling directives? If I was to fire an event I could not know whether receivers are siblings or not without any additional processing.
Question 2
Instead of checking for scrollable container every time when my directive's condition becomes true I could theoretically change dropdown parent's openDropdownItems to first complete it's original execution an then also execute the scrollability check and use the result of it along with my directive's condition.
I can gain access to dropdown's controller in my directive via directive requires property and adjust it in the post-link phase.
But this also means that I couldn't use my directive outside of dropdown which I would like to as it is a generally usable directive I could attach on several elements in my app to scroll elements into view under certain conditions.
What would you suggest how to do this?
To sum up the comments:
For Question 1, there is no direct way for sibling directives to communicate with each other. I usually create a wrapper parent directive to relay the messages.
As per the comments, the condition something.item === selectedItem introduces a watch; for a list of select items this can be many watches and detrimental to performance. Even if it doesn't occur now, it is a trap and someone may be lured into using this for a long list of items in the future. I would rather have a single watch in the parent of the <li>s and add the "scroll into view" logic there.
With the previous point in mind, you could still have a standalone setInViewWhen directive (seems useful) and have the controller of your X-select replacement directive share code with it. E.g.:
a service containing the common functionality,
an angular value containing a base class (Typescript or JS) and have the controller of both the setInViewWhen and X directives extend from it
or any other solution that is convenient for your case
Related to Question 2: The children could require their parent and change a method in it (this technique is even endorsed by Angular, see ng-model and custom controls where they override ngModel.$render() by replacing it). If you do that however, there would be many children changing the method of the parent, which could lead to a mess.

angular-ivh-treeview show only selected nodes and disabled checkbox

Searched the internet a long time but didn't find a way to do the following:
Show only selected nodes and make the checkbox disabled.
Looked to use a filter (ivh-treeview-filter) to show only nodes which are selected, but providing a filter function does not work (ivh-treeview mentions it uses Angular filterFilter. Any examples around to use a filter function?
There is a ivh-treeview-visible-attribute in ivh-treeview.js, however i think there is no active code for this, anywhere an example where this works?
How can i make the checkboxes disabled?
Thanks in advance
I'm not 100% clear on what you're asking, ivh-treeview provides a jsbin template in its README for demos, perhaps you could show us what you've attempted so far? I'll take a stab though.
You can definitely use ivh-treeview-filter to show only selected nodes. Define a function like so in your controller:
this.mySweetFilter = function(node) {
return node.selected;
};
And pass it to your directive:
<div ivh-treeview="vm.treeData"
ivh-treeview-filter="vm.mySweetFilter">
</div>
Here's a fully working demo that lets you toggle whether all nodes are visible or just the selected ones: http://jsbin.com/nirovahupa/1/edit?html,js,output.
As for disabling checkboxes dynamically - there's nothing baked in to the directive to allow you to do this. You can however (and are encouraged to) use your own custom node templates and it would be a simple matter of hiding the treeview checkboxes and showing your own disabled ones as needed.
Hope that helps!

AngularJS scope issue with a UI-modal window

I'm using the UI-bootstrap modal window in my Angular application and I'm running into some kind of a scope problem.
I've got a modal dialog which basically has two modes. At first, it displays a list of existing items for the user to select from. In case the item the user needs is not in the list, he can click "Create", then I hide the div containing the list and display another div which contains an input form so the user can add an entry to the list. This is all really trivial stuff. The buttons to toggle which div is being shown work fine. I basically have a boolean scope variable called "create", which takes care of this.
Then, in the modal-footer I have two save buttons. One is shown when in "list" mode and the other is shown when the user is in "create" mode. Again, works fine.
Now, when the user is in "create" mode and clicks the corresponding "save" button, then I need to process the form and finally switch the state back to the list, that is set the "create" scope variable back to false, but this is not working for me. It's like I'm dealing with more than one scope since the view does not update when I update the "create" variable from the button click in the controller.
I've created a working Plunker which demonstrates this, please have a look:
http://plnkr.co/edit/KDxzH21Lmthg0bc0cfUT?p=preview
I know this is probably something really simple I'm missing. Hoping someone can point me in the right direction!
EDIT: As per the suggestion below from Mik378, I created an "intermediate" object in the scope and assigned the "create" variable to it. Now this works like I wanted to.
I updated the Plunker: http://plnkr.co/edit/KDxzH21Lmthg0bc0cfUT?p=preview
If you're using scope.myVariableToReach, you have to change that by scope.oneIntermediateObjectNotAccessibleInTheChildScope.myVariableToReach.
Otherwise, when you set scope.myVariableToReach directly, it would change the child one, not affecting the outer.

how to prevent AngularJs from having old view and new view on dom when route changes

I am using a directive "slideable" which creates a slideout area and has a toggle. This code that was not written by me but it demonstrates a larger issue for me. When I changing views (most commonly /user/:id type), slideable is a directive used on the template. The directive searches for an element during its link function and binds a click event. The issue is that when I am changing routes and the new view ( same type but different id ) is being loaded the directive is re-binding to the old view. If I stop the browser in chrome during the link then I will see two ng-views on the dom and the issue is it binds to the one that is leaving.
I also have other issues that appear to be related to this phenomenon. Is it normal that the old view would still be on the dom while the new view is being formulated?? Why wouldnt the old-view be destroyed before the new one is rendered? How do I get around this issue in a directive like this?
Thanks.
I am looking to understand conceptually what is happening. I already modified the directive to select the latest view and to appropriately search and bind to the correct element. But I am a bit perplexed as to why there would be a state where both co-exist on the dom.
One definitive reason why the old HTML fragment is briefly present along with the new one is to support animation of transitions from the old to the new. Take a look at the ngView documentation and you'll see an example of an animated transition, and it'll be clear that this is not a bug or a design flaw.
Usually when someone has problems with binding to the right element or element's event, it's because they are selecting the element without limiting the scope of the selector to the HTML fragment being added or updated, or trying to target parts of the DOM outside of the directive. So that's the first place to check, that the directive is doing things right, but like I said we'll need code to check on that.

Check the validity of dynamically added forms on angularjs controller's $scope

To check the validity of a form in my page, I test this property in my controller's scope:
$scope.formName.$valid
The problem is that when I add forms dynamically to the page (based on a model property), the $scope.newFormName property is not added to the scope.
This plnkr illustrate the problem
Click the 'Add form' button to add forms to the page
Click the 'Search forms' to update the list with the forms found in the $scope
Note that the added forms are not found in the scope
Is there any way to make this work? How can I check the validity of this dynamically added forms?
So your code adds a list of identical forms. And you want to see whether this list is valid or not.
The solution is to use ngForms within a parent form. See this Plunkr (my modified version of yours).
Form input values are bound to objects in the $scope.dynamicData array, which also is used by ngRepeat to create the list of forms.
Invalid fields are shown with a solid red border, and invalid forms have a dashed red border.
When forms are nested like this, the parent form is invalid when any of its child forms are invalid.
I'd use angular.element(). I would also personally get it via ID rather than name, but that is just me. View this plunker to see what I did: http://plnkr.co/edit/b87HJt
I'm using angular.element() to get the element by the name, getElementsByName and then using the $attr directive to get at the name.

Resources