2

I'm building a form using model-driven forms and building my form elements with formcontrol. I have a service where I put some functions and I send the formgroup to this function to access the formcontrols. I would like to know how could I change the formcontrols properties to do things like hide the element. I can't find a way to do this on the internet.

Thanks for all

edit:

Here is the example code:

My app begins with app.component.html

<h1>
  Orçamento
</h1>
<app-form [campos]="cpos"></app-form>

and here my app.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  cpos: any[] = [];

  constructor() { }

  ngOnInit() {

  }
}

I have a class called campo-base.ts it's the base for all my fields:

export class CampoBase<T> {
    nome: string;
    valor: T;
    label: string;
    obrigatorio: boolean;
    ordem: string;
    tamanho: number;
    tipoCampo: string;
    combo: {chave: string, valor: string}[] = [];
    tipo: string;
    disabled: boolean;
    tamcol: string;
    espcol: string;
    classe: string;
    rowcol: number;
    f3: string;
    change: string;
    visivel:boolean;

    constructor (opcoes: {
        valor?: T,
        nome?: string,
        label?: string,
        obrigatorio?: boolean,
        ordem?: string,
        tamanho?: number,
        tipoCampo?: string,
        combo?: {chave: string, valor: string}[],
        tipo?: string,
        disabled?: boolean,
        tamcol?: string,
        espcol?: string,
        classe?: string,
        rowcol?: number,
        f3?: string,
        change?: string,
        visivel?: boolean
    } = {}) {
        this.valor       = opcoes.valor;
        this.nome        = opcoes.nome || '';
        this.label       = opcoes.label || '';
        this.obrigatorio = !!opcoes.obrigatorio;
        this.ordem       = opcoes.ordem || '';
        this.tamanho     = opcoes.tamanho === undefined ? 1 : opcoes.tamanho;
        this.tipoCampo   = opcoes.tipoCampo || '';
        this.combo       = opcoes.combo || [];
        this.tipo        = opcoes.tipo || '';
        this.disabled    = !!opcoes.disabled;
        this.tamcol      = opcoes.tamcol || '';
        this.espcol      = opcoes.espcol || '';
        this.classe      = opcoes.classe || '';
        this.rowcol      = opcoes.rowcol === undefined ? 0 : opcoes.rowcol;
        this.f3          = opcoes.f3 || '';
        this.change      = opcoes.change || '';
        this.visivel     = (!!opcoes.visivel ? true : opcoes.visivel);
    }
}

and I use it in form.component.ts which received data from a service containing a list of fields with their properties.

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup }   from '@angular/forms';

import { CampoBase }         from './campo-base';
import { FormService }       from "./form.service";
import { FormDadosService } from './form-dados.service';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.css'],
  providers:[FormService,FormDadosService]
})
export class FormComponent implements OnInit {

  @Input() campos: CampoBase<any>[] = [];

  formulario: FormGroup = new FormGroup({});
  payLoad = '';

  coluna: number = 0;

  constructor(private formService: FormService, private servico: FormDadosService) { }

  ngOnInit() {
    this.servico.getCampos().subscribe((data) => {
      let classe: string = '';
      let combobox: {chave: string, valor:string}[] = [];

      data.forEach(campo => {
        classe = '';

        //Ajusta os tamanhos dos campos no form
        if (campo.ZI2_TIPO != '4') {
          classe += 'form-group ';

          if (campo.ZI2_ESPCOL !== '00') {
            classe += 'col-md-' + parseInt(campo.ZI2_TAMCOL).toString() + ' col-md-pull-' + parseInt(campo.ZI2_ESPCOL).toString() + ' col-md-offset-' + parseInt(campo.ZI2_ESPCOL).toString();
            classe += ' col-lg-' + parseInt(campo.ZI2_TAMCOL).toString() + ' col-lg-pull-' + parseInt(campo.ZI2_ESPCOL).toString() + ' col-lg-offset-' + parseInt(campo.ZI2_ESPCOL).toString();
          } else {
            classe += 'col-md-' + parseInt(campo.ZI2_TAMCOL).toString() + ' col-lg-' + parseInt(campo.ZI2_TAMCOL).toString();
          }
        }

        //Calcula o tamanho dos campos na linha para adicionar um novo row
        if (this.coluna >= 12) {
          this.coluna = 0;
        }
        this.coluna += parseInt(campo.ZI2_TAMCOL) + parseInt(campo.ZI2_ESPCOL);

        //Trata os campos combobox
        combobox.length = 0;
        if (campo.ZI2_CBOX !== null) {
          for (let x in campo.ZI2_CBOX) {
            if (campo.ZI2_CBOX.hasOwnProperty(x)) {
              combobox.push({chave: x, valor: campo.ZI2_CBOX[x]});
            }
          }
        }

        //Instancia o campo e adiciona na lista de campos
        this.campos.push(new CampoBase({
          valor:       '',
          nome:        campo.ZI2_CAMPO,
          label:       campo.ZI2_DESC,
          tipoCampo:   campo.ZI2_TIPO,
          tamanho:     campo.ZI2_TAM,
          ordem:       campo.ZI2_SEQ,
          obrigatorio: campo.ZI2_OBRIGA === '1',
          disabled:    campo.ZI2_VISUAL !== "A",
          classe:      classe,
          tamcol:      campo.ZI2_TAMCOL,
          espcol:      campo.ZI2_ESPCOL,
          rowcol:      this.coluna,
          f3:          campo.ZI2_F3,
          combo:       combobox.slice(),
          change:      campo.ZI2_CHANGE,
          visivel:     true
        }));

      });

      this.formulario = this.formService.toFormGroup(this.campos);
    });    
  }

