0

I want to rewrite this fiddle as a directive. It was my very first experiment with Angular. I actually got the idea from w3 schools, dom manipulation

I thought my fiddle was nicely written, however, after reading that coupling behaviour to a directive may be an anti design pattern I am again at a loss. So designing an element as

<photo>
    <img ng-click="toggleOnOff()" ng-src="{{'togglePhotoSrc()'}}"/>

Does not seem to be recommended. Right?

I want to solve this as an isolated scope. I'd like the two urls to be known in the directive. If possible, I'd like to do this without using element.addClass.

Design advice appreciated as much as a working example.

EDIT: I got this working but would appreciate comments especially in terms of better design!!

<body ng-controller="MyCtrl">
 <div><pre>State is on =  {{isOn}}</pre></div>
 <div>
    <input type="checkbox" ng-model="isOn" />Switch the light
       <a toggle="isOn">
          <img ng-src="{{ getToggledUrl() }}">
       </a>
</div>


<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>

<script>
     angular.module('app', [])
        .directive('toggle', function() {
            return {
                scope: {
                    toggle: "="
                },
                link: function($scope, element, attrs) {

                    element.click(function() {
                        $scope.$apply(function() {
                            var src = element.find('img').attr('src');
                            $scope.toggle = !$scope.toggle

                        })
                    })
                }
            }
        })

     function MyCtrl($scope) {
          $scope.isOn = false;
          $scope.onUrl = "http://www.w3schools.com/js/pic_bulbon.gif";
          $scope.offUrl = "http://www.w3schools.com/js/pic_bulboff.gif";
         //toggle image
          $scope.getToggledUrl = function() {
             return $scope.isOn ? $scope.onUrl : $scope.offUrl;
    };
}

Community
  • 1
  • 1
scalaGirl
  • 335
  • 4
  • 17
  • Why do you toggleClass in the directive? Also what do you mean you would like to know the src inside the directive? Do you want to pass them? In any case I think a template in your directive would be the angular way to go if you want to get rid of jQuery. – masimplo Feb 13 '14 at 01:48
  • toggleClass removed--my bad && !intentional. My original thought was to encapsulate the URLs in the directive as I imagined that isolated scopes are used for reusable components. Is this a thinking mistake? I only use jquery cause all other attempts have failed. I started out with a template but struggled with how to design the way the new directive should look. – scalaGirl Feb 13 '14 at 08:36

2 Answers2

1

For a simple image toggle, you might have an easier time with a hide / show style instead:

<input type="checkbox" ng-model="isOn" />Switch the light
<img ng-show="isOn" ng-src="{{ onUrl }}">
<img ng-hide="isOn" ng-src="{{ offUrl }}">

If you were to write a directive for this, you could use the above as a template, and provide onUrl and offUrl as parameters to it. You could also provide isOn, if the control lives outside of the directive.

Hope this helps!

jkinkead
  • 4,101
  • 21
  • 33
  • Very nice answer. Very clean and readable..perhaps the bool should be renamed ifOn. Perhaps a nice interface could be – scalaGirl Feb 13 '14 at 09:01
  • Isolate scopes are great for very simple widgets like the above (no transclusion or controller logic). Generally, if you treat your directives like pure functions and have them depend only on the attributes you provide to them - and not external scope variables - you'll avoid a lot of the pain that isolate scopes can cause. Otherwise, isolate scopes can be very tricky! – jkinkead Feb 13 '14 at 17:43
  • Thx that clears up a lot. However there is a tiny isOn holding state in a tiny controller in our example. So can or should this be moved elsewhere. I understand well that state belongs in the controller but I am not clear about when to have it within a directive or outside of it. So would you not use the isolated scope in this case which has a trivial controller? – scalaGirl Feb 13 '14 at 18:18
  • Having scope state (`isOn`) is very different from having a controller. A controller is actually not a problem at all; it's more the kind of stuff a controller implies (things like setting `$watch`es, updating stuff on `$scope`, etc). I would actually recommend isolate scopes everywhere - they help with good design - but remain aware that they can be very confusing with transclusion. – jkinkead Feb 13 '14 at 18:30
1

I have made a plunker demonstrating how I would go about it. I don't know if you need the value of the status of the directive outside the directive itself so I have 2 versions, one takes the on/off value from the controller scope and plays with that and another one that has all the logic inside of it.

The "interface" for the two directives is as follows:

<my-toggle value="isOn" on-url="http://www.w3schools.com/js/pic_bulbon.gif" off-url="http://www.w3schools.com/js/pic_bulboff.gif"></my-toggle>


<my-toggle-internal message="toggle me too" on-url="http://www.w3schools.com/js/pic_bulbon.gif" off-url="http://www.w3schools.com/js/pic_bulboff.gif"></my-toggle>

Here is the plunker http://plnkr.co/edit/1cdHuy

Since I do not know what you have in mind the ideal solution would be I won't go into implementation details until you point me in the right direction so I don't explain things you already know. Feel free to ask anything though.

masimplo
  • 3,674
  • 2
  • 30
  • 47
  • What I have in mind is to become a pro at writing directives. The fact that its a DSL for html has me quite excited. Writing a clean interface that shows intension is super important to me and I just couldn't. This angular community is amazing. What is especially challenging are the many ways to go about solving the same problem. – scalaGirl Feb 13 '14 at 15:54