简体   繁体   中英

AngularJS: directive's template is not rendering correctly

I have a really strange behavior in a directive I wrote for a dropdown replacement. the model used can be nested infinitely using child nodes. the directive is included like this (HAML-Code):

.control-group
  -# use the angular directive "dropdown-tree" that can handle unlimited nested models!
  .controls{ "dropdown-tree" => "", "ng-model" => "categories_as_tree", "current-value" => "currentCategory", "dropdown-placeholder" => "choose category", "ng-disabled" => "!categories || categories.length==0", "on-change"=>"fetchProducts(category)" }

%h4 Products
.control-group
  .controls{ "dropdown-tree" => "", "ng-model" => "products_as_tree", "current-value" => "currentProduct", "dropdown-placeholder" => "choose product", "ng-disabled" => "!products || products.length==0" }

As I encountered so far, the model is behaving absolulately correctly! in debug view or via console.log or $log whatever, but the rendering of the directive gets messed up showing the items doubled or even multiplied, dependent on how many times you switch the dropdown.

the coffee script looks like this, which is easy code:

# use array -> being resistend against uglifiers mangle
mymodule.directive 'dropdownTree', [ ->
  return {
    templateUrl: '/angular/templates/dropdown_tree'
    ,
    #require: 'ngModel', # don't need that so far, we keep things simple!
    scope: {
      ngModel: '=',
      dropdownPlaceholder: '@',
      currentValue: '=',
      ngDisabled: '=',
      onChange: '&'
    },
    link: (scope, element, attr, ngModelCtrl) ->

      # fake the ng-change
      scope.$watch('currentValue', ->
        scope.onChange()
      , true)

      scope.selectValue = (value) ->
        scope.currentValue = value

  }
]

the view template code of the directive gets delivered by an rails controller it is written in HAML and looks like this. it uses infinite nesting if the child nodes exist:

.btn-group{ :name => "FIXME", "ng-required" => "true" }
  %a.btn.btn-default.dropdown-toggle{ "data-toggle" => "dropdown", href: "#", "ng-disabled"=>"ngDisabled" }
    {{currentValue.name || dropdownPlaceholder }}
    %span.caret
  %ul.dropdown-menu{ "ng-show" => "!ngDisabled" }
    %div{ "ng-repeat" => "model in ngModel", "ng-include" => "'node.html'" }

%script{ :type => "text/ng-template", :id => "node.html" }
  %li{ "ng-click" => "selectValue(model)" }
    %a
      {{model.name}}
  %ul{ "ng-repeat" => "model in model.children", :style => "margin-left: 10px;" }
    %div{ "ng-include" => "'node.html'" }

I did not have any problems with this only the one the second dropdown does not update its view correctly, the model does. I wonder if you can help or see something which is no legitimate.

kind regards, Alex

If you have nested ng-repeats within each other maybe the reason it's rendering strangely is that you are using the same names for the repeats within each other. If you do this you essentially override the reference as you progress down the DOM tree.

EDIT: Modified plunker ( http://plnkr.co/edit/h6XdT5w0JRlVIQfxjByn?p=preview ) with data displayed in a dropdown menu with sub menus (using bootstrap 2.3.2 and angular-ui 0.5.0).

Following my comment on your question:

I did some correction on how the html tags are nested to obtain valid html. I took your source from a plunker you mentionned in another SO question ( angularjs: force re-rendering/ full refresh a directive template ).

Here is a live demo: http://plnkr.co/edit/JTt3moub2haMGxH65YtK?p=preview

I also took aside the node.html template in its own file instead of the script notation that can cause problems if not used at the right place. When you click on an item, it is correctly set as the current value.

template.html

<ul class='dropdown-menu' ng-show='!ngDisabled'>
    <li ng-include="'node.html'" ng-repeat='model in ngModel'></li>
  </ul>

node.html

<a href="#" ng-click='selectValue(model)'>{{model.name}}</a>
<ul>
  <li ng-include="'node.html'" ng-repeat='model in model.children'></li>
</ul>

hierarchical data

$scope.products = [
  {name: 'Product1', children: [
    {name:'Product1.1', children: [
      {name:'Product1.1.1'}
    ]},
    {name:'Product1.2', children: [
      {name:'Product1.2.1'},
      {name:'Product1.2.2'},
      {name:'Product1.2.3', children: [{name:'Product1.2.3.1'}]},
      {name:'Product1.2.4'}
    ]}
  ]},
  {name: 'Product2', children: [
    {name:'Product2.1', children: [
      {name:'Product2.1.1'},
      {name:'Product2.1.2'}
    ]},{name:'Product2.2', children: [
      {name:'Product2.2.1'},
      {name:'Product2.2.2'},
      {name:'Product2.2.3'}
    ]}
  ]}
];

Look how I removed the div that should not be nested inside an ul element.

the issue is that ng-include does not update. if using with ng-if (adding one more element) it works!

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