dynamicTitle not rendered

#1

I have a page defined as below that contains UIViewTextTile component - I want to have a dynamic title for it - the datasource it is an “evaluate” function with response selector “tuples” - anyway the dynamicTitle does not work and it is not show - if I use standard title it works fine. What am I doing wrong here?

ui module MyApp {
    page Dashboard {
        "url": "dashboard",
        "title": "{~MyApp.Dashboard.title~}",
        "data": {
            "record": "MyApp.DashboardPlants"
        },
        "components": [
            {
                "id": "SideMenu",
                "component": "MyApp.Menu",
                "renderTo": "#side-menu",
                "activeItem": "MyApp"
            },
            {
                "id": "SideSubMenu",
                "component": "MyApp.SubMenu",
                "renderTo": "#side-sub-menu",
                "activeItem": "MyApp"
            },
            {
                "id": "pageMainTitleAA",
                "renderTo": "#pageMainTitle",
                "component": "UIViewTextTile",
                "dynamicTitle": "Pippo pluto {{ record.data.cells[0].str}}",
                "box" : true,
                "boxHeight" : 200,
                "titleAlign":"center",
                "data": {
                    "record": "MyApp.CurrentDate"
                }
            },
            {
                "id": "UsageChart",
                "renderTo": "#Chart",
                "component": "MyApp.UsageChart",
                "title": "{~MyApp.Detail.DetailTabs.UsageChart.title~}"
            }
        ],
    "id": "Dashboard"
    }
}
0 Likes

#2

I would like to follow this thread, without opening a new one.

I need to create a dynamicTitle for a UIViewUsageAndEvents chart. I am using the field dynamicTitle:

"dynamicTitle": "Risk Index for Sibilo: {{record.data.id}}",

because I have the following data record in the same component:

"data": {
      "record": "ItTgAdvAnaBoilerUi.DashboardSibiloSourcePlot"
},

Is it correct? How can I make it visible in my chart? Do I have to reference it someway in the corresponding template? Maybe something like this:

"<div class='col-md-12 text-center'>",
    "{{ h1 dynamicTitle }}",
"</div>",
0 Likes

#3

dynamicTitle is just the formula for a title. At the onRenderDataLoaded stage for UIViewComponent, any existing dynamicTitle is evaluated, and the result is stored as title. So in your component’s template, you will just need {{ h1 title }}, for example.

@marcosordi There is a bug in UIViewTextTile, in that it overrides onRenderDataLoaded but doesn’t call UIViewComponent.onRenderDataLoaded. Could you please file a ticket?

0 Likes

#4

@matt yes going to open the ticket

0 Likes

#5

The formula {{record.data.id}} is not computed. Is it related to the fix you mentioned above or am I missing something? I’m using the following template inside my component:

 "template":["<div data-expanded='show'>",
          "<div class='form-inline' style='margin: 15px 0 5px 0;'>",
            "<div class='col-md-8 text-center'>",
              "<% if (relativeDate) { %><label class='col-xs-5 col-form-label'> <%= component.get('relativeDateLabel') %> </label> <% } %>",
              "<% if (dateRangeSelectorEnabled) { %><div class='<%= relativeDate ? 'col-md-auto': 'col-md-auto' %>'><span id='DateRangeSelect'></span></div><% } %>",
            "</div>",
            "<div class='col-md-4 text-right' style='float: right;'>",
              "<% if (baselineEnabled) { %><a class='btn btn-default btn-sm baseline-period'><%= baselinePeriodText %></a><% } %>",
              "<% if (sourceIdSelectEnabled){ %>",
                "<label id='sourceIdSelectLabel' style='margin: 0 5px 0 0px;'><%= component.get('sourceIdSelectLabel') %></label>",
                "<div id='sourceIdSelect' style='display: inline-block;'></div>",
              "<% } %>",
              "<% if (intervalEnabled){ %>",
                "<div class='row' style='margin: 0 0 5px 0;'>",
                  "<label style='margin: 0 0 0 0;'><%= component.get('intervalText') %></label>",
                "</div>",
                "<div id='IntervalSelect' style='display: inline-block; width: 110px;'></div>",
              "<% } %>",
              "<% if (component.get('enablePriorPeriod')) { %>",
                "<span style='margin-left: 10px;' id='PriorPeriodCheckbox'></span>",
              "<% } else { %>",
                "<span style='margin-left: 10px; display:none;' id='PriorPeriodCheckbox'></span>",
              "<% } %>",
              "<% if (clearSelectedLegendSeriesButtonEnabled) { %>",
                "<a class='btn btn-default btn-sm' id='clearSelectedSeries' style='margin-left: 10px;'>",
                  "<%= component.get('clearSelectedSeriesButtonText') %>",
                "</a>",
              "<% } %>",
            "</div>",
          "</div>",
          "<div class='summary-container row'>",
            "<div class='col-md-12 text-center'>",
                "<%= component.get('dynamicTitle') %>",
            "</div>",
          "</div>",
          "<div class='chart-container'></div>",
          "<div class='legend-container'></div>",
        "</div>"],
