AngularJS ng-model $scope in ng-repeat is undefined

后端 未结 4 1921
有刺的猬
有刺的猬 2020-12-04 18:24

I apologize in advance if i\'m not wording this properly. I have a textbox with ng-model inside an ng-repeat and when I try to get the textbox valu

4条回答
  •  星月不相逢
    2020-12-04 19:09

    As @Gloopy already stated, ng-repeat creates a new child scope for each item in your posts array. Since each item of the posts array is a primitive (a string), ng-repeat also creates a post property on each child scope, and assigns each of them the appropriate value from the array. Inside the ng-repeat block is ng-model="postText". This creates a postText property on each of the child scopes. Here is what that all looks like (for 2 of the 4 child scopes):

    ng-repeat scopes

    When a user types some text into one of the input textboxes, the appropriate gray box will store the text. (E.g., the 2nd (from the top) gray box will store text a user types into the "tech" textbox.) The parent scope cannot see the postText properties in the child scope -- this is the problem you had. There are three common solutions:

    1. @Gloopy's answer: define a function on the parent scope (which the child scopes can access, because ng-repeat child scopes prototypically inherit from the parent scope) and pass the child scope property value (i.e., postText's value) up to the parent.
    2. Use objects instead of primitives in your posts array. E.g.,
      $scope.posts = [ {type: 'tech'}, {type: 'news'}, ...];
      Then in your ng-repeat loop, use

      Because each array item is an object (and not a primitive), each child scope gets a reference to the appropriate object in array posts, rather than a copy (of a value). Therefore, post.postText gets created on parent $scope property posts, and hence it is visible to the parent scope. (So, in this case the child scopes would simply call savePost() -- there would be no need to pass any values up to the parent scope.)
      In other words, if a user typed "this is tech related" into the first text box, the posts array in the parent would be automatically updated as follows:
      $scope.posts = [ {type: 'tech', postText: 'this is tech related'}, {type: 'news'}, ...];
      One last note: the postText property is not added to the posts object until the user types something.
    3. Use ng-model="$parent.someProperty" to bind the form element to a property on the parent scope, rather than on the child scope. This solution would be difficult to implement for your scenario, and it is a rather fragile solution, as it depends on the HTML structure for scope inheritance... but I mention it for completeness.

    (A fourth solution was presented by @Renan in comments on @Gloopy's answer. This is a like solution 1., but with a variation: this is used instead of passing a value up to the parent. I'm not a fan of this approach as it makes it difficult to determine which $scope is being accessed or modified. I think it is better that functions defined on $scope only access and modify their own $scope.)

    For more information (and lots more pictures) about how prototypal scope inheritance works in Angular, see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

提交回复
热议问题