sHamann
sHamann

Reputation: 799

Angular 2 ngModel cant reach Array property

Hello fellow programmer,

after i make an API call i convert the JSON to an array.

This array will display in an Component inside an HTML table, after you pressed the "edit" button the Component will change his content from an HTML table to an HTML form, where you can edit the Data, which Data you can send back to the API server.

component.html

        <div *ngFor="let rule of generateArray(gutschein['ruleset_list']['rulesets']); let i = index" >
            <div class="form-group">
                <label>Rule: </label> <input type="text" class="form-control" id="rule{{i}}" 
                requierd
                [(ngModel)]="rule['condition']['expression']" name="rule"  #rule="ngModel">
                <div [hidden]="rule.valid || rule.pristine" class="alert alert-danger">
                    Rule is not valid
                </div>
            </div>
            <div class="form-group">
                <label>Discount: </label> <input type="text" class="form-control" id="discount{{i}}" 
                requierd
                [(ngModel)]="rule['results']['results'][i+1]['calculation']" name="discount"  #discount="ngModel">
                <div [hidden]="discount.valid || discount.pristine" class="alert alert-danger">
                    Discount is not valid
                </div>
            </div>  

component.ts

generateArray(obj) {
    return Object.keys(obj).map((key) => { return obj[key] });
}

it seems like the ngModel cant reache my this.rule['condition']['expression'] Data.

If i changed it to this.rule['condition'] it works perfectly.

Any Idea how I can fixed this problem? I would be thankfull for an explanation not only the fixed code.

JSON

    {
        "id": "1",
        "code": "A1234",
        "valid": null,
        "ruleset_list": {
            "rulesets": {
                "1": {
                    "id": "1",
                    "voucher_id": "1",
                    "condition": {
                        "expression": "shop.totalamount > `15` && current_datetime < `1490021400`"
                    },
                    "results": {
                        "results": {
                            "1": {
                                "id": "1",
                                "value_path": "shop.totalamount",
                                "calculation": "#VALUE * 0.9",
                                "new_value": null
                            }
                        }
                    }
                },
                "2": {
                    "id": "2",
                    "voucher_id": "1",
                    "condition": {
                        "expression": "shop.totalamount > `20` && current_datetime < `1490021400`"
                    },
                    "results": {
                        "results": {
                            "2": {
                                "id": "2",
                                "value_path": "shop.totalamount",
                                "calculation": "#VALUE * 0.8",
                                "new_value": null
                            }
                        }
                    }
                }
            }
        }
    }

Upvotes: 1

Views: 2114

Answers (1)

AVJT82
AVJT82

Reputation: 73367

Your root cause is #rule="ngModel" and #discount="ngModel". You are already using two-way binding, so do not declare a template reference as ngModel. The template reference value and the two way binding do not go together. As per said here:

Template input and reference variable names have their own namespaces. The hero in let hero is never the same variable as the hero declared as #hero.

So I guess you could basically say that there two variables "fight each other" and clash.

Furthermore, you can also use dot notation in your ngModels, so the following are equal:

rule['condition']['expression']

and

rule.condition.expression

I won't go into that any closer, since here are great answers: Access / process (nested) objects, arrays or JSON. But you can of course use bracket notation if you like :)

EDIT:

Missed the fact, that each name attribute needs to be unique so that the form fields are evaluated as different form fields, and not one and the same. So you can use the index to do that:

name="rule{{i}}" and name="discount{{i}}"

Finally, a plunker to play with:

Demo

PS. I would perhaps actually suggest you use reactive forms here now, that would be easier to handle, since by removing the template reference you will have trouble with the validation. With reactive forms, you can skip the ngModels altogether and set the default values to the form control and also handle the validation. Here more info about dynamic forms and nested dynamic forms, as that is what you would need.

Here's a recent question and answer with values coming from http-request, meaning just like yours you need to set empty form controls first and when you want to edit form use setvalue or patchvalue to set the pre values to your form. Depending on your use case, maybe you won't need to use patchValue at all. BUT in case you need.

Upvotes: 3

Related Questions