0

In the code below, the fact that I am calling $(".test").buttonset(); is breaking updating of {{ item.value() }}. I assume it has something to do with jQuery UI rewriting the DOM.

<html ng-app>
    <head>
        <link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/start/jquery-ui.css" type="text/css" rel="Stylesheet" />
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/jquery-ui.min.js"></script>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
        <script type="text/javascript">
            function ExampleCtrl($scope, $timeout) {
                $scope.list = [
                    { name: "a", value: 5 },
                    { name: "b", value: 6 },
                    { name: "c", value: 5 },
                    { name: "d", value: 6 }
                ];

                $scope.calc = [
                    [
                        {
                            name: "a, b avg",
                            value: function() { return(($scope.list[0].value + $scope.list[1].value) / 2) }
                        },
                        {
                            name: "c, d avg",
                            value: function() { return(($scope.list[2].value + $scope.list[3].value) / 2) }
                        }
                    ]
                ];

                $timeout(function() {
                    $(".test").buttonset();
                }, 0);
            }
        </script>
    </head>
    <body ng-controller="ExampleCtrl">
        <div class="test" ng-repeat="group in calc">
            <div ng-repeat="item in group">
                <input type="radio" name="a" id="{{item.name}}" />
                <label for="{{item.name}}">{{item.name}}: {{ item.value() }}</label>
            </div>
        </div>

        <ul ng-repeat="item in list">
            <li>{{item.name}}: <input type="number" ng-model="item.value" />
        </ul>
    </body>
</html>

Here is a jsFiddle version: http://jsfiddle.net/vhLXW/

2 Answers 2

2

Dom Manipulation in the Controller is not a good idea. WHat you could do is create a simple directive which will take care of doing the buttonset() job for you. It would look something like

directive('buttonset', function() {
    return function(scope, elm, attrs) {
      elm.buttonset();
    };
  });

And in your html, you could do

<div class="test" ng-repeat="group in calc" buttonset>

Now your code has no dependancy on the presence of class="test" in your html.

What is happening in your situation is that the JQuery UI is transforming the DOM. It is hiding your radio buttons and putting in other html Elements ( restyling the radio group ). When this new widget is clicked the JQuery UI code propagates that click to the correspoding Radio Button.

If JQuery UI exposed an event or something similiar, then we could have hooked into that and from Angular you could have called $scope.$apply(); . But, unfortunately it doesnt seem be to the case ( unless I am mistaken ).

If you really need the look of JQuery UI you could write your own code ( copy paste the specific piece of HTML after JQuery UI manipulates the DOM ). Use the styling from JQuery UI and write your own logic. Angular makes it extremely easy to write your own logic.

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

2 Comments

Here is a jsFiddle version of what I currently have: jsfiddle.net/vhLXW Here is a jsFiddle version that does what I think you suggest: jsfiddle.net/vhLXW/1 In the first, the buttonset works, but updating does not. In the second, the buttonset isn't being created, but updating works.
Here is a fiddle that uses directives and in which the buttonset works. jsfiddle.net/ganarajpr/vhLXW/2. Like I said in the answer, if you want it to update that you will have to create your own buttonset ( which you could style using JQuery UI! ).
1

Let jQuery UI modify the DOM first, then let AngularJS do its DOM manipulation. This way, Angular will bind to the modified DOM elements. I put your JavaScript at the bottom of the page to get it to work without the need for a timeout:

</body>
<script type="text/javascript">
    $(".test").buttonset()  // allow jQuery UI to manipulate the DOM here, first!
    function ExampleCtrl($scope) {
        $scope.list = [ ... ];
        $scope.calc = [ [...] ];
    }
</script>

UPDATE: as @Chas.Owens pointed out, this does not work. Although data binding works correctly, the UI is broken. If seems that if jQuery UI modifies the DOM first (as shown above), AngularJS data binding works, but it somehow breaks the UI. If AngularJS modifies the DOM first (the original problem), AngularJS data binding doesn't work (likely because buttonset adds an additional <span> around the label's text), but the UI works.

4 Comments

While ugly, this is a better option than duplicating all of the work jQuery UI has already done.
Looks like I spoke too soon, the buttonset doesn't work: jsfiddle.net/vhLXW/3
What browser? Your fiddle works for me in IE8 and Chrome 21 (21.0.1180.75 m). When I change a numeric value in one of the list items, the appropriate buttonset avg is updated.
The updating works, the buttonset is broken. Clicking on one of the buttons in the buttonset should make it turn green (and the other go back to blue). This is in Chrome 21.

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.