  onSubmit() {

    this.payLoad = JSON.stringify(this.formulario.value);

  }

}

I build my form using form-campo.component.ts:

import { Component, Input, OnInit } from '@angular/core';
import { FormGroup }        from '@angular/forms';

import { CampoBase }        from './campo-base';
import { OrcamentoService } from '../orcamento.service';

@Component({
  selector: 'formcampos',
  templateUrl: './form-campo.component.html',
  styleUrls: ['./form-campo.component.css']
})
export class FormCampoComponent implements OnInit {

  @Input() campo: CampoBase<any>;
  @Input() form: FormGroup;

  isValid() {
    return this.form.controls[this.campo.nome].pristine || this.form.controls[this.campo.nome].valid;
  }

  constructor(private orcamentoService: OrcamentoService){}

  ngOnInit() {
    if (this.campo.change)
      this.form.controls[this.campo.nome].valueChanges.subscribe(valor => {
        let aChanges = this.campo.change.split(";");
        let expression = "";

        for (let i = 0; i < aChanges.length; i++) {
          if (aChanges[i].length > 0) {
            expression = "_this.orcamentoService." + aChanges[i].replace(/\(\)/g,"") + '(_this.form, valor)';
            eval(expression);
          }
        }
      });
  }
}

with this template:

<div [formGroup]="form" [class]="campo.classe" *ngIf="campo.visivel">
  <label [attr.for]="campo.nome" [hidden]="campo.tipoCampo === '4'">{{campo.label}}</label>
  <div [class.input-group]="campo.f3">
    <select *ngIf="(campo.tipoCampo == '1' || campo.tipoCampo == '3') && campo.combo.length > 0" [formControlName]="campo.nome" class="form-control" [id]="campo.nome">
        <option *ngFor="let opt of campo.combo" [value]="opt.chave">{{opt.valor}}</option>
    </select>
    <input *ngIf="(campo.tipoCampo == '1' || campo.tipoCampo == '3') && campo.combo.length == 0" [formControlName]="campo.nome" class="form-control"
            [id]="campo.nome" [type]="'text'" [maxlength]="campo.tamanho" [placeholder]="campo.label">
    <input *ngIf="campo.tipoCampo == '2'" [formControlName]="campo.nome" class="form-control"
            [id]="campo.nome" [type]="'email'" [maxlength]="campo.tamanho" [placeholder]="campo.label">
    <input *ngIf="campo.tipoCampo == '4'" [formControlName]="campo.nome"
            [id]="campo.nome" [type]="'hidden'">
    <textarea *ngIf="campo.tipoCampo == '5'" [formControlName]="campo.nome" class="form-control"
            [id]="campo.nome" [placeholder]="campo.label"></textarea>
        <span class="input-group-btn" *ngIf="campo.f3">
                <button class="btn btn-primary" type="button" id="f3{{campo.nome}}"><span class="glyphicon glyphicon-search"></span></button>
        </span>
  </div>
</div>

and finally in orcamento.service I tried to manipulate the visibility of some fields like below:

import { Injectable } from '@angular/core';

@Injectable()
export class OrcamentoService {

  constructor() { }

  gatTipoOrc(form, valor) {
    if (valor == "N") {
      form.controls['ZPO_UM'].enable();
    } else {
      form.controls['ZPO_UM'].disable();
      form.controls['ZPO_UM'].reset();
    }
  }

