0

I've noticed two instances in my code where using ng-if causes my program to start working. On one, I do an ng-if="isOwnProfile", for an image-upload toolbar.

Using the ng-if causes the event listener to stop working. Code example:

   $scope.myOwnProfile = false;
   if (userLoggedIn === userUID) {
     console.log('my images');
     $scope.myOwnProfile = true;
   } else {
     console.log('not my images');
   }
    $("#image-upload").change(function(e) {
    $scope.myOwnProfile = true;
    var file = e.target.files[0];
    var imageRef = firebase.storage().ref...

and in HTML:

<section ng-if="myOwnProfile">
   <input id="image-upload" type="file" accept="image/*"> <br /><br />
</section>

In this case the event listener will stop working and not respond. Another case is where you add a message to the page (and firebase). Code:

    $scope.addMessage = function(){
    var date = new Date();
    $scope.messages.$add({
        timestamp: (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear(),
        from: $scope.visitorRealName.name,
        content: $scope.message
    });
    $scope.message = '';
};

HTML:

    <section ng-if="AreWeFriends === true || myOwnProfile === true">

    <form ng-submit="addMessage()">
        <input ng-model="message"> 
        <button type="submit">Add Message</button>
    </form>

    </section>

In the second case I get an error from Firebase "Key content was undefined. Cannot pass undefined in JSON. Use null instead".

I can't determine why using ng-if causes this to happen? What I do is set the user's profile to true whether they are a) a friend or b) it's the person's own profile (which is why I change $scope).

AL.
  • 36,815
  • 10
  • 142
  • 281
  • 1
    See [Upload a file from a local directory using Angular 1.6.2](http://stackoverflow.com/questions/42880340/upload-a-file-from-a-local-directory-using-angular-1-6-2/) for advice on uploading without using jQuery. – georgeawg Mar 19 '17 at 15:09
  • 1
    *New AngularJS developers often do not realize that `ng-repeat`, `ng-switch`, `ng-view`, `ng-include` and `**ng-if**` all create new child scopes, so the problem often shows up when these directives are involved.* -- [AngularJS Wiki - Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) – georgeawg Mar 19 '17 at 15:11
  • 1
    With such a small section it is better to use the [ng-show directive](https://docs.angularjs.org/api/ng/directive/ngShow) as it uses CSS instead of creating scopes to add/remove DOM. – georgeawg Mar 19 '17 at 15:14
  • George: I do have additionally code to upload to FireBase Storage I just didn't include it, looking into ng-show/ng-hide now. The reason I didn't use it initially was because I thought it would be easier to inject code since you're just hiding classes with CSS – nashvilleCoder Mar 19 '17 at 15:40
  • 1
    Adding event listeners should be done with a custom directive so that they are created and destroyed properly inside `ng-repeat`, `ng-switch`, `ng-view`, `ng-include` and **ng-if**. See [AngularJS Developer Guide - Creating Custom Directives](https://docs.angularjs.org/guide/directive). – georgeawg Mar 19 '17 at 15:42
  • 1
    AngularJS has its own DOM manipulation library called [jqLite](https://docs.angularjs.org/api/ng/function/angular.element#angularjs-s-jqlite). It is better to use that together with custom directives. Using jQuery without using custom directives has its hazards. – georgeawg Mar 19 '17 at 15:51
  • 1
    @nashvilleCoder Does my answer answer your question to **"why using ng-if causes this to happen?"**? – cnorthfield Mar 19 '17 at 15:52
  • Possible duplicate of [What are the nuances of scope prototypal / prototypical inheritance in AngularJS?](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs) – georgeawg Mar 19 '17 at 15:56
  • Yes, thank you very much cnorthfield, georgeawg (again!)!! – nashvilleCoder Mar 19 '17 at 16:17

1 Answers1

1

That's because the ngIf directive creates it's own child scope.

The ngIf directive creates it's own scope, so the ngModel directive containing message is set on the scope created by the ngIf directive, not your controller.

When you access the value in your controller it's not there, so it's undefined. You are essentially passing undefined to your content key inside your object you're adding to your messages so Firebase complains.

To fix it i'd recommend using the controller as syntax so that you can reference the controller, or use the ngShow directive, which doesn't create it's own child scope.

Here are a few examples of what happens:

(function() {

  'use strict';

  angular.module('app', []);

})();

(function() {

  'use strict';

  angular.module('app').controller('MainController', MainController);

  MainController.$inject = ['$scope'];

  function MainController($scope) {

    var vm = this;

    $scope.test1 = test1;
    $scope.test2 = test2;
    $scope.test3 = test3;
    $scope.test4 = test4;

    function test1() {

      // this is undefined because there is no property `message` 
      // on the $scope of this controller
      alert($scope.message);

    }

    function test2() {

      // this contains the value binded to the message property 
      // of this controller because we used the controller as syntax
      alert(vm.message);

    }
    
    function test3(message) {

      // because we are passing in the message value we can
      // access it without caring where it came from
      alert(message);

    }
    
    function test4() {

      // the property `message` exists on this $scope because we
      // used the ngShow directive instead of the ngIf
      alert($scope.message4);

    }

  }

})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>


<div ng-app="app" ng-controller="MainController as MainCtrl">

  <h4>Without controller as syntax</h4>
  <p>Does not work because message is not set on the controller's $scope, it's set on the scope created by the ngIf directive</p>
  <form ng-if="true">
    <input ng-model="message">
    <button ng-click="test1()">Submit</button>
  </form>
  
  <hr>

  <h4>With controller as syntax</h4>
  <p>Works because message is set on the controller so we can access it using `this` in our MainController</p>
  <form ng-if="true">
    <input ng-model="MainCtrl.message">
    <button ng-click="test2()">Submit</button>
  </form>

  <hr>
  
  <h4>Without controller as syntax but passing the message value into our function</h4>
  <p>Works because although message is set on the scope created by the ngIf directive, we are passing it to test3 in our MainController.</p>
  <form ng-if="true">
    <input ng-model="message">
    <button ng-click="test3(message)">Submit</button>
  </form>
  
  <hr>
  
  <h4>With ngShow directive instead of ngIf</h4>
  <p>Works because message is set on $scope from our contoller</p>
  <form ng-show="true">
    <input ng-model="message4">
    <button ng-click="test4()">Submit</button>
  </form>

</div>
cnorthfield
  • 3,384
  • 15
  • 22