Lightning Web Component Reactive and Non Reactive Properties - lwc

I have a Lightning Web Component with 2 private properties. One property is reactive via track, and the second is non-reactive (not decorated).
This is the HTML file:
<template>
<table style="background: white;">
<tr>
<td>
Reactive Private Property:
<lightning-input type="text" onchange={reactiveHandler}></lightning-input>
Value: {reactiveProperty}
</td>
</tr>
<tr>
<td>
Nont-Reactive Private Property:
<lightning-input type="text" onchange={nonReactiveHandler}></lightning-input>
Value: {nonReactiveProperty}
</td>
</tr>
</table>
</template>
This is the JS file:
import { LightningElement, track } from 'lwc';
export default class ReactiveAndNonReactiveProperties extends LightningElement {
#track reactiveProperty;
nonReactiveProperty;
reactiveHandler(event) {
this.reactiveProperty = event.target.value;
}
nonReactiveHandler(event) {
this.nonReactiveProperty = event.target.value;
}
}
As you can see, only one property is decorated with #track. However, when I type something in the input text of the non-reactive property, it is still rendered on the screen, which should not happen until I change the value in the input text of the reactive property.

Correction in provided answer: According to Spring'20 release, all primitive properties are reactive by default, however, if the value held by property is either an array or an object, then you'll need to specify the property reactive using #track decorator

According to Spring'20 release , all properties are by default reactive. Even if you don't use "track" , it will be rendered on the screen.

Related

(GWT) - Why doesn't my call to TableSectionElement.setInnerSafeHtml() get rendered to the DOM?

I have a legacy application that uses UIBinder to render the UI. I am trying to spit out records into a standard element. In my view.ui.xml file, I have a table with my semantic element setup and a element with the appropriate ui:field.
<table>
<thead>
<tr>
<th> </th>
<th>Customer #</th>
<!-- Shortened for brevity -->
</tr>
</thead>
<tbody ui:field="tbody"><!-- Note the ui:field defined -->
</tbody>
</table>
In my view.java class, I have that ui:field coupled to a TableSectionElement.
public class View extends Composite implements ViewPresenterInterface {
#UiField TableSectionElement tbody;
public TableSectionElement getTbody() {
return tbody;
}
}
In my class that handles rendering the table elements, the code is fairly straight-forward:
private void renderOrderTable() {
List<OrderToReviewProxy> sortedList = sortOrders(orders);
SafeHtmlBuilder sb = new SafeHtmlBuilder();
for(OrderToReviewProxy order : sortedList) {
sb.appendHtmlConstant("<tr>");
sb.appendHtmlConstant("<td>");
sb.appendEscaped(order.getCustomerNumber());
sb.appendHtmlConstant("</td>");
sb.appendHtmlConstant("</tr>");
}
view.getTbody().setInnerHTML(""); // Breakpoint A
view.getTbody().setInnerSafeHtml(sb.toSafeHtml()); // Breakpoint B
}
At Breakpoint A, inspecting view.getTbody() shows me the <tbody></tbody> element empty, as expected.
Stepping over Breakpoint B, inspecting view.getTbody() shows me the <tbody> element with the expected HTML that the SafeHtmlBuilder generated during the for loop.
However, when I look at the rendered page, the <tbody> element has no child nodes. None of the HTML from calling sb.toSafeHtml() seems to get written into the DOM. Can anyone help me out here? I have no idea why this isn't working.

TYPO3 Extbase: Assigning 2 different objects to the view?

