0

I'm working on migrating a HUGE angular form to a new layout (can't change technologies). The current version is drawn from a huge nasty javascript array that gets fed into an ng-repeat and calls various templates. The templates aren't changing in the new form except for some new css stuff. The new/old path that angular follows to generate the form html is controlled by a server side feature toggle (not really import to this question).

For the form, the only difference now is I'm not in a ng-repeat loop and instead I'm doing a ng-include multiple times in a more well defined layout.

The problem I'm having is the values being passed into each ng-include elements only get the ng-init values of the last call on the page, see example below..

<!-- begin template definitions -->
<script type="text/ng-template" id="/templates/currency.html">
    <ul class="input-field-layout">
        <li>
            <input type="text" class="form-control {{field.additionalClasses}}" id="input_{{field.name}}"
                   ng-class="{{field.ngClass}}"
                   ng-model="$ctrl.contract[field.name]"
                   ng-disabled="field.disabled && !$ctrl.overrideSelected"
                   ng-change="field.onChange()"
                   ng-blur="field.onBlur()"
                   ng-readonly="field.readOnly"
            />
            <span class="input-group-btn" ng-if="field.showCalculator && field.canCalculate()">
                <button type="button" class="btn btn-default btn-sm"
                        id="calc_{{field.name}}"
                        ng-click="field.calculate()"
                        tabindex="-1"
                >
                    <i class="glyphicon glyphicon-flash"></i>
                </button>
            </span>
        </li>
        <li class="input-description-text" ng-if="field.descriptionText">
            <span>{{field.descriptionText}}</span>
        </li>
    </ul>
</script>
<!-- end template definitions -->


<!-- begin snippet for old form -->
<div ng-repeat="fieldGroup in $ctrl.fieldGroups">
    <div class="clearfix" ng-if="fieldGroup.clearFixClass == true"></div>
    <div>
        <h3>{{fieldGroup.name}}</h3>
        <hr>
        <div class="form-group form-group-sm"
             ng-repeat="field in fieldGroup.fields">
            <label class="control-label {{field.additionalClasses}}" for="input_{{field.name}}">{{field.field_label}}</label>
            <div ng-include="'/templates/'+field.type+'.html'"></div>
        </div>
    </div>
</div>
<!-- begin snippet for old form -->


<!-- being snippet for new form -->
<div class="flexRow">
    <label class="boldInput" for="input_adjustment">Adjustment</label>
    <div
        ng-init="
        field = {
            name: 'adjustment'
        }
        "
        ng-include="'/templates/currency.html'">
    </div>
</div>
<div class="flexRow">
    <label class="boldInput" for="input_total_due">Total</label>
    <div
        ng-init="
        field = {
            name: 'total',
            descriptionText: 'blah blah blah',
            disabled: $ctrl.disabledCheck.disabled_fields,
            showCalculator: true,
            calculate: $ctrl.calculateTotal,
            canCalculate: $ctrl.canCalculateTotal,
            additionalClasses: 'boldInput'
        }
        "
        ng-include="'/templates/currency.html'">
    </div>
</div>
<!-- end snippet for new form -->

The old logic generates what you'd expect, but the new logic generates the following. Notice how both fields have the id input_total and other attributes/values of the final call to the template on the page.

<div class="flexRow">
    <label class="boldInput" for="input_adjustment">Adjustment</label>
    <ul class="input-field-layout ng-scope">
        <li>
            <input type="text" class="form-control boldInput ng-empty ng-touched" id="input_total" ng-class="" ng-model="$ctrl.contract[field.name]" ng-disabled="field.disabled">
            <!-- ngIf: field.showCalculator && field.canCalculate() -->
        </li>
        <!-- ngIf: field.descriptionText --><li class="input-description-text ng-scope" ng-if="field.descriptionText">
            <span class="ng-binding">blah blah blah</span>
        </li><!-- end ngIf: field.descriptionText -->
    </ul>
</div>
<div class="flexRow">
    <label class="boldInput" for="input_total">Total</label>
    <ul class="input-field-layout ng-scope">
        <li>
            <input type="text" class="form-control boldInput ng-empty ng-touched" id="input_total" ng-class="" ng-model="$ctrl.contract[field.name]" ng-disabled="field.disabled">
            <!-- i've removed the calculator html for sake of space in this question -->
        </li>
        <!-- ngIf: field.descriptionText --><li class="input-description-text ng-scope" ng-if="field.descriptionText">
            <span class="ng-binding">blah blah blah</span>
        </li><!-- end ngIf: field.descriptionText -->
    </ul>
</div>

I feel like ng-init is not what I should be using to pass-in values for field to the templates in the new form logic, but Uncle Google is failing me.

What should I be using instead of ng-init or how does the scope(?) change inside an ng-repeat versus multiple calls to the templates?

Andrew Sohn
  • 675
  • 1
  • 6
  • 15

2 Answers2

0

When you assign field = { ... } you're overwriting it the second time, which binds back to the initial template.

To do this type of "pass parameter to template" thing, you should really be using a directive or component.

The reason the ng-repeat works is because it creates its own scope for each iteration, meaning the init variable is bound to a different scope each time and doesn't get overwritten.

Chad H
  • 584
  • 3
  • 11
0

I just found this post: Losing scope when using ng-include

It seems if you add ng-if="true" to the ng-include it causes angular create a new scope!

This is probably taking advantage of a caveat in angular's ng-if but it lets me move forward.

<!-- being modified snippet for new form -->
<div class="flexRow">
    <label class="boldInput" for="input_adjustment">Adjustment</label>
    <div
        ng-if="true"
        ng-init="
        field = {
            name: 'adjustment'
        }
        "
        ng-include="'/templates/currency.html'">
    </div>
</div>
<div class="flexRow">
    <label class="boldInput" for="input_total_due">Total</label>
    <div
        ng-if="true"
        ng-init="
        field = {
            name: 'total',
            descriptionText: 'blah blah blah',
            disabled: $ctrl.disabledCheck.disabled_fields,
            showCalculator: true,
            calculate: $ctrl.calculateTotal,
            canCalculate: $ctrl.canCalculateTotal,
            additionalClasses: 'boldInput'
        }
        "
        ng-include="'/templates/currency.html'">
    </div>
</div>
<!-- end modified snippet for new form -->
Andrew Sohn
  • 675
  • 1
  • 6
  • 15