0

I have a form in html with a submit button. The button has a disabled option that is false only when the form is valid.

<button 
  type="submit" 
  class="btn btn-info btn-fill btn-wd" 
  [disabled]="ragazzoForm.form.invalid">
  Aggiungi Ragazzo
</button>

And it works perfectly. Then i have one of the fields which is a special ID. The id is generated with a function, in the component: genIdReg(). PS: "ragazzoForm" is the name of the form, I declared it in the tag.

<input 
  type="text" 
  class="form-control border-input" 
  readonly 
  name="idreg" 
  [value]="genIdReg(ragazzoForm)" 
  ngModel>

In this function take in input the form, i extract the values i need and then i use another function to cipher it, after that i return the ID.

The problem is that the function cipher does a .toUpperCase() to the string it gets in input. And because the genIdReg() is automatically called even if the form is empty, it returns error like:

RagazziComponent.html:9 ERROR TypeError: Cannot read property 'toUpperCase' of undefined at RagazziComponent.webpackJsonp.../../../../../src/app/ragazzi/ragazzi.component.ts.RagazziComponent.cipher (ragazzi.component.ts:38) at RagazziComponent.webpackJsonp.../../../../../src/app/ragazzi/ragazzi.component.ts.RagazziComponent.genIdReg (ragazzi.component.ts:62) at Object.eval [as updateRenderer] (RagazziComponent.html:14) at Object.debugUpdateRenderer [as updateRenderer] (core.es5.js:13131) at checkAndUpdateView (core.es5.js:12275) at callViewAction (core.es5.js:12638) at execComponentViewsAction (core.es5.js:12570) at checkAndUpdateView (core.es5.js:12276) at callViewAction (core.es5.js:12638) at execEmbeddedViewsAction (core.es5.js:12596) at checkAndUpdateView (core.es5.js:12271) at callViewAction (core.es5.js:12638) at execComponentViewsAction (core.es5.js:12570) at checkAndUpdateView (core.es5.js:12276) at callWithDebugContext (core.es5.js:13493)

So i implemented a control on the validity of the form before executing genIdReg():

genIdReg(form: NgForm) {
  if (!form.form.invalid) {
    console.log("sto generando")
    if (this.magicNumber == null) this.magicNumber = Math.floor(Math.random() * 1000);
    const nome = this.cipher(form.value.nome, this.magicNumber);
    const cognome = this.cipher(form.value.cognome, this.magicNumber);
    const classe = this.cipher(form.value.classe, this.magicNumber);
    console.log(nome, cognome, classe, this.magicNumber);
    return nome + cognome + classe + this.magicNumber;
  } else {
    return "Completa i campi per generare il codice"
  }
}

If the form that's in input is not invalid, then do the things, else return a placeholder. Pay attention at the console.log at line 3.

When i load the page... The value of the input is "Completa i campi per generare il codice", so it seems like it went smoothly. But in the console i get "sto generando". And obviously the above written TypeError, because cipher() is getting called. So it's like if both if and else are called... I can't understand what's wrong with my code...

EDIT: Here the html form code. (The first line is line 9 in the whole file):