I have a showAction in my Controller of the class "Termin" that not only assigns the object of that class to the view, but it should also pass a second object that is in relation to that object via the getter.
public function showAction(\...\Kundentermine\Domain\Model\Termin $termin)
{
$this->view->assign('termin', $termin);
$this->view->assign('kaufmnnisch', $termin->getZugewkaufmaennisch());
}
The corresponding template should not only show details for the "Termin" object, but also have a link to the show action of another Controller which i pass the second assigned object "kaufmnnisch" to.
<tr>
<td>
<f:link.action action="show" controller="Kaufmnnisch" arguments="{kaufmnnisch : kaufmnnisch}">
<f:translate key="tx_kundentermine_domain_model_termin.kaufmaennisch" />
</f:link.action>
{termin.kaufmaennisch}
</td>
<td>
</td>
</tr>
But i get an error:
Argument 1 passed to ...\Kundentermine\Controller\KaufmnnischController::showAction() must be an instance of ...\Kundentermine\Domain\Model\Kaufmnnisch, null given
Why is the object NULL? Testing "$termin->getZugewkaufmaennisch()" inside a va_dump() within the TerminController gets me the correct object, so why isn't the object assigned to the view?
Edit: As requested, here is the show Action of the Controller "Kaufmnnisch" that gets the object $kaufmnnisch, which supposedly is NULL on arrival, so to speak.
/**
* action show
*
* #param \...\Kundentermine\Domain\Model\Kaufmnnisch $kaufmnnisch
* #return void
*/
public function showAction(\...\Kundentermine\Domain\Model\Kaufmnnisch $kaufmnnisch)
{
$this->view->assign('kaufmnnisch', $kaufmnnisch);
}
Edit2: Problem solved...
The problem was that the object was assigned to the template, but from there it should have been passed to a partial as an argument. So it never arrived ath the point:
<tr>
<td>
<f:link.action action="show" controller="Kaufmnnisch" arguments="{kaufmnnisch : kaufmnnisch}">
<f:translate key="tx_kundentermine_domain_model_termin.kaufmaennisch" />
</f:link.action>
{termin.kaufmaennisch}
</td>
<td>
</td>
</tr>
Now, after i added it here:
<f:render partial="Termin/Properties" arguments="{termin:termin, kaufmnnisch:kaufmnnisch}" />
It works flawlessly.

Angular 2 Dynamic Nested Form

