wutzebaer
wutzebaer

Reputation: 14863

Bind element to scope variable

Is it possible to bind an element to a scope variable?

I imagine something like that:

<div ng-controller="myCtl">
    <span ng-scope-bind="myHelloSpan">hello</span>
</div>

app.controller('myCtl', function($scope) {
   $($scope.myHelloSpan).fadeIn(100);
})

I couldn't find anything in the docs.

Upvotes: 1

Views: 4167

Answers (2)

ulvesked
ulvesked

Reputation: 449

This is not the Angular way to do it.

A controller should never change the DOM (HTML). That should be handled by a directive. Your controller is supposed to manipulate your business logic and change scope variables and then your directives watch these variables and trigger changes to the DOM/HTML. This is how the core directives (like ng-show, ng-repeat, ng-href etc.) works.

Read more about creating your own directive at https://docs.angularjs.org/guide/directive#creating-directives

If you really, really want to do this (though I recommend you do not!), here is a very simple directive that should do what you want:

app.directive('myScopeBinding', function () {
     return {
         link: function ($scope, element, attr) {
            var myScopeVariableName = attr.myScopeBinding;
            $scope[myScopeVariableName] = element;
         }
     }
 });

This directive will allow you to assign a DOM element to a scope variable:

<div ng-controller="myCtl">
    <button type="button" ng-click="myCustomFadeTrigger();">Click to fade in hello span</button>
    <span my-scope-binding="myHelloSpan">hello</span>
</div>

and it can be used in your controller like this:

app.controller('myCtl', function($scope) {
    $scope.myCustomFadeTrigger = function() {
        $($scope.myHelloSpan).fadeIn(100);
    };
})

The problem with this is that the controller creates the scope, so by the time your controller executes, the directive has not beed assigned to the scope variable. This is why my example uses a button. It could also be used with a timeout, to let the span-directive assign itself to the scope.

Another issue with this is which scope the element is assigned to. If used inside an ng-repeat, it will be assigned to the current iteration. If used inside another directive with its own scope, it will be assigned to that scope, and not necessarily within the controller scope.

THE ANGULAR WAY TO DO IT is to create a directive that does the stuff for you based on some scope variable or event.

app.directive('myDirective', function () {
     return {
         link: function ($scope, element, attr) {

            // Get a jQuery-wrapper only once
            var $element = $(element);

            // Listen for a scope event
            $scope.$on('myCustomEvent', function() {
                $element.fadeIn(100);
            });

            // Watch a variable
            $scope.$watch('myElementVisible', function() {
                if ($scope.myElementVisible == true) {
                    $element.fadeIn(100);
                }
                else {
                    $element.fadeOut(100);
                }
            });

         }
     }
 });

And the HTML to use this directive:

<div ng-controller="myCtl">
    <button type="button" ng-click="test1();">Click to fade using event</button>
    <button type="button" ng-click="test2();">Click to fade using variable</button>
    <span my-directive>hello</span>
</div>

Upvotes: 1

handle
handle

Reputation: 69

I would also like to know the answer to this question. Let me try to rephrase it to improve clarity for anyone who knows how to do this:

I have a javascript array of objects called arr. In the HTML document, I'm using ng-repeat to make one intl-tel-input element per object in arr. Now, I need each of the objects in arr to reference its associated input element on the DOM, so that I can use logic like this:

for (var i = 0; i < arr.length; ++i)
    foo(arr[i].domElement.intlTelInput('getNumber'));

where foo is a placeholder for some function that depends on the result of the getNumber method attached to the intlTelInput jQuery plugin, and domElement is the intl-tel-input element associated with this object in arr.

In the examples from the intl-tel-input project source page, unique element IDs are mostly used to select specific instances of the control on the page, however I cannot use any element IDs because there could be multiple instances of the widget on the page, leading to ID collisions.

Without explicitly using unique IDs, how can I "bind" an HTML element to a javascript object, as opposed to binding the javascript object to the HTML element?

Upvotes: 1

Related Questions