  gatUM(form, valor) {
    form.controls['ZPO_QTDPCX'].visivel = false;
  }

  habEntrega(form, valor) {

  }

}
  • With 'Hide the element' you mean hiding certain form controls based on logic (for example, based on the value of other controls)? – MikeOne Feb 20 '17 at 14:37
  • Yes, this is what I need. – Cristal Embalagens Feb 20 '17 at 14:39
  • You'd have to build a mechanism for that. For example, instead of only defining your form controls, you can define an object that also has a 'hidden' property (or function) for every control. I cannot describe this easily in a comment, so I'd have to make an Answer out of that.. – MikeOne Feb 20 '17 at 14:42
  • If you can post the reactive form code you have now, I might be able to easily add that functionality for you.. – MikeOne Feb 20 '17 at 14:43
  • I'll try to do in an answer. Some parts of the code are written in portuguese, I hope it doesn't make the code hard to be understood. – Cristal Embalagens Feb 20 '17 at 14:49
  • @MikeOne I posted an answer with most part of my code. I hope it helps you. Thanks a lot for helping me =) – Cristal Embalagens Feb 20 '17 at 14:58

2 Answers2

0

If I understand you correctly, you want to do things like hide an element - <div hidden></div> or in angular1 <div ng-hide="isHidden"></div>

In angular2 you should bind to the element properties similar to the traditional way:

<div [hidden]="true"></div>.

Of course you can use a variable, <div [hidden]="isHidden"></div>

Also note that using hidden is not recomended: https://stackoverflow.com/questions/35578083

EDIT:

As J. Adam Connor mentioned you can also use *ngIf instead of [hidden]. This will cause your element to be completely removed from the DOM which is a very good approach in general but should be carefully used with Forms because some validations and bindings require your form to match your model. That can be messy if it is missing from your actual Form in the DOM.

Community
  • 1
  • 1
Tom
  • 3,711
  • 2
  • 25
  • 31
  • How can I do this programmatically from the service as I pass the form as parameter. When I am building the form it's ok to manipulate this properties, my question is to change it after the class FormControl is instantiated. Sorry for my english, I'm not a native speaker. – Cristal Embalagens Feb 20 '17 at 14:38
  • See my answer. Use ngIf and set the variable in the if statement according to the parameters. Note that it doesn't have to be a boolean value. – J. Adam Connor Feb 20 '17 at 14:40
  • In both ways it will work by editing your template with a variable. `*ngIf` is a better approach if you want your element to be completely removed from your DOM while the `hidden` approach will keep it in the DOM but hide it. – Tom Feb 20 '17 at 14:43
  • Actually, if you use model-based (reactive) forms, do NOT use *ngIf to remove controls from your form, this will break stuff. The adviced way is to actually use hidden. – MikeOne Feb 20 '17 at 14:52
  • @MikeOne can you elaborate? – J. Adam Connor Feb 20 '17 at 14:55
  • 1
    @J.AdamConnor If you use *ngIf, as i mentioned, it completely remove them from the DOM what might mess your models, validations etc because angular will search for them in some situations. If you use hidden they are still in the DOM just not visible to the user so there is no harm to the form itself. – Tom Feb 20 '17 at 15:04
  • @Tom this is a good point. I suppose it should be made clear, then, that the controls will still be accessible to users and that this isn't a complete validation strategy. – J. Adam Connor Feb 20 '17 at 15:08
  • @J.AdamConnor of course :) validation should be made on client and server side in any case as a good hacker can always add form parameters based on your models and send them to your server. – Tom Feb 20 '17 at 15:09
  • What @Tom said. I tried *ngIf, but that caused a lot of issues with model driven forms. I can't find the article anymore, but I read that even a guy from the core team stated that use should really use hidden for formControls (*ngIf for all other stuff) – MikeOne Feb 20 '17 at 15:10
  • I see that with FormControl we can only get/set values in field and validate it but not change any property. This does not reflect the real world application or I'm doing things in the wrong way. Angular2 is a little but confuse to understand how it works with forms using model-driven form. – Cristal Embalagens Feb 20 '17 at 20:21
  • Can you try as a test the following: 1) put `[hidden]="true"` and see if it hides the element. 2) put a variable in your `*.component.ts` - `isHidden=true` and set your element with `[hidden]="isHidden"` and see if it hides the element. 3) now you can play with this variable and set it with validators from your model. – Tom Feb 21 '17 at 08:12
0

Well, I did it using *ngIf and interacting with another sibling components through a shared service.

Thanks for all for the help.