1

I am using AngularJS and I want to create a button that does an action only if the user is registered. If the user is not registered, it opens a signup tooltip with a specific message. Here is the button markup:

<button signup-tooltip="submitForm()" signup-message="Please sign in to submit the form">submit form</button>

The signup tooltip template looks like this:

<div class="signup-tooltip-view">
    ....Many elements
    ....
</div>

Important thing is that the signup tooltip must opened above the button.
My signupTooltip looks like that:

module.directive('signupTooltip', function() {
    return {
        scope: {
            action: '&signupTooltip',
            message: '@signupMessage'
        },
        templateUrl: 'path/to/signup/tooltip.html',
        link: function(scope, element, attrs) {
            function calculatePosition() {
                // This method calculates the position of the tooltip (above element)
                var top, left;
                ....
                ....
                ....
                return {
                    top: top,
                    left: left
                };
            }

            element.click(function() {
                if (user.isRegistered) {
                    scope.action();
                } else {
                    var tooltipPosition = calculatePosition();

                    // Here I need a reference for the template element <------PROBLEM
                    var tooltipTemplate = ..... HOW TO GET THE TOOLTIP ELEMENT

                    tooltipTemplate
                        .appendTo(element.parent())
                        .css(tooltipPosition);
                }
            });
        }
    };
});

Inside the directive I need a reference for the compiled element (the element that created from). How can I get it (keep in mind that I need signupMessage to be compiled with the template)??
What is the angular way for such use case (tooltips)?

2 Answers 2

1

You should work more with the framework, not around it.

This is the "Angular Way" of doing things:

JS:

var template = '<button ng-click="onClick()">submit form</button>\
<div class="signup-tooltip-view" ng-bind="message" ng-show="!!position" style="top:{{ top }}px;left: {{ left }}px">';

module.directive('signupTooltip', function() {
    return {
        restrict: 'E',
        scope: {
            action: '&',
            message: '@'
        },
        template: template,
        link: function(scope, element, attrs) {
            function calculatePosition() {
                // This method calculates the position of the tooltip
                var top, left;
                return {
                    top: top,
                    left: left
                };
            }

            scope.onClick = function() {
                if (user.isRegistered) {
                    scope.action();
                } else {
                    scope.position = calculatePosition();
                }
            };
        }
    };
});

Of course you can put the template in a separate file and reference it with the templateUrl attribute instead of providing a string.

HTML:

<signup-tooltip action="submitForm()" message="Please sign in to submit the form">


Here is the jsFiddle-demo

Sign up to request clarification or add additional context in comments.

3 Comments

+1 This is exactly what I was talking about in the comments. Not sure how far out does OP want to move the tooltip though.
I have a list of more then 100 items. Each item has a button that opens the signup tooltip and does an action. I don't want to compile those 100+ directives where the user will press one or two buttons. The better solution will be something lazy. A directive that initiate all the compile only when the button pressed. What do you think?
If you want to use a directive for this task you have to compile the template anyway, so i would try this approach. And don't worry too much about performance, because creating 100+ directives is not as critical as you think. See this example with 250 elements: jsfiddle.net/23sy3c4c/5
0

Bearing in mind that element refers to your directive, this should do it (uses jQuery's find function):

var tooltipTemplate = element.find(".signup-tooltip-view");

If you're using only angular's jqLite, you'd have to go with the children() method because it doesn't support find for classes. Of course, you can always use plain JS to select it by iterating over its childNodes.

7 Comments

I don't want Angular to append the template to element. Is this the "Angular Way"?
Appending would be the angular way because directives should be used as standalone units. Why don't you simply make a higher-level directive that would render both the tooltip and the button markup (and simply use replace: true?
This way I'll have to find the template and then remove it from the element node (I need to append it to a different parent). In addition, I don't like the idea of using the class name in the directive. What do you mean by "higher-level directive that would render both the tooltip and the button markup"? (replace: true is deprecated)
Yeah, yeah, didn't understand you want the template outside of it. In such case, this is a bad approach. I'd still suggest you build a wrapper directive which would include the template parent and the button.
Currently this is a button, but I might use this directive for anchor or for other elements. In addition, in a wrapper directive case I still have to remove the template from the element. Can you provide an example for such a wrapper directive?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.