0

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

3
  • Just curious, why do you use on-change instead of ng-change? Commented Oct 12, 2013 at 22:38
  • I am not familiar with HAML, but following your indentation, this is my understanding: You first loop on div for your first level inside a ul, and inside the div you put li. On the sublevels you loop on ul that are siblings of the li and you use div inside the ul to start the next sublevel... As I see it your hierarchy looks like this: ul>div[loop]>[li>a|ul[loop]>div>[li>a|ul[loop]>div.... AFAIK, this is not valid HTML. The only valid element in a ul is a li. Commented Oct 12, 2013 at 22:51
  • I did use on-change because the value is read by the directive as callback getting called in a custom scope.$watch... I did not want to mix up ng-change for other users of the directive, because ng-change is native directive... yep, I know it is bad html. the reason for this was adding some blocks beeing able to receive the correct item. I am going to check your post below. thank you so much. Commented Oct 13, 2013 at 1:29

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi, that's doing the trick for infinite recursion - actually, it worked so far as non-directive code. thank you.
1

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.

7 Comments

I think that you should not use the attribute name ng-model if you are not using it like it should be in your directive. Try naming it something else, it might help avoid weird bugs.
Hi. I just checked and have seen sadly with your solution it behaves still same - the view won't update in a right way. dl.dropboxusercontent.com/u/21600359/… thank you so much, really for your help.
the reason why my html was so screwed up is that the element the ng-click will watch on needs to be isolated and not being nested withing another ng-clicks to prevent many gets fired the same time. (angular sometimes really confronts w3c specs to achieve things. we should need comments for that don't know if it is available already) here is a screenshot of the second problem dl.dropboxusercontent.com/u/21600359/…
Is your ng-click on the a element or the li? It will do that if it is on the li since the elements are nested.
I added a new plunker with more modifications and it displays the data in a real dropdown menu with sub menus.
|
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.