0 Likes

#6

You should be rendering the title, not the dynamicTitle. dynamicTitle will always contain only the un-evaluated expression. title has the evaluated result.

0 Likes

#7

Thank you @matt, I corrected that but now I am missing another point.

How can I get the element selected inside the drop-down list using {{record.data.id}}-like notation? I have the following:

Data Source:

ui module MyPackageUi {
    dataSource DashboardSibiloSourcePlot {
        "collection": false,
        "record": true,
        "c3type": "MyPackageTdSibilo",
        "c3function": "fetch",
        "responseSelector": "objs",
        "c3arguments": {
            "spec": {
                "limit": -1,
                "include": "id, assets.id, name"
            }
        }
    }
}

and the following reference in my UIViewUsageAndEvent chart:

"data": {
   "record": "MyPackageUi.DashboardSibiloSourcePlot"
},

I tried the following commands but it shows nothing.

  • {{record.data.id}}
  • {{record.data[0].id}}

Is there a way to debug this from Console?

0 Likes

#8

"<%= component.get('title') %>", is NOT working too. And the same with simply: {{h1 title}}

@matt How can I show the evaluated result? I did not find a way, even if I try to force rendering and refreshing with the following functions:

  • chart.refresh()
  • chart.render()

The commands above work only if I run them in the JavaScript Console where the UI is loaded.

@marcosordi Is there a known issue with rendering?

Thank you very much for your patience.

0 Likes

#9

Here, an update on the title rendering.

I noticed that if I define a dynamic title like "dynamicTitle": My title "{{id}}" I obtain the value “My title development”. This means the evaluation is obtained and it should correspond to an environment property.

As stated in documentation of UIViewUsageAndEvents, for field title:

The title also supports dynamic content from record, pageParams and environment. It enssentailly calls C3.script.helpers.matchDynamicContent. @example “{{fieldA}} and {{fieldB}}, then {{fieldC}}” It will try to extract all dynamic fields from record, pageParams and environment, in the order of priority.

Now, what I need is getting the name of the component record field. The record is defined as follows:

"data": {
          "record": "MyPackageUi.DashboardSibiloSourcePlot"
        },

Actually, when I use record.data.name the evaluation is an empty string “” (i.e. My title instead of My title name). On the contrary, I can correctly get the value record.data.name when I use the metrics, like this:

"series": [
            {
                "id": "RiskScore",
                "isTimeseries": true,
                "name": "{~MyPackageUi.UsageChart.series.15MinUsage.name~}",
                "data": {
                    "c3type": "MyPackageTdAsset",
                    "c3function": "evalMetric",
                    "c3arguments": {
                        "spec": {
                            "id": "{{record.data.assets[0].id}}",
                            "expression": "{{record.data.name}}",
                            "include": "start, end, dates, data"
                        }
                    },
                    "responseTransform": null,
                    "collection": false
                },
                "color": "#f0a63a",
                "unit": "%",
                "chartType": "line",
                "axis": "{~MyPackageUi.chart.consumption.axis~}",
                "tooltipGroup": "usage",
                "hasPriorPeriodSeries": false,
                "disabled": false,
                "priorPeriodSeriesConfig": {
                    "color": "#d68510"
                }
            }
        ],

I’m really struggling with this problem and I cannot figure out what is the issue.

What about using a custom data source?

Here, the Type declaration:

type MyPackageUiSibiloInfo {

  /**
   * Boiler Unit reference
   */
  boilerUnit : string

  /**
   * Name of the metric to be used
   */
  metricName: string

  sibiloId: string

  
  /**
   * Get all the information at a given timestamp
   */
  fetchSibiloInformation : function(spec: !FetchSpec): MyPackageUiSibiloInfo js server
}

Here, the function implementation:

var logger = C3.logger("fetchSibiloInformation");

