Ellone
Ellone

Reputation: 3898

Generating HTML list from JSON array

Using angularJS I'm trying to build a list that will be used as a menu. I have an array of JSON and the menu must be built depending on conditions on JSON fields :

In my controller, for now, I have this :

Getting the JSON :

$scope.cartoList = [];
$http.get('frList.json')
.success(function(data) { 
    $scope.cartoList = data;
    $scope.buildMenu();
})
.error(function(data) { 
    console.log("Error while getting json.");
})

Initialize the menu list :

    $scope.initMenu = function () {
    for (var i = 0; i < $scope.cartoList.length; i++) {
        if ($scope.cartoList[i].informationSystem != "" && $scope.ISList.indexOf($scope.cartoList[i].informationSystem) === -1)
            $scope.ISList.push($scope.cartoList[i].informationSystem);
        if ($scope.cartoList[i].macroProcess != "" && $scope.macroProcessList.indexOf($scope.cartoList[i].macroProcess) === -1)
            $scope.macroProcessList.push($scope.cartoList[i].macroProcess);
    }
}

JSON Sample :

[
{   "area" : "Middle",
    "block" : "Position",
    "created" : "2015-6-15",
    "defaultZoom" : "1",
    "displayName" : "Architecture",
    "fileName" : "AA_APK",
    "informationSystem" : "A Group",
    "lastupdate" : "2015-6-15",
    "level" : "Block",
    "macroProcess" : "",
    "type" : "AA"
  },
  { "area" : "",
"block" : "",
"created" : "2015-6-15",
"defaultZoom" : "1",
"displayName" : "Processus order VM",
"fileName" : "AM_Process_order_VM",
"informationSystem" : "A Group",
"lastupdate" : "2015-6-15",
"level" : "",
"macroProcess" : "Deal period",
"type" : "AM"
 },
 ...
]

HTML :

<ul class="sidebar-menu slimscroll">
<li class="treeview" ng-repeat="IS in ISList"><a href="javascript:void(0)"> <i class="fa fa-folder"></i>{{IS}} <i class="fa fa-angle-left pull-right"></i></a>
        <ul class="treeview-menu">

          <li class="treeview"><a href="javascript:void(0)"> <i class="fa fa-folder"></i>Processus<i class="fa fa-angle-left pull-right"></i></a>
            <ul class="treeview-menu">
              <li class="treeview" ng-repeat="mProcess in macroProcessList"><a href="javascript:void(0)"><i class="fa fa-folder"></i>{{mProcess}}<i class="fa fa-angle-left pull-right"></i></a>
                <ul class="treeview-menu">
                  <li ng-repeat="carto in cartoList" ng-if="carto.type == 'AM' && carto.macroProcess == mProcess && carto.informationSystem == IS"><a href="javascript:void(0)" ng-click="changeSVG(carto.fileName)"><i class="fa fa-sitemap"></i>{{carto.displayName}}</a></li>
                </ul>
              </li>
            </ul>
          </li>

          <!-- Other <li> will come here -->

        </ul>
</li>
</ul>

The problem is the menu doesn't drop down when I click on my IS elements.

The treeview and treeview-menu comes from the template I'm using that you can find here : https://almsaeedstudio.com/themes/AdminLTE/index.html

But I don't think this is related.

EDIT : The menu dropdown is handled by JQuery, so it might be related. Maybe the event is not catched because of some conflict between JQuery and angular ?

This should be the relevant part :

$.AdminLTE.tree = function (menu) {
var _this = this;

$("li a", $(menu)).on('click', function (e) {
  //Get the clicked link and the next element
  var $this = $(this);
  var checkElement = $this.next();

  //Check if the next element is a menu and is visible
  if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible'))) {
    //Close the menu
    checkElement.slideUp('normal', function () {
      checkElement.removeClass('menu-open');
      //Fix the layout in case the sidebar stretches over the height of the window
      //_this.layout.fix();
    });
    checkElement.parent("li").removeClass("active");
  }
  //If the menu is not visible
  else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
    //Get the parent menu
    var parent = $this.parents('ul').first();
    //Close all open menus within the parent
    var ul = parent.find('ul:visible').slideUp('normal');
    //Remove the menu-open class from the parent
    ul.removeClass('menu-open');
    //Get the parent li
    var parent_li = $this.parent("li");

    //Open the target menu and add the menu-open class
    checkElement.slideDown('normal', function () {
      //Add the class active to the parent li
      checkElement.addClass('menu-open');
      parent.find('li.active').removeClass('active');
      parent_li.addClass('active');
      //Fix the layout in case the sidebar stretches over the height of the window
      _this.layout.fix();
    });
  }
  //if this isn't a link, prevent the page from being redirected
  if (checkElement.is('.treeview-menu')) {
    e.preventDefault();
  }
});
};

Upvotes: 0

Views: 511

Answers (2)

Ellone
Ellone

Reputation: 3898

So yeah, the issue came from jQuery handling some event when angular should have.

To fix it, you can add ng-click="toggleMenu($event)" to the <a> inside a <li class="treeview"...

And then add in controller :

    $scope.toggleMenu = function (e) {
    var elem = angular.element(e.currentTarget);
    var $this = elem ;
    var checkElement = $this.next();

    //Check if the next element is a menu and is visible
    if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible'))) {
    //Close the menu
    checkElement.slideUp('normal', function () {
        checkElement.removeClass('menu-open');
        //Fix the layout in case the sidebar stretches over the height of the window
        //_this.layout.fix();
    });
    checkElement.parent("li").removeClass("active");
    }
    //If the menu is not visible
    else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
        //Get the parent menu
        var parent = $this.parents('ul').first();
        //Close all open menus within the parent
        var ul = parent.find('ul:visible').slideUp('normal');
        //Remove the menu-open class from the parent
        ul.removeClass('menu-open');
        //Get the parent li
        var parent_li = $this.parent("li");

        //Open the target menu and add the menu-open class
        checkElement.slideDown('normal', function () {
        //Add the class active to the parent li
        checkElement.addClass('menu-open');
        parent.find('li.active').removeClass('active');
        parent_li.addClass('active');
        //Fix the layout in case the sidebar stretches over the height of the window
        //  _this.layout.fix();
        });
    }
    //if this isn't a link, prevent the page from being redirected
    if (checkElement.is('.treeview-menu')) {
        e.preventDefault();
    }
}

Upvotes: 0

Gabriel Pires
Gabriel Pires

Reputation: 366

I think you are having a little misconception about angularJS and what it can do for you. You are taking a long path to have your menu shown, when it should make your life easier instead, and I think I might clarify what angular can do for so in order to get your menu list going =).

AngularJS has a ng-repeat attribute, that allows you to access arrays in your controller and display something for each element of it. Like:

<ul>
    <li ng-repeat="element in list">
        <div class="{{element}}">{{element}}</div>
    </li>
</ul>

This makes you create a li element for every element in the list variable in the scope.

Using this kind of feature lets you see your menu much earlier in the development stage. From what I see in your scenario, you have two possible outcomes for every element: it is either a menu or not, and the element changes according to it. Check out this fiddle to get a hold of what you can do:

http://jsfiddle.net/4xkvq7dj/1/

This example is simple enough so you understand the basics of what you can do. From there on is a matter of finding out what you want to accomplish. Building a menu in the controller using the DOM isn't the angular way, and it really gets messy fast. You can achieve a lot of things using ng-repeat and other angular directives in the HTML, so the view logic stays in the view =).

Better of luck with your project!

Upvotes: 1

Related Questions