<form (submit)="aggiungiRagazzo(ragazzoForm)" #ragazzoForm="ngForm">
                        <div class="row">
                            <div class="col-md-3">
                                <div class="form-group">
                                    <label>ID Registrazione</label>
                                    <input type="text" class="form-control border-input" readonly name="idreg" [value]="idreg" ngModel>
                                </div>
                              </div>
                            <div class="col-md-6">
                                <div class="form-group">
                                    <label for="exampleInputEmail1">Email</label>
                                    <input type="email" class="form-control border-input" placeholder="Email" required name="email" ngModel>
                                </div>
                            </div>
                            <div class="col-md-3">
                                <div class="form-group">
                                    <label for="exampleInputEmail1">Classe</label>
                                    <input type="text" class="form-control border-input" placeholder="Classe" required name="classe" ngModel>
                                </div>
                            </div>
                            <!-- <div class="col-md-3">
                              <div class="form-group">
                                  <label for="exampleInputEmail1">Classe</label>
                                  <select  *ngFor="let classe of classi" type="text" class="form-control border-input"  required name="classe" ngModel>
                                    <option>Prova</option>
                                    </select>
                              </div>
                          </div> //TODO -->
                        </div>

                        <div class="row">
                            <div class="col-md-6">
                                <div class="form-group">
                                    <label>Nome</label>
                                    <input type="text" class="form-control border-input" placeholder="Nome" value="Chet" name="nome" ngModel>
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="form-group">
                                    <label>Cognome</label>
                                    <input type="text" class="form-control border-input" placeholder="Cognome" value="Faker" name="cognome" ngModel>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-md-12">
                                <div class="card card-plain">
                                    <div class="header">
                                        <h4 class="title">Turni in cui non può registrarsi</h4>
                                    </div>
                                    <div class="content table-responsive table-full-width">
                                        <table class="table table-hover">
                                            <thead>
                                                <tr>
                                                    <th *ngFor="let cell of tableData2.headerRow">{{ cell }}</th>
                                                </tr>
                                            </thead>
                                            <tbody>
                                                <tr *ngFor="let row of tableData2.dataRows">
                                                    <td *ngFor="let cell of row">
                                                      <div class="checkbox" style="margin-left: 20px;">
                                                        <input id="checkbox1" type="checkbox" ngModel name={{cell}}>
                                                        <label for="checkbox1">
                                                            {{cell}}
                                                        </label>
                                                      </div></td>
                                                </tr>
                                            </tbody>
                                        </table>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="text-center">
                            <button type="submit" class="btn btn-info btn-fill btn-wd" [disabled]="ragazzoForm.form.invalid">Aggiungi Ragazzo</button>
                        </div>
                        <div class="clearfix"></div>
                    </form>
  • nope. tried both. Still same happening – Francesco Buccoliero Feb 03 '19 at 10:53
  • Please share the Template Code as well. The Error says there's some issue on html line 9. – SiddAjmera Feb 03 '19 at 10:59
  • Do you mean the HTML? Anyway , i updated the error in the main post. If you look it says it's in the cipher function – Francesco Buccoliero Feb 03 '19 at 11:02
  • Just a comment, related to your code, I would start by refactoring the code. Functions should not be called in template. They will be run on each change detection, which is **often**, which hurts performance. Depending on the case - In worst case you can run into something that seems like an infinite loop and kills your app, since the function is called over and over. You should use variables in template. – AT82 Feb 03 '19 at 11:04
  • i would have the input field to autoupdate every time i edit datas in the form. How should i do it with a static variable in template? – Francesco Buccoliero Feb 03 '19 at 11:08
  • If you change to reactive form you can easily do this by listening to `valueChanges` which is fired whenever form values are changed and there you could handle logic you need to do :) You **can** do this by using template driven form, since under the hood template driven forms basically work like reactive form, but I wouldn't recommend it, it's sorta a bit hacky, and can be achieved much easier with reactive form! :) – AT82 Feb 03 '19 at 11:11
  • I'm using angular since a month. Didn't even knew about this kind of form. Gonna look for it. Anyway this doesn't solve the basic logical error that's in my submission! May that be a bug? – Francesco Buccoliero Feb 03 '19 at 11:14
  • edit: added the html – Francesco Buccoliero Feb 03 '19 at 11:24

1 Answers1

0

As mentioned in comment, you shouldn't call functions in template, they are called on each change detection and will hurt the perfomance, in the worst cases you can run into something that seems like an infinite loop.

But why doesn't your code work? We can find this out, when we use a debugger! When you have the function in template, it's called instantly when template is rendered... has the form been properly built when it's called first time(s)... no. The form exists, but the form controls do not, so the form is considered valid!

Okay.... but why does the console.log show differently? Look here: https://stackoverflow.com/a/12773316/7741865:

it is due to console.log() being queued up, and it prints a later value of the array (or object).

Here's an image using debugger where we can see that indeed, the form exists, but form controls do not!

enter image description here

I would recommend, that you use a reactive form instead, and listen to valueChanges and handle all your logic there instead! :)

AT82
  • 71,416
  • 24
  • 140
  • 167