function fetchSibiloInformation(spec) {

	if (spec.filter === undefined)
		return MyPackageUiSibiloInfo.make(); // how to return undefined??

	var filter, result, fetchResultObjs;

	filter = spec.filter;

	fetchResultObjs = MyPackageTdSibilo.fetch({
    		"limit":-1,
    		"filter": filter,
    		"include": "id, assets.id, name"
	}).objs[0];



	/*
	* 	Create the result object.
	*/

	result = MyPackageUiSibiloInfo.make({
				"boilerUnit": fetchResultObjs.asset[0].id,
				"metricName": fetchResultObjs.name,
				"sibiloId": fetchResultObjs.id
			});

	return result;
}

Finally the datasource:

ui module MyPackageUi {
    dataSource SibiloInfoSource {
        "record": true,
        "collection": false,
        "c3type": "MyPackageUiSibiloInfo",
        "c3function": "fetchSibiloInformation",
        "responseSelector": "objs",
        "responseTransform": "",
        "c3arguments": {
            "spec": {
                "filter" : "id == 'XXX'",
                "limit": -1,
                "include": "id, assets.id, name"
            }
        }
    }
}

Now, I can use the following configuration for UsageChart and title is computed but not rendered. The metrics should be evaluated correctly, but it not rendered as well.

