Tree menu / Collapse with three levels

Asked

Viewed 915 times

1

I’m creating a navigation sidebar for an Angularjs application. The structure is basically a tree menu, but each level has different interactions (which I will still build). For example, the first and second levels will only show and hide the content when clicked, animated and all, while the third, depending on a parameter, will enable or disable a "Collapse" effect. Here is the structure of the menu:

  • Presidente
    • Dilma
    • Aetius
    • Marina
    • Luciana Genro
  • Governor
    • Alckmin
    • Skaf
    • Padilla
  • Senator
  • Deputy
    • Congressman
      • Tirirca
      • Russoman
      • Feliciano
    • State Representative
      • Rooftop
      • Tripoli
      • Capez

Each of these items can open a link or expand the child menu. For example, clicking on "Senator" would go to a link with the composition of the Senate, while the other items would expand the list to another side menu and the child elements of this list would have the effect of "Collapse", when selecting one, hide the other.

This is what I’ve been able to develop so far: http://jsfiddle.net/mata9a1z/

<leftnav pins="true" collapsable="true" labels="true"></leftnav>

I would like to pass the parameter labels to enable icons in the first level, the parameter pins to the second level and collapsable to the third level. This is possible?

The functions select() and isSelected() work, why collapse(), isCollapsed() and foo() do not work in the third level? I had to use the attribute require: '^leftnav' in the directives because otherwise different instances of controllers would be created for each menu item, and so when selecting an option, it would not hide the others. There’s another way to do this?

1 answer

1


It is possible to achieve the desired result through directives, since it is possible to have recursion within directives, using the $compile, that allows a directive to have directives in its template.

its module

angular
  .module('MenuRecursivo', [])
  .directive('candidatos', function () {
    return {
      restrict: 'E',
      replace: true,
      scope: {
        // diz para o Angular criar um novo
        // escopo para a candidatos usando
        // o mesmo nome (candidatos)
        candidatos: '='
      },

      // a diretiva filha `candidato` é usada para tornar possível a recursão
      // note o ng-repeat iterando sobre a `candidatos`
      template: '<ul><candidato ng-repeat=\'candidato in candidatos\' candidato=\'candidato\'></candidato></ul>'
    };
  })
  .directive('candidato', function ($compile) {
    return {
      restrict: 'E',
      replace: true,
      scope: {
        // novamente, diz para o Angular
        // criar um escopo separado com o mesmo nome
        candidato: '='
      },

      // propriedade que será usada no li,
      // que pode variar conforme a necessidade.
      // aqui seria possível até criar uma nova diretiva para
      // cuidar da exibição, como inserir o texto em um <a>
      // ou apenas exibir algum dado do item
      template: '<li>{{candidato.cargo || candidato.name}}</li>',

      link: function (scope, element, attrs) {
        // Prepara o template dos subitens
        var subItems = '<candidatos candidatos=\'candidato.candidatos\'></candidatos>';

        // Identifica se o template deve ser compilado
        // caso este item tenha filhos ou atenda o critério
        // que define que o item tem filhos
        if (angular.isArray(scope.candidato.candidatos)) {
          // compila o template
          $compile(subItems)(scope, function (cloned, scope)   {
            // adiciona o template ao elemento atual,
            // ou seja, neste caso, o <li>
            element.append(cloned);
          });
        }
      }
    };
  })
  .controller('MainCtrl', function ($scope) {
    // suposta estrutura
    $scope.items = [
      {
        cargo: 'Presidente',
        candidatos: [
          { name: 'Dilma' },
          { name: 'Aécio' },
          { name: 'Marina' },
          { name: 'Luciana Genro' }
        ]
      },
      {
        cargo: 'Governador',
        candidatos: [
          { name: 'Alckmin' },
          { name: 'Skaf' },
          { name: 'Padilha' }
        ]
      },
      {
        cargo: 'Senador'
      },
      {
        cargo: 'Deputado',
        candidatos: [
          {
            cargo: 'Deputado Federal',
            candidatos: [
              { name: 'Tirirca' },
              { name: 'Russomano' },
              { name: 'Feliciano' }
            ]
          },
          {
            cargo: 'Deputado Estadual',
            candidatos: [
              { name: 'Telhada' },
              { name: 'Tripoli' },
              { name: 'Capez' }
            ]
          },
          {
            cargo: 'Exemplo de 4 niveis',
            candidatos: [
              {
                cargo: 'Deputado Estadual',
                candidatos: [
                  { name: 'Telhada' },
                  { name: 'Tripoli' },
                  { name: 'Capez' }
                ]
              }
            ]
          }
        ]
      }
    ];
  });

html

<html ng-app="MenuRecursivo">
<head>
  <script src="script.js"></script>
</head>
<body>
  <div ng-controller="MainCtrl">
    <candidatos candidatos='items'></candidatos>
  </div>
</body>
</html>

Plnkr working: http://plnkr.co/edit/D8MZUu6OAzAk1TAMnhwf

The important thing is to consider the following passage:

// ...
if (/* condição necessária para se executar a recursão */) {
  // compila o template
  $compile(subItems)(scope, function (cloned, scope)   {
    // adiciona o template ao elemento atual,
    // ou seja, neste caso, o <li>
    element.append(cloned);
  });
}
// ...

This is the section that performs the recursion of templates. The subItems would be the template for the daughter list. I think it gets brighter by looking at the plunkr.

I believe that from here I can adapt according to your need.

  • Thank you very much! When I asked the question so far this menu has undergone many modifications, I had already managed to make it work but without using $Compile. I’ll try to refactor it now using this method.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.