1

I'm transitioning from NGJS to NG and trying to recode my previous application to practice.

I stumbled upon the new NgInit where initializations are done in Angular's Component.

What I'm trying to achieve is to initialize a value WITHIN the scope to be used as a toggle to hide and unhide HTML elements. I'm trying to solve this without looping within ngOnInit() {} to initialize for each object within the array. (See ng-init in ng-repeat block)

Below is a working copy of the scenario I'm trying to achieve:

angular.module("app", [])

.controller("controller", function($scope) {
  
  $scope.init = function() {
    $scope.modules = [
      {
        label: 'Module A',
        children: [
          'Module A - 1',
          'Module A - 2',
          'Module A - 3'
        ]
      },
      {
        label: 'Module B',
        children: [
          'Module B - 1',
          'Module B - 2',
          'Module B - 3'
        ]
      },
      {
        label: 'Module C',
        children: [
          'Module C - 1',
          'Module C - 2',
          'Module C - 3'
        ]
      }
    ];
  };
  
});
.child {
  padding-left: 24px;
  padding-top: 8px;
  padding-bottom: 8px;
}

.parent {
  padding: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<body ng-app="app" ng-controller="controller" ng-init="init()">
  <div class="parent" ng-repeat="module in modules" ng-init="toggle=true">
    {{module.label}}
    <button ng-click="toggle = !toggle">toggle</button>
    <span ng-if="toggle" id="child-group">
      <div class="child" ng-repeat="child in module.children">
        {{child}}
      </div>
    </span>
  </div>
</body>

Here's a Plunker if you prefer: https://plnkr.co/edit/JDBBPLkr21wxSe2dlRBv?p=preview

Michael 'Maik' Ardan
  • 4,213
  • 9
  • 37
  • 60
  • You can "initialize" a variable, when you defined it, in constructor, or in ngOnInit function (your component must be implements OnInit). Generally is recomended make in ngOnInit – Eliseo Mar 22 '18 at 08:01
  • Yeah. But unfortunately, `NgOnInit` behaves differently from the previous `ng-init`. The initialized `toggle` is accessible only within the scope of initialization. Another thing is if I initialized the toggle in constructor, I have to define 1 boolean for each Parent which you don't want to do specially if you don't have any idea how many "modules" (in this case) the API gateway will respond. – Michael 'Maik' Ardan Mar 22 '18 at 08:07
  • In Angular js (as I remember it) $scope is "only". In Angular, if you want to mantein the value of a "variable" along the app life, you must use a "service" to save the variable, see, e.g. https://stackoverflow.com/questions/34572005/persisting-and-accessing-values-globally-in-multiple-components-in-angular-2 – Eliseo Mar 22 '18 at 08:13

3 Answers3

3

You could do it like this. You loop over your array with *ngFor. The button toggles the corresponding boolean value, which defines if your element is shown or not (with the *ngIf directive)

@Component({
     selector: 'my-app',
     template: `
         <div *ngFor="let module of modules; let i = index">
         <button (click)="show[i] = !show[i]">toggle</button>
         <h2>{{module.label}}</h2>
         <div *ngIf="show[i]">
            <li *ngFor="let child of module.children">
             {{child}}
          </li>
         </div>
         </div>             
    `,
})

Then initialize your variables:

export class AppComponent {     
  modules:any[];
  show:boolean[];

  constructor() {
    this.modules = [
    {
       label: 'Module A',
       children: [
      'Module A - 1',
      'Module A - 2',
      'Module A - 3'
    ]
    },
    {
      label: 'Module B',
      children: [
      'Module B - 1',
      'Module B - 2',
      'Module B - 3'
    ]
    },
    {
      label: 'Module C',
      children: [
      'Module C - 1',
      'Module C - 2',
      'Module C - 3'
    ]
    }
    ];
      this.show = this.modules.map(()=>true);
   }
 }
opp
  • 1,010
  • 10
  • 17
2

Implement OnInit while declaring the component's class and move your initialization code to ngOnInit function.

@Component({
  ...
})
export class componentClass implements OnInit {
  ...

  ngOnInit() {
    // initialization code block
  }
}

Mention that Angular(Version2+) provides life hook for a component from been created to been destroyed.


For ng-init at ng-repeat part, From Angular2, you should use ngFor instead and ngFor only allows a limited set of local variables to be defined, see DOC.

Pengyy
  • 37,383
  • 15
  • 83
  • 73
  • Unfortunately, it behaved differently from `ng-init` in which I can initialize the values within the scope of directive. What if the array comes from an API and you don't have any idea how many objects the API will respond. I'm trying to solve the problem without looping through the array just to initialize the toggle boolean. – Michael 'Maik' Ardan Mar 22 '18 at 08:11
  • @MichaelArdan you meant to ask about `ng-init` at `ng-repeat` block? – Pengyy Mar 22 '18 at 08:12
  • Sorry if the question confused you. Yeah, I'm talking about the `ng-init` in my `ng-repeat` block. Edited the question – Michael 'Maik' Ardan Mar 22 '18 at 08:14
  • @MichaelArdan `ngFor` only allows a limited set of local variables to be defined. see https://angular.io/api/common/NgForOf#local-variables – Pengyy Mar 22 '18 at 08:16
  • It seems that the same solution is not achievable in NG++. Thanks Pengyy – Michael 'Maik' Ardan Mar 22 '18 at 08:27
0

I did not understand your request, could you explain yourself better? why do not you try to use the @component ...

@Component({
  selector: 'tag-selector',
  templateUrl: './pagina.html',
  styleUrls: ['./pagina.css']
})
export class Controller{
  your code
}

Edit:

if you declare the $scope out of Init, it should work anyway

angular.module("app", [])

.controller("controller", function($scope) {

  $scope.init = function() {

  };
  $scope.modules = [{
    label: 'Module A',
    children: [
      'Module A - 1',
      'Module A - 2',
      'Module A - 3'
    ]
  }, {
    label: 'Module B',
    children: [
      'Module B - 1',
      'Module B - 2',
      'Module B - 3'
    ]
  }, {
    label: 'Module C',
    children: [
      'Module C - 1',
      'Module C - 2',
      'Module C - 3'
    ]
  }];

});

I'm sorry, but I'm not sure I fully understood the question ...

Marco Brugali
  • 75
  • 2
  • 9
  • Hi Marco! The question was confusing sorry. I had to edit the question to clarify things. I was talking about `ng-init` beside my `ng-repeat`. In my sample, I have 3 `toggle` variables initialized. Changing the value of one of the `toggle` wouldn't affect the other 2 since it is declared only within the scope of the `ng-repeat`. – Michael 'Maik' Ardan Mar 22 '18 at 08:18