Tree menu / collapse with three levels

1

I'm creating a navigation sidebar for an application in AngularJS. The structure is basically a tree menu, however 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, with animation and all, while third, depending on a parameter, will enable or disable a collapse effect. Here is the structure of the menu:

  • President
    • Dilma
    • Aetius
    • Marina
    • Luciana Genro
  • Governor
    • Alckmin
    • Skaf
    • Padilla
  • Senator
  • Member
    • Federal Deputy
      • Tirirca
      • Russomano
      • Feliciano
    • State Representative
      • Tiled
      • Tripoli
      • Capez

Each of these items can open a link or expand the child menu. For example, clicking "Senator" would link to the senate composition, 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 have been able to develop so far: link

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

I would like to pass the labels parameter to enable icons on the first level, the pins parameter for the second level and collapsable for the third level. Is this possible?

The select() and isSelected() functions work, why collapse() , isCollapsed() , and foo() do not work on the third level? I had to use the require: '^leftnav' attribute in the directives because otherwise you would create different instances of controllers for each menu item, so when you select one option, you would not hide the others. Is there another way to do this?

    
asked by anonymous 06.10.2014 / 19:12

1 answer

1

You can achieve the desired result through directives, as recursion can be achieved within policies by using $compile , which allows a policy to have policies in its template.

Your 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: link

The important thing to note is the following:

// ...
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 recurses templates. subItems would be the template for the child list. I think it gets clearer by looking at plunkr.

I think that from here you can adapt as needed.

    
17.12.2014 / 23:03