ui module MyPackageUi {
    component UsageChartPlus {
        "id": "MyPackageUi.UsageChartPlus",
        "component": "UIViewOnlineUsageAndEventsBoiler",
        "enablePriorPeriod": false,
        "enableExportChart": false,
        "enableStatistics": false,
        "ignoreTimeZone": true,
        "dynamicTitle": "Risk Index for Sibilo {{sibiloId}}",
        "medianText": "{~MyPackageUi.UsageChart.medianText~}",
        "showPriorText": "{~MyPackageUi.UsageChart.showPriorText~}",
        "consumptionText": "{~MyPackageUi.chart.consumption.axis~}",
        "statisticsDataPointsText": "{~MyPackageUi.UsageChart.statisticsDataPointsText~}",
        "statisticsHolidayText": "{~MyPackageUi.UsageChart.statisticsHolidayText~}",
        "statisticsFooterText": "{~MyPackageUi.UsageChart.statisticsFooterText~}",
        "intervalText" : "{~MyPackageUi.UsageChart.intervalText~}",
        "dateRangeSelectorEnabled": false,
        "dynamicDateRange":true,
        "data": {
          "record": "MyPackageUi.SibiloInfoSource"
        },
        "template":["<div data-expanded='show'>",
                      "<div class='form-inline' style='margin: 15px 0 5px 0;'>",
                        "<div class='col-md-8 text-center'>",
                          "<% if (relativeDate) { %><label class='col-xs-5 col-form-label'> <%= component.get('relativeDateLabel') %> </label> <% } %>",
                          "<% if (dateRangeSelectorEnabled) { %><div class='<%= relativeDate ? 'col-md-auto': 'col-md-auto' %>'><span id='DateRangeSelect'></span></div><% } %>",
                        "</div>",
                        "<div class='col-md-4 text-right' style='float: right;'>",
                          "<% if (baselineEnabled) { %><a class='btn btn-default btn-sm baseline-period'><%= baselinePeriodText %></a><% } %>",
                          "<% if (sourceIdSelectEnabled){ %>",
                            "<label id='sourceIdSelectLabel' style='margin: 0 5px 0 0px;'><%= component.get('sourceIdSelectLabel') %></label>",
                            "<div id='sourceIdSelect' style='display: inline-block;'></div>",
                          "<% } %>",
                          "<% if (intervalEnabled){ %>",
                            "<div class='row' style='margin: 0 0 5px 0;'>",
                              "<label style='margin: 0 0 0 0;'><%= component.get('intervalText') %></label>",
                            "</div>",
                            "<div id='IntervalSelect' style='pointer-events: none; opacity: 0.5; display: inline-block; width: 100px;'></div>",
                          "<% } %>",
                          "<% if (component.get('enablePriorPeriod')) { %>",
                            "<span style='margin-left: 10px;' id='PriorPeriodCheckbox'></span>",
                          "<% } else { %>",
                            "<span style='margin-left: 10px; display:none;' id='PriorPeriodCheckbox'></span>",
                          "<% } %>",
                          "<% if (clearSelectedLegendSeriesButtonEnabled) { %>",
                            "<a class='btn btn-default btn-sm' id='clearSelectedSeries' style='margin-left: 10px;'>",
                              "<%= component.get('clearSelectedSeriesButtonText') %>",
                            "</a>",
                          "<% } %>",
                        "</div>",
                      "</div>",
                      "<div class='summary-container row'>",
                        "<div class='col-md-12 text-center'>",
                            "<%= component.get('title') %>",
                        "</div>",
                      "</div>",
                      "<div class='chart-container'></div>",
                      "<div class='legend-container'></div>",
                    "</div>"],
        "intervalOptions": [
            { "text": "{~MyPackageUi.UsageChart.IntervalSelect.opts.7.text~}", "id": "MINUTE" },
            { "text": "{~MyPackageUi.UsageChart.IntervalSelect.opts.5.text~}", "id": "FIVE_MINUTE" },
            { "text": "{~MyPackageUi.UsageChart.IntervalSelect.opts.0.text~}", "id": "QUARTER_HOUR" },
            { "text": "{~MyPackageUi.UsageChart.IntervalSelect.opts.1.text~}", "id": "HOUR" }
        ],
        "customChartCfg": {
            "lang": {
                "shortMonths": [
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.0~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.1~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.2~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.3~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.4~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.5~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.6~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.7~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.8~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.9~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.10~}","{~MyPackageUi.UsageChart.chartCfg.lang.shortMonths.11~}"
                ],
                "months": [
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.0~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.1~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.2~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.3~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.4~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.5~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.6~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.7~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.8~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.9~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.months.10~}","{~MyPackageUi.UsageChart.chartCfg.lang.months.11~}"
                ],
                "weekdays": [
                    "{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.0~}","{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.1~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.2~}","{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.3~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.4~}","{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.5~}",
                    "{~MyPackageUi.UsageChart.chartCfg.lang.weekdays.6~}"
                ]
            },
            "xAxis": {
              "title": {
                "text": "Time",
                "align": "middle"
              },
              "opposite": false
            }
        },
        "metricSelectorCollection": {
                    "responseSelector": "",
                    "collection": true,
                    "c3type": "MetricSearchUtil",
                    "c3function": "fetchMetrics",
                    "c3arguments": {
                        "spec": {
                            "filter": "intersects(name, ['BalanceTubeDeltaPress', 'FilterInDeltaPress', 'PWayInPress', 'PWayThrustBearingOutLubeOilTemp', 'ExternalSideInSealingFluidTemp', 'AuxTurbSideBearingTemp', 'AuxTurbSideBearingVib', 'AuxTurbSideInSealingFluidTemp', 'AuxTurbSideOutSealingFluidTemp', 'AuxTurbSideSealingFluidDeltaTemp', 'BalanceTubeFlow', 'FeedWaterPumpEfficiency', 'ExternalSideBearingTemp', 'ExternalSideBearingVib', 'ExternalSideOutSealingFluidTemp', 'ExternalSideSealingFluidDeltaTemp', 'ExternalThrustPadTemp', 'FilterQuality', 'FeedWaterPumpNetPositiveSuctionHead', 'OutCollectorTemp', 'OutPress'])",
                            "limit": -1,
                            "include": "name, label, unit"
                        }
                    }
        },
        "metricSelectorSrcType": "ItTgAdvAnaStructureTdGenerationAsset",
        "yAxisMin": null,
        "autoSelectFirstIntervalOption": false,
        "defaultInterval": "MINUTE",
        "minutesTimeRange": 60,
        "dayTimeRange": 365,
        "secondTimeRangeInDays": 1,
        "minRestriction": 3,
        "fiveMinRestriction" : 7,
        "startDate": {
            "referenceConfig": [
            "now"
            ],
            "direction": "before",
            "number": 14,
            "unit": "day"
        },
        "endDate": {
            "referenceConfig": [
            "now"
            ],
            "direction": "after",
            "number": 1,
            "unit": "hour"
        },
        "permissions": [
            {
                "typeName": "MyPackageTdAsset",
                "action": "evalMetric"
            }
        ],
        "statisticsDataPoints": true,
        "statisticsHoliday": true,
        "series": [
            {
                "id": "RiskScore",
                "isTimeseries": true,
                "name": "{~MyPackageUi.UsageChart.series.15MinUsage.name~}",
                "data": {
                    "c3type": "MyPackageTdAsset",
                    "c3function": "evalMetric",
                    "c3arguments": {
                        "spec": {
                            "id": "{{record.data.boilerUnit}}",
                            "expression": "{{record.data.metricName}}",
                            "include": "start, end, dates, data"
                        }
                    },
                    "responseTransform": null,
                    "collection": false
                },
                "color": "#f0a63a",
                "unit": "%",
                "chartType": "line",
                "axis": "{~MyPackageUi.chart.consumption.axis~}",
                "tooltipGroup": "usagePlus",
                "hasPriorPeriodSeries": false,
                "disabled": false,
                "priorPeriodSeriesConfig": {
                    "color": "#d68510"
                }
            }
        ],
        "legendGroups": [
            {
                "id": "usagePlus",
                "name": "{~MyPackageUi.UsageChart.legendGroups.Usage~}",
                "series": [
                    "RiskScore"
                ]
            }
        ]
    }
}
0 Likes

