简体   繁体   中英

ng-show not binding as expected

I'm trying to have a simple 'toggle' on my form. If I click one button, then I should activate a correspondingly-named section, otherwise it should be hidden from view. My issue appears to be that of a scope one. If I don't use an isolated scope on my substeps, then both substeps will appear active on one toggle and inactive on the other (this is incorrect behaviour). If I do use an isolated scope, then isActive() is never called.

My code is as follows.

<div ng-controller='SubstepCtrl'>
    <button activates='CreateNewMeter'>
        Create new Meter
    </button>

    <button activates='UseExistingMeter'>
        Use Existing Meter
    </button>

    <div class='sub-step' substep='CreateNewMeter' ng-show='isActive(name)'>
        <h1>Create New Meter</h1>
    </div>

    <div class='sub-step' substep='UseExistingMeter' ng-show='isActive(name)'>
        <h1>Use Existing Meter</h1>
    </div>
</div>

Angular:

.controller('SubstepCtrl', function($scope) {
    $scope.activeSubstepName = undefined;
    $scope.isActive = function(name) {
        return $scope.activeSubstepName == name;
    };
})

.directive('activates', function() {
    return {
        link: function($scope, $element, $attrs) {
            $element.on('click', function() {
                $scope.activeSubstepName = $attrs.activates;
                $scope.$apply();
            });
        }
    };
})

.directive('substep', function() {
    return {
        link: function($scope, $element, $attrs) {
            $scope.name = $attrs.substep;
        }
    };
});

I have managed to achieve this in a fairly hacky way with JQuery but I was wondering if there was a way to Angular-ify it.

The expected behaviour is that if I click the "Create new Meter" button, that the "CreateNewMeter" substep should be displayed, and the "UseExistingMeter" should not. As I understand the issue here is that the substep divs are not creating a subscope and are both using the parent scope - therefore name is undefined - right?

If so, how can I remedy this?

Try this, which avoids creating your own directive:

<div ng-controller='SubstepCtrl'>
    <button ng-click='setMeter("new")'>
        Create new Meter
    </button>

    <button activates='setMeter("existing")'>
        Use Existing Meter
    </button>

    <div class='sub-step' substep='CreateNewMeter' ng-show='meter === "new"'>
        <h1>Create New Meter</h1>
    </div>

    <div class='sub-step' substep='UseExistingMeter' ng-show='meter === "existing"'>
        <h1>Use Existing Meter</h1>
    </div>
</div>

set a function on your controllers' scope:

.controller('SubstepCtrl', function($scope) {
    $scope.activeSubstepName = undefined;
    $scope.isActive = function(name) {
        return $scope.activeSubstepName == name;
    };
    $scope.meter = null;
    $scope.setMeter = function(meterType) {
        $scope.meter = meterType;
    };
});

Here is a solution in which you create a new directive with it's own isolated scope. In my opinion, this is a better solution, especially since it allows you to have as many sub-steps as you want.

What you need to do when you create your isolate scope in the directive is have a property for both the name and for the isActive function. Since the name is just a string that you have defined in your html, you can specify it as an @ attribute so that is set in your directives scope to the string you specify in your html. I created the function (passing it to the directive using the & syntax) to be called showWhen and if you notice, in order to pass in the parameter name that you specify in your function in the html, you need to pass an object within your directive that uses name as a key with the directives name as the value.

The html:

<div ng-controller='SubstepCtrl'>
    <button activates='CreateNewMeter'>
        Create new Meter
    </button>

    <button activates='UseExistingMeter'>
        Use Existing Meter
    </button>

    <button activates='UseImaginaryMeter'>
        Use Imaginary Meter
    </button>

    <button activates='none'>
        "Clear" all
    </button>

    <substep name="CreateNewMeter" show-when="isActive(name)">
      <h1>Create New Meter</h1>
    </substep>

    <substep name="UseExistingMeter" show-when="isActive(name)">
      <h1>Use Existing Meter</h1>
    </substep>

    <substep name="UseImaginaryMeter" show-when="isActive(name)">
      <h1>Use Imaginary Meter</h1>
    </substep>
</div>

The directive code:

.directive('substep', function() {
    return {
      restrict: 'E',
      scope: {
        name: '@',
        showWhen: '&'
      },
      transclude: true,
      template: '<div ng-transclude class="sub-step" ng-show="showWhen({name:name})"></div>'
    };
});

Here is a sample plunker: http://plnkr.co/edit/TKJehABKIPPHRbrUrqr3?p=preview

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM