Oh man - you have no idea how your comments have helped!
If you’re interested please take a look at the original answer I wrote to your suggestions. As soon as I had finished writing it I realized that the easiest thing to do was to create a property (uiInput
) on the data, and that simplified the code a million-fold.
So the html I ended up with in the custom element went from
<span if.bind="(row.sector == 'Borrowings (+)' || row.sector == 'Repayments (-)') && selectedPeriod.period == 'Months'">
<span class="border p-1" contenteditable textcontent.bind="row.months[(selectedYear-1)*12+$index] | number" input.delegate="callback()"></span>
</span>
<span if.bind="bindingNames().indexOf(row.sector) > -1">
${item | numberFormat: "0,0 | null" }
</span>
to
<span if.bind="row.uiInput && selectedPeriod.period == 'Months'">
<span class="border p-1" contenteditable textcontent.bind="row.months[(selectedYear-1)*12+$index] | number" input.delegate="callback()"></span>
</span>
<span else>
${item | numberFormat: "0,0 | null" }
</span>
Again a million thanks for your help.
All the best
Jeremy
Original answer to your comments
Your comments made me look at the code again.
In the main form where costingService.calculateAllValus()
can take a long time
protected calculateValues() {
if (this.costing) {
const { productionList, shellingCapacityMt, productionCosts } = this.costingsService.calculateAllValues(this.costing, this.selectedYear);
this.productionList = productionList;
this.shellingCapacityMt = shellingCapacityMt;
this.productionCostsList = productionCosts;
}
}
protected selectedYearChanged(year: number) {
this.calculateValues();
}
protected selectedPeriodChanged() {
this.calculateValues();
}
private async loadCostingByCompanyId(companyId: string) {
this.costing = await this.costingsService.loadByCompanyId(companyId);
this.costingsService.fillDefaultCosts(this.costing, this.dictionary);
this.calculateValues();
}
Main form html where the custom elements are all bound to this.costing
and each utilize the bound values of selectedYear
and selectedPeriod
.
<div class="card-body">
<fieldset>
<legend>Select production year</legend>
<select-production-year title="Purchases and Production" selected-year.two-way="selectedYear"
selected-period.two-way="selectedPeriod" periods-id="sp1" periods.bind="periods"
callback.call="calculateValues()"></select-production-year>
</fieldset>
<purchases id="purchases" costing.bind="costing" selected-year.bind="selectedYear" callback.call="calculateValues()">
</purchases>
<production id="production" production-list.bind="productionList" costing.bind="costing" selected-year.bind="selectedYear"
selected-period.bind="selectedPeriod" callback.call="calculateValues()"></production>
<production-costs id="profit-and-loss" root.bind="productionCostsList" , selected-year.two-way="selectedYear"
selected-period.two-way="selectedPeriod" periods.bind="periods" costing.bind="costing"
callback.call="calculateValues()"></production-costs>
<cashflow id="cashflow" root.bind="productionCostsList" , selected-year.two-way="selectedYear"
selected-period.two-way="selectedPeriod" periods.bind="periods" costing.bind="costing" callback.call="calculateValues()"></cashflow>
<kpis id="kpis" root.bind="productionCostsList" , selected-year.two-way="selectedYear" selected-period.two-way="selectedPeriod"
periods.bind="periods" costing.bind="costing" callback.call="calculateValues()"></kpis>
...
and finally one of the custom elements cashflow-custom-element.ts
@autoinject
export class CashflowCustomElement {
@bindable public selectedYear: number;
@bindable public selectedPeriod: IPeriod;
@bindable public root: IProductionCostsResults;
@bindable public costing: ICosting;
@bindable public callback: () => void;
@bindable public periods: IPeriod[];
constructor(private readonly productionCostsService: ProductionCostsService) { }
public bindingNames() {
return [
"B/F",
"Interest (-)",
"Capital purchases machines (-)",
"Civil engineering (-)",
"P&L (+)",
"Balance"
];
}
public bindingNamesFromCosting = () => this.costing.variables.productionCostsResults.cashflow.map(cf => cf.sector);
public getValues(row: IExpenseItem) {
if (row && this.selectedPeriod) {
const startDate = this.costing ? this.costing.variables.dates.civilEngineeringEndDate : moment().format("YYYY-MM-DD");
const values = this.productionCostsService.getRowValues(row, this.selectedYear, { period: this.selectedPeriod.period }, startDate);
return values;
}
}
}
cashflow-custom-element
<template>
<require from="./filter-value-converter"></require>
<require from="./select-production-year-element"></require>
<require from="./quick-links-element"></require>
<fieldset>
<quick-links legend="Cashflow"></quick-links>
<select-production-year selected-year.two-way="selectedYear" selected-period.two-way="selectedPeriod" periods-id="spCashflow"
periods.bind="periods" show-periods="true" callback.call="callback()"></select-production-year>
<table class="table table-sm table-borderless ${selectedPeriod.period == 'Quarters' ? 'quarter-highlight last-col-bold' : ''}">
<thead>
<tr class="small">
<th style="width:20%"> </th>
<th repeat.for="date of getValues(root.quantities[0]).dates" class="text-right">
${date | dateFormat: "MMM YY"}
</th>
</tr>
</thead>
<tbody>
<tr repeat.for="row of root.cashflow" class="small ${row.name=='Header' ? 'font-weight-bold text-primary' : ''} ${row.sector=='Balance' ? 'font-weight-bold' : ''}">
<td>${row.sector}</td>
<td repeat.for="item of getValues(row).values" class="text-right" class.bind="item | 'posNegColor'">
<span if.bind="(row.sector == 'Borrowings (+)' || row.sector == 'Repayments (-)') && selectedPeriod.period == 'Months'">
<span class="border p-1" contenteditable textcontent.bind="row.months[(selectedYear-1)*12+$index] | number" input.delegate="callback()"></span>
</span>
<span if.bind="bindingNames().indexOf(row.sector) > -1">
${item | numberFormat: "0,0 | null" }
</span>
</td>
</tr>
</tbody>
</table>
</template>
+++++++++++++++++++++++++++++++++
So the problem I am trying to solve is that the bindNames()
function returns hard wired names for the binding rules used in the table.
What it should be doing is using the bindingNamesFromCosting ()
function, but this doesn’t work because costing
is not yet bound to the custom element at the time that its called…