#10

Why is your responseSelector set to "objs"? Your API does not return an objs field.

0 Likes

#11

Yes, you are right. It was just a typo as result of the amount of trials I did. Sorry.

Actually I set "responseSelector": null.

0 Likes

#12

Did "responseSelector": null solve the issue?

0 Likes

#13

The value below was a typo.

In my last trial I am using"responseSelector":null.

Is there the need to override some functions which are called during the rendering?

Moreover, since I did not found a solution with a custom dataSource component, I went back to the following data source:

ui module MyPackageUi {
    dataSource DashboardSibiloSourcePlot {
        "collection": false,
        "record": true,
        "c3type": "MyPackageTdSibilo",
        "c3function": "fetch",
        "responseSelector": "objs",
        "c3arguments": {
            "spec": {
                "filter" : "id == 'XXX'",
                "limit": -1,
                "include": "id, assets.id, name"
            }
        }
    }
}

Do you think that building a custom dataSource is the best choice, instead?

0 Likes

#14

dynamicTitle can access only the record on the component, so your component must have:

"data": {
  "record": "..."
}

In the case of dynamicTitle, if a record exists, the data will be directly resolvable from your binding expression, e.g. Risk Index for Sibilo: {{id}}, assuming the data source response is simply { "id": "xxx" }

You can verify the correctness of the binding expression in your dynamicTitle by checking the data on the data source.

For example, if it is a UIDataRecord:

env.findDataSource('MyDataSourceId').getData('binding.expression');

You can also call getData without any arguments to check the full data.

If it is a UIDataCollection:

C3.util.getDescendantProp(env.findDataSource('MyDataSourceId'), 'binding.expression');
0 Likes

#15

Hi @matt,
In the attached image what I get from the datasource.

How should I define the binding expression for “dynamicTitle”? Risk Index for Sibilo: {{id}} should be fine. And to retrieve it, can I use the folliwing string in the template of the UIViewUsageAndEvents type?

[quote=“rghera, post:9, topic:2376”]
“<%= component.get(‘title’) %>”,
[/quote]

0 Likes

#16

OK @matt, maybe I found where things get stuck. If you can see the image above, you should notice that I asked for ‘dtrCodes’ : ‘TN31.3SIB07CAAC’ but even after the metrics.load() I still see the previously set value, which was ‘TN31.3SIB10CAAC’.

Indeed, if I add:

chart.render();

instead of:

if (newFilter == '') {
   chart.render();
}

I get the previously selected value in the title. Therefore, now I found the way to visualize the title, but it renders the previolsly obtained value from the datasource, like the updating process was not synchronous.
@matt Am I missing something in order to force the update the dynamicTitle after the call of the UIAction which refresh the dataSource I use to set the correct title?

0 Likes

#17

@rghera to fix this into UIViewOnlineUsageAndEventsBoiler remix the function

onRenderDataLoaded: member function() : any js client

as below

function onRenderDataLoaded() {
  var dynamicTitle  = this.get('dynamicTitle'),
    environment   = this.get('environment'),
    pageParams    = environment && environment.get('site.currentPage.params'),
    record      = this.getData('record');
    var currentTitle;



  if (C3.util.notEmpty(dynamicTitle)) {
    currentTitle = UIHelper.matchDynamicContent(record, dynamicTitle, pageParams, null, environment)
    if (currentTitle != this.get('title')) {
    this.set('title', currentTitle);
    this.refresh();
    }
  }

  /**
   * We only need to call unmask here if this config is set to true. This enables
   * non-standard components (eg UsageAndEvents) to setup and remove masks as they see fit.
   */
  if (this.get("maskWhileWaitingForData")) {
    this.unmask();
  }
}

credit to @AlessandroT

0 Likes