Angularjs ng-model ne fonctionne pas à l'intérieur de ng-if

206

Voici le violon montrant le problème. http://jsfiddle.net/Erk4V/1/

Il apparaît que si j'ai un modèle ng à l'intérieur d'un ng-if, le modèle ne fonctionne pas comme prévu.

Je me demande s'il s'agit d'un bug ou si je ne comprends pas bien l'utilisation appropriée.

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="testb" />
        </div>
        <div ng-if="!someothervar">
            testc (with ng-if): <input type="checkbox" ng-model="testc" />
        </div>

    </div>
</div>
Justin Carlson
la source
6
Pour une solution de contournement, vous pouvez utiliser ng-show = "CONDITION" au lieu de ng-if. Ça devrait marcher.
Hari Das
Je suppose que ce n'est plus un problème maintenant que l'on peut utiliser controllerAs?
jamiebarrow
J'ai eu le même problème lors de l'utilisation d'une directive avec implicite scope:falseet j'ai ajouté un ng-ifélément autour de la directive - les étendues étaient initialement liées, mais elles se sont séparées après qu'un observateur a mis à jour l'une des valeurs de portée ...
Aprillion

Réponses:

223

La ng-ifdirective, comme les autres directives, crée une portée enfant. Voir le script ci-dessous (ou ce jsfiddle )

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

<script>
    function main($scope) {
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.obj = {test: false};
    }
</script>

<div ng-app >
    <div ng-controller="main">
        
        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        {{obj.test}}
        
        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="testb" /> {{testb}}
        </div>
        <div ng-if="!someothervar">
            testc (with ng-if): <input type="checkbox" ng-model="testc" />
        </div>
        <div ng-if="!someothervar">
            object (with ng-if): <input type="checkbox" ng-model="obj.test" />
        </div>
        
    </div>
</div>

Ainsi, votre case à cocher modifie l' testbintérieur de la portée enfant, mais pas la portée parent externe.

Notez que si vous souhaitez modifier les données dans la portée parent, vous devrez modifier les propriétés internes d'un objet comme dans la dernière div que j'ai ajoutée.

Jon7
la source
1
Comment pourrais-je accéder à la portée du ng-if depuis la fonction des contrôleurs principaux? Un peu frustrant. Quelle en est la raison?
Justin Carlson
Peu importe, @sza vient de répondre à cette ^ question. Je vais marquer cette réponse comme correcte, car elle explique la raison exacte pour laquelle j'ai eu des problèmes.
Justin Carlson
21
C'est une des raisons pour lesquelles il est assez courant d'utiliser un objet conteneur sur votre portée, plutôt que de modifier directement les propriétés de la portée, comme indiqué dans l'exemple: $scope.obj = {...}et ng-model="obj.someProperty"surmonte cette limitation.
wulftone
204

Vous pouvez utiliser $parentpour faire référence au modèle défini dans la portée parent comme ceci

<input type="checkbox" ng-model="$parent.testb" />
zs2020
la source
16
alors je l'ai ng-model="$parent.$parent.fooparce que je suis déjà dans une lunette avec un ng-repeat- est-ce vraiment la meilleure façon?
chovy
4
c'est vraiment déroutant. pourquoi donc? et pourquoi ng-hide n'a-t-il pas sa propre portée?
Dominik Goltermann
5
Re @Gaul: probablement parce que ng-hide / ng-show opère sur le DOM actuel et ajoute / supprime simplement une classe CSS, tandis que ng-if / ng-switch / ng-repeat répète tout le muck avec le DOM et garde une trace de l'état supplémentaire . Semble raisonnable.
trisweb
4
Sensible n'est pas le mot que j'utilise.
Jonathan Dumaine
3
Ajoutez un objet à la portée d'origine et modifiez les propriétés de cet objet. par exemple ng-model = "myObject.property". Cela contournera toute l'inanité portée / $ parent. Google "règle de point angulaire" pour plus d'informations.
Asmor
50

Vous pouvez utiliser la directive ngHide (ou ngShow) . Il ne crée pas de portée enfant comme le fait ngIf.

<div ng-hide="testa">
Vasiliy Kevroletin
la source
3
Pourquoi crée-t-il ngIfalors une portée enfant? Cela me semble très étrange.
freeall
5
Faites attention aux commentaires de la réponse zsong. ng-hidene change pas la structure html. Il change simplement les styles CSS. ng-ifest plus complexe: il supprime et insère des parties html selon l'état. Il crée une portée enfant pour stocker l'état (au moins, il doit stocker une partie html cachée).
Vasiliy Kevrolet dans le
Ouaip, travaillez pour moi
Basit
7

Nous l'avons eu dans de nombreux autres cas, ce que nous avons décidé en interne est de toujours avoir un wrapper pour le contrôleur / directive afin que nous n'ayons pas besoin d'y penser. Voici votre exemple avec notre wrapper.

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

<script>
    function main($scope) {
        $scope.thisScope = $scope;
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.testd = false;
    }
</script>

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        Test D: {{testd}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="thisScope.testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="thisScope.testb" />
        </div>
        <div ng-show="!testa">
            testc (with ng-show): <input type="checkbox" ng-model="thisScope.testc" />
        </div>
        <div ng-hide="testa">
            testd (with ng-hide): <input type="checkbox" ng-model="thisScope.testd" />
        </div>

    </div>
</div>

J'espère que cela aide, Yishay

Yishay Haspel
la source
3

Oui, la directive ng-hide (ou ng-show) ne créera pas de portée enfant.

Voici ma pratique:

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

<script>
    function main($scope) {
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.testd = false;
    }
</script>

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        Test D: {{testd}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="$parent.testb" />
        </div>
        <div ng-show="!testa">
            testc (with ng-show): <input type="checkbox" ng-model="testc" />
        </div>
        <div ng-hide="testa">
            testd (with ng-hide): <input type="checkbox" ng-model="testd" />
        </div>

    </div>
</div>

http://jsfiddle.net/bn64Lrzu/

Xiayan Y
la source
0

Vous pouvez le faire comme ça et votre fonction de mod fonctionnera parfaitement, faites-moi savoir si vous voulez un stylo code

  <div ng-repeat="icon in icons">                   
                <div class="row" ng-if="$index % 3 == 0 ">
                    <i class="col col-33 {{icons[$index + n].icon}} custom-icon"></i>
                    <i class="col col-33 {{icons[$index + n + 1].icon}} custom-icon"></i>
                    <i class="col col-33 {{icons[$index + n + 2].icon}} custom-icon"></i>
                </div>
         </div>
Xvegas
la source