1

So I have my SPA application.

Here on plunker (I'll not paste all the code, as it is too much) you can see the it's parts recreation (the structure and the hierarchy are the same).

Where did the problem started... By the design doc, On one of the pages we have a link to a policy documentation and a checkbox for a client to check, that he did read it.

By design:

  1. Checkbox is always enabled, so client could always attempt to check it
  2. After the client opens the policy documentation (click on the link), the checkbox should turn checked when client is clicking on it
  3. If client didn't opened the (clicked) the policy documentation link yet and tries to check the checkbox, it return to be not checked at the moment he releases the mouse, and relevant error message should be shown

UPDATED:

Thanks to Rafaeelloo and Kobi Cohen I could get the code simplified a lot, and to get rid of the watches, and useless stuff...

For now this is what I have...

Here is the directive controller:

app.directive('checkBox', [function() {
  var directive = {
    scope: {}, 
    restrict: 'E', 
    controller: function() {},
    controllerAs: 'checkBoxCtrl',
    bindToController: {
      ngModel: '=',
      callback: '&callback',
      text: '@' 
    },
    templateUrl: 'check-box.directive.view.html',
    link: function() {},
    require: 'ngModel'
  };
  return directive;
}]);

And directive html:

<div class="checkboxContainer">
    <label class="btn btn-checkbox" ng-class="{'active': checkBoxCtrl.ngModel}" 
                                    ng-click="checkBoxCtrl.ngModel = !checkBoxCtrl.ngModel; checkBoxCtrl.callback()">
        <span class="checkboxPic"></span> 
        <span class="checkboxText">{{checkBoxCtrl.text}}</span> 
    </label>
</div> 

Here is view html:

<check-box text="check me"  ng-model="viewCtrl.checkBoxResult" 
                            callback="viewCtrl.callback()"></check-box>
<br/>
<a ng-click="viewCtrl.openGoogleClicked = true" href="//www.google.com" target="_blank">Open Google</a>
 
<hr> 

<h3>Status</h3>  
check box result: {{viewCtrl.checkBoxResult}}    
<br/>
google opened: {{viewCtrl.openGoogleClicked}}
<br/>
callback function called: {{viewCtrl.callbackCalled}}

And this is view controller:

angular.module('app').controller('viewController', ['$scope', function($scope) {
  this.checkBoxResult;
  this.openGoogleClicked;
  this.callbackCalled;
 
  this.callback = function callback() {  
    this.callbackCalled = true;
    if (this.checkBoxResult && angular.isUndefined(this.openGoogleClicked)) {
      this.checkBoxResult = false;
    } 
  };
}]);

The question/problem/opened question: Callback is called before the ngModel of the view controller had been changed. So it doesn't really meter if I change it back to false within the callback, or not, as after a callback the directive (as I understand) still binds the new value to it.

Is there a cure to this?

Community
  • 1
  • 1
neoselcev
  • 138
  • 12
  • 1
    Try to use ngModelController instead of using watch() – be4code Apr 18 '16 at 21:16
  • @Rafaeelloo Can you give me some hint or example (the head just refusing to think clear)? Will really appreciate it! – neoselcev Apr 18 '16 at 21:21
  • 1
    instead of putting it in the scope, try: require: 'ngModel', then you can use it in your link function (scope, element, attrs, controllers) { controllers[0] ... – Kobi Cohen Apr 18 '16 at 21:25
  • @KobiCohen Seems to work without the watch within controller. Can you please explain what is the difference and why it works now? – neoselcev Apr 18 '16 at 21:35
  • 1
    simple, when you require an attribute, you gain access to its controller, in this case the ng-model controller. but if you just expose the value to the scope, then you need to apply the change and notify angular that this value has changed since it is not watched by the ng-model directive and thus doesn't take effect – Kobi Cohen Apr 18 '16 at 21:47
  • @KobiCohen when you require only one controller last attribute is the required controller, I think ;) – be4code Apr 18 '16 at 21:49
  • @Rafaeelloo please check again. It is always an array – Kobi Cohen Apr 18 '16 at 21:50
  • @KobiCohen please take a look here https://docs.angularjs.org/api/ng/type/ngModel.NgModelController If you define `require` as array, last parameter in link function also will be array. – be4code Apr 18 '16 at 21:52
  • @Rafaeelloo I don't find it there. as much as I remeber, it is always an array, but I could be wrong – Kobi Cohen Apr 18 '16 at 21:57
  • @KobiCohen plase check https://docs.angularjs.org/guide/directive last example defines `require` as and `controllers` is array too. Maybe in one version it was defined in way you say. – be4code Apr 18 '16 at 22:00
  • Thank you guys! I would love to vote, if you would take this to an answer! – neoselcev Apr 19 '16 at 16:17
  • @KobiCohen thanks again! I [simplified to directive and the view controller](https://embed.plnkr.co/ryCA4Z/). but right now callback function is fired before the ngmodel within the view controller had changed the value. Any ideas on how can I menage this? – neoselcev Apr 25 '16 at 21:56
  • @Rafaeelloo Thanks a lot! Now I'm having another problem with timings... I ill appreciate if you could see my last comment to KobiCohen. – neoselcev Apr 25 '16 at 21:59
  • 1
    @neoselcev, I am not sure but I think your `callback` can be simplified to `ng-change="callbackFunction()"` and it should work properly and then there is no need to use ng-model on check-box directive. – be4code Apr 26 '16 at 17:11
  • @Rafaeelloo Thanks for the idea! The thing is, that most of the time (at the rest of the website) I use this directive without the `callback` (_in my code it is optional_), as it is not needed. To be correct `callback` was only added because of this screen. So, if this is possible, I would love to leave both the `ngModel` and the `callback` (_optional_), and some how to get them work together. Any ideas, how I can accomplish that or why it doesn't work as I need it to? – neoselcev Apr 26 '16 at 19:20
  • 1
    @neoselcev if I understand correctly: you need to execute function (called `callback` here) when model value change? If true then the best solution is using `ng-change`. I'm trying to show you that `callback` function is overkill here if I understand you correctly. – be4code Apr 26 '16 at 19:26
  • @Rafaeelloo About the callback: as I understand, `ng-change` on `directive` won't work, if `ctrl.$setViewValue` is not called within `link` function, right? About **what is needed**: not just a `callback`... `callback` has to check, if link within the view was clicked already, and if not, `ng-model` of the directive has to be set to false. – neoselcev Apr 26 '16 at 20:22
  • @Rafaeelloo To be more accurate: If I understand you right [this is what I did](https://embed.plnkr.co/PAdpDI/). Though, `ng-change` on `directive` didn't work, till `ctrl.$setViewValue` wasn't called within `link` function... Did I understood you right? Anyway `ng-model` still can be set to `true` before the link is clicked, as `directive` binds a new value, after `on-change` (see the console). **The requirement:** `ng-model` within the view can't be set to true, if link wasn't clicked yet (`derective` should allow to be clicked though, but `on-change` should set `ng-model` to false). – neoselcev Apr 26 '16 at 20:40
  • 1
    @neoselcev if you're using `require` you need to use `$setViewValue()` but your code is pretty messed up but I don't have enough time to show you. You may see this: [link](https://github.com/sebastianha/angular-bootstrap-checkbox) as it is quite similar to yours. – be4code Apr 26 '16 at 22:28
  • @Rafaeelloo Thank! [I got the idea](http://embed.plnkr.co/NGJkLs/)! If you would take this to an answer, I would love to upvote! – neoselcev Apr 27 '16 at 06:06
  • 1
    @neoselcev please also take a look on `ngModelController.$validators`or `$parsers` and `$formatters` because I think it also could be useful for your application. – be4code Apr 27 '16 at 06:13

1 Answers1

2

Depending on what you're trying to do, you may do it like here (here) or you can write validator that has function to determine validity (here is example of writing your own validator) then your form and checkbox will have invalid what is in my opinion better than disallowing user to check the checkbox

Community
  • 1
  • 1
be4code
  • 688
  • 4
  • 15
  • Thank again! BTW I agree with you that validator could be better here, but it was the client's requirement, so I can't change it... – neoselcev Apr 27 '16 at 06:23