Basically I want to create a dynamic form with nested objects like the picture below:
Pay offs are in an array on the model
We should be able to add/remove pay offs as needed.
The form should sync underlying form controls and model
The number of pay offs is arbitrary and should be loaded into the form from the model
There are no working examples that I could find as how to do this in Angular 2, although this was really easy to do in Angular 1.
Below is my original question, I've since updated it for clarification (see above):
First I just wanted to point out that I'm aware that a new version of Angular 2 rc.2 has just been released a few days ago. So the code for creating a dynamic, nested form may have changed some but there's not enough documentation to figure this out.
In the latest version(s) of Angular 2 (I'm currently using rc.1 but planning to update to rc.2) I need to create a form like this (pseudo-code of view):
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
<input type="text" ngControl="name">
<div *ngFor="let expense for expenses; let i = index;" control-group="expenses">
<input type="text" ngControl="expense.amount" [(ngModel)]="myModel.expenses[i].amount">
<input type="checkbox" ngControl="expense.final" [(ngModel)]="myModel.expenses[i].final">
</div>
<a class="button" (click)="addExpenseControl()">Add</a>
<a class="button" (click)="deleteExpenseControl()">Delete</a>
</form>
So the pseudo-code above won't work but to be honest because of lack of documentation I can't figure out how to wire something like this up. There's a few tutorials about nested ControlGroup but this won't fit the case here since we need to be able to dynamically add and remove control groups, and also I need to be able to sync them with a model.
I found this plunkr here provided by Angular team which allows adding of Controls to a form--but this is not adding/removing a ControlGroup, rather it's using ControlArray and I'm not sure if that applies here?
I'm very familiar with using the newer model-based Angular 2 forms however I'm grasping for resources in order to properly nest them (dynamically!), and tie this nested data into the main form model. How would I refer to nested controls in the view? Is the pseudo-code above even close? I'd post code from my controller but honestly I wouldn't know where to start when it comes to the nested expenses (ControlGroup ??) above...
I had to figure this out on my own because it seems that forms are still changing in Angular 2 and I've not seen any other examples similar to this (although it seems like a very common use-case).
Here is a plunkr of working example using Angular2 RC3.
I am using updated Angular 2 form code from this document.
app.component.ts (contains the form):
import { Component } from '#angular/core';
import {REACTIVE_FORM_DIRECTIVES, FormControl, FormGroup, FormArray} from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: 'app/app.html',
directives: [REACTIVE_FORM_DIRECTIVES],
providers: []
})
export class AppComponent {
form: FormGroup;
myModel:any;
constructor() {
// initializing a model for the form to keep in sync with.
// usually you'd grab this from a backend API
this.myModel = {
name: "Joanna Jedrzejczyk",
payOffs: [
{amount: 111.11, date: "Jan 1, 2016", final: false},
{amount: 222.22, date: "Jan 2, 2016", final: true}
]
}
// initialize form with empty FormArray for payOffs
this.form = new FormGroup({
name: new FormControl(''),
payOffs: new FormArray([])
});
// now we manually use the model and push a FormGroup into the form's FormArray for each PayOff
this.myModel.payOffs.forEach(
(po) =>
this.form.controls.payOffs.push(this.createPayOffFormGroup(po))
);
}
createPayOffFormGroup(payOffObj) {
console.log("payOffObj", payOffObj);
return new FormGroup({
amount: new FormControl(payOffObj.amount),
date: new FormControl(payOffObj.date),
final: new FormControl(payOffObj.final)
});
}
addPayOff(event) {
event.preventDefault(); // ensure this button doesn't try to submit the form
var emptyPayOff = {amount: null, date: null, final: false};
// add pay off to both the model and to form controls because I don't think Angular has any way to do this automagically yet
this.myModel.payOffs.push(emptyPayOff);
this.form.controls.payOffs.push(this.createPayOffFormGroup(emptyPayOff));
console.log("Added New Pay Off", this.form.controls.payOffs)
}
deletePayOff(index:number) {
// delete payoff from both the model and the FormArray
this.myModel.payOffs.splice(index, 1);
this.form.controls.payOffs.removeAt(index);
}
}
Notice above that I manually push new FormGroup objects into the form.controls.payOffs array, which is a FormArray object.
app.html (contains form html):
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<label>Name</label>
<input type="text" formControlName="name" [(ngModel)]="myModel.name" placeholder="Name">
<p>Pay Offs</p>
<table class="simple-table">
<tr>
<th>Amount</th>
<th>Date</th>
<th>Final?</th>
<th></th>
</tr>
<tbody>
<tr *ngFor="let po of form.find('payOffs').controls; let i = index">
<td>
<input type="text" size=10 [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount">
</td>
<td>
<input type="text" [formControl]="po.controls.date" [(ngModel)]="myModel.payOffs[i].date">
</td>
<td>
<input type="checkbox" [formControl]="po.controls.final" [(ngModel)]="myModel.payOffs[i].final">
</td>
<td>
<button (click)="deletePayOff(i)" style="color: white; background: rgba(255, 0, 0, .5)">x</button>
</td>
</tr>
</tbody>
<tr>
<td colspan="4" style="text-align: center; padding: .5em;">
<button (click)="addPayOff($event)" style="color: white; background: rgba(0, 150, 0, 1)">Add Pay Off</button>
</td>
</tr>
</table>
</form>
In the html form I link the form to the model on the inputs with statements like so:
... [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount" ...

AngularJS rows are displayed with empty data

I am testing out angularJS.
In app.js I have
function ListCtrl($scope, Restangular) {
Restangular.all("employee").getList().then(function(employee) {
$scope.employee = employee;
console.log($scope.employee.emp);
});
}
and in html I have
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Emp No</th>
<th>Name</th>
<th><i class="icon-plus-sign"></i></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="employee | filter:search | orderBy:'ename'">
<td>{{employee.empno}}
</td>
<td>{{employee.ename}}</td>
<td>
<i class="icon-pencil"></i>
</td>
</tr>
</tbody>
</table>
Problem I am facing is there are empty rows being displayed with no data being displayed.
What could be the reason for this?
Edit 1
JSON returned from server
{"emp":[{"empno":"7369","ename":"SMITH","hiredate":
"1980-12-17T00:00:00+03:00","job":"CLERK","mgr":"7902","sal":"800"},
{"comm":"300","empno":"7499","ename":"ALLEN","hiredate":
"1981-02-20T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1600"},
{"comm":"500","empno":"7521","ename":"WARD","hiredate":
"1981-02-22T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1250"},
{"empno":"7566","ename":"JONES","hiredate":
"1981-04-02T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2975"},
{"comm":"1400","empno":"7654","ename":"MARTIN","hiredate":
"1981-09-28T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1250"},
{"empno":"7698","ename":"BLAKE","hiredate":
"1981-05-01T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2850"},
{"empno":"7782","ename":"CLARK","hiredate":
"1981-06-09T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2450"},
{"empno":"7788","ename":"SCOTT","hiredate":
"1987-04-19T00:00:00+03:00","job":"ANALYST","mgr":"7566","sal":"3000"},
{"empno":"7839","ename":"KING","hiredate":
"1981-11-17T00:00:00+03:00","job":"PRESIDENT","sal":"5000"},
{"comm":"0","empno":"7844","ename":"TURNER","hiredate":
"1981-09-08T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1500"}]}
console log from chrome browser
[Object, Object, Object, Object, Object, Object, Object, Object,
Object, Object, Object, Object, Object, Object, route: "employee",
getRestangularUrl: function, addRestangularMethod: function, one:
function, all: function…]
0: Object
empno: "7369"
ename: "SMITH"
hiredate: "1980-12-17T00:00:00+03:00"
job: "CLERK"
mgr: "7902"
sal: "800"
Based on the JSON you've included it looks like $scope.employee should contain a one key called "emp", which is what you print to the console. You might need to change your ng-repeat to work with that.
Also, I'm unfamiliar with the form of your ng-repeat expression. I believe they are supposed to follow a form similar to "something in somethings" so in this case instead of just employee you may want that to be employee in employee.emp.
In a more general sense, the Angular Batarang plugin for Chrome is infinitely helpful for solving these sorts of problems.

Why is FilteringSelect in declarative dijit form causing invalid submit?

Help me understand this.
Isn't dijit.form.FilteringSelect (extended from ValidationTextBox) supposed to have property required = false by default?
Why is it that simply including a FilteringSelect in a declarative form like so below automatically results in dijit.form.Form.isValid() == false?
Even manually setting the filteringselect's required prop to false results in an invalid form submit. I feel like there's something I'm missing here.
I'm Using dojo toolkit version 1.6.1.
<!-- form.html -->
<form id="form" dojoType="dijit.form.Form">
<table>
<tr>
<td id="friend">
<select name="friend" id="friend-input" dojotype="dijit.form.FilteringSelect"></select>
</td>
</tr>
<tr>
<td>
<input type="submit" id="submit-input" value="Submit" label="Submit" dojotype="dijit.form.Button">
</td>
</tr>
</table>
</form>
/* form.js */
dojo.require("dijit.form.Button");
dojo.require("dijit.form.FilteringSelect");
dojo.require("dijit.form.Form");
dojo.ready(function() {
var form = dijit.byId("form");
var friendInput = dijit.byId("friend-input");
friendInput.required = false;
dojo.connect(form, "onSubmit", function(event) {
event.preventDefault();
if (form.isValid()) {
alert("Ready to submit data: " + dojo.toJson(form.get("value")));
} else {
alert("Form is not valid.");
}
});
});
Like Frode mentioned, we need to set the required to false.
But, lots of fields might be used. Not a good idea to set 'required' for each, in the dojo.ready section.
<select name="friend" id="friend-input"
dojotype="dijit.form.FilteringSelect" required="false"></select>
The better way is to mention it as attribute in the html itself. Let me give an example why it is better.
If the field is included in a tab, and if the tab is refreshed on certain actions, the html will be arsed again. So, in that scenario, the required will be true again for that field. So, therefore, provide it in the html declaration of the widget itself to avoid these scenarios.