How to avoid errors from onchange methods on lightning-input? - salesforce-lightning

I'm trying to learn the basics of Lightning Web Components and I'm having trouble getting the value of a lighting-input element.
I understand that it's designed for one way data binding instead of two way (a decision that I find questionable), but I can't get an onchange method to work either. I'm running this sample on the Lighting playground:
//app.html
<lightning-input
label="test"
onchange={handleChange}>
</lightning-input>
//app.js
import { LightningElement, track, api } from 'lwc';
export default class App extends LightningElement {
handleChange(event) {
console.log(event)
}
}
And making any change to the input in the template gives me the following error:
Error: Disallowed method "appendChild" in ShadowRoot.
Why does the onchange method not work as expected and should I go about making it work as intended?

console.log() works in playground.
You have to write this way - console.log(event.target.value)
Replace it in your code & it will print values..!!

Turns out the problem was with using console.log(). Seems that it has some issues working in the Playground.

Related

Ag Grid Properties do not exist while using TypeScript

I'm trying to use TypeScript and AgGrid and using an example similar to https://www.ag-grid.com/react-data-grid/column-sizing/#resizing-example
The issue is that when using TypeScript I get different values than when I'm trying to use regular JavaScript.The main property I'm trying to get back is api
https://gitpodio-templatetypescr-ze7tjimg9lm.ws-us43.gitpod.io/ - here is a gitpod that I created. If you look at the console.log I get most of the same properties, but I'm not getting the api property to make changes.
*** Edit ***
Actually one thing I just noticed is that if I use
import { AgGridReact } from 'ag-grid-react'; it works properly
but if I use
import { AgGridReact } from '#ag-grid-community/react'; it doesn't work. Why would there be a difference and what am I missing?
*** Edit 2 ***
Why do I need to at least put in one module to get the api? Is there a core module that I can put in, instead of something like the ClientSideRowModelModule to trigger the api?

Go to definition, go to implementation, autogenerate import for Ember

Im using Ember with VS Code.
What I need is to generate import string on a fly when I encounter dependency. For example I write someting like:
#tracked isLarge = false;
But I don’t have “#tracked” imported yet. So the otion could be to set the coursor on #tracked, press something like “Action + .” and pick “generate import”. It should generate import string:
import { tracked } from '#ember/tracking';
But it doesn’t work out of the box. How can I do that?
UPDATE: the same question about:
go to definition
go to implementation
cmd+click to navigate to implementation/component
You can use the extension My Code Actions
You can create actions that just insert the text independent of an error.
"my-code-actions.actions": {
"[javascript]": {
"import tracked": {
"where": "insertAfter",
"insertFind": "^import",
"text": "import { tracked } from '#ember/tracking';\n"
}
}
}
The key combo to use is the Code Action combo: Ctrl+.
If you get a diagnostic (PROBLEM panel, and squiggle) you can use that to further customize the action and you can use text from the diagnostics message.
I'm current adding the possibility to make multiple edits in an action and to use further customization and generalization.
"Ember Language Server" brings some solution. But it works mostly with library code that has .d.ts typings.
In case of custom JS code it still doesn't work.
So there is no straight solution. Only 2 ways:
Write .d.ts typing for custom code JS files
Move project to typescript

How to specify event handlers when using lit-html?

The main documentation under [Writing Templates] the following example for binding an event handler with lit-html is provided.
html`<button #click=${(e) => console.log('clicked')}>Click Me</button>`
Adding this a simple page with the default render and html functions imported and calling render however doesn't seem to render the button. If you remove the #click event binding then the button is rendered. There must be something I'm missing or a serious bug in the library.
version: 0.10.2
The links below relate to how events handler bindings work in lit-html:
https://polymer.github.io/lit-html/guide/writing-templates.html
https://github.com/Polymer/lit-html/issues/399
https://github.com/Polymer/lit-html/issues/145
https://github.com/Polymer/lit-html/issues/273
https://github.com/Polymer/lit-html/issues/146
The previous accepted answer was wrong. lit-extended is deprecated and that workaround only worked for a period in 2018 while lit-html was switching over to the new syntax.
The correct way to consume an event is:
html`<button #click=${e => console.log('clicked')}>Click Me</button>`
You can configure the event by assigning an object with a handleEvent method too:
const clickHandler = {
// This fires when the handler is called
handleEvent(e) { console.log('clicked'); }
// You can specify event options same as addEventListener
capture: false;
passive: true;
}
html`<button #click=${clickHandler}>Click Me</button>`
There is also lit-element which gives you a base for building web components with Lit and TypeScript enhancements to move the boilerplate noise of creating event handlers into decorators:
#eventOptions({ capture: false, passive: true })
handleClick(e: Event) { console.log('clicked'); }
render() {
return html`<button #click=${this.handleClick}>Click Me</button>`
}
It appears that in order to use event handler bindings one must not use the standard lit-html API but instead lit-extended which appears to be distributed along with lit-html. Changing import statement to import lit-extended and changing the attribute syntax as shown below seems to work for me.
Before:
import { html, render } from "lit-html";
html`<button #click=${(e) => console.log('clicked')}>Click Me</button>`
After (working):
import { html, render } from "lit-html/lib/lit-extended";
html`<button on-click=${(e) => console.log('clicked')}>Click Me</button>`
Note that the #click syntax didn't seem to work for me at all regardless of what several examples show in the GitHub issues as well as the main documentation. I'm not sure if the above syntax is the preferred way or only way to do event binding but it seems to be one that is at least working.
To me it looks like this may be a good candidate for contributing improvements to the lit-html documentation.

Angular Dynamic form observable property binding

I have a problem with some dynamically generated forms and passing values to them. I feel like someone must have solved this, or I’m missing something obvious, but I can't find any mention of it.
So for example, I have three components, a parent, a child, and then a child of that child. For names, I’ll go with, formComponent, questionComponent, textBoxComponent. Both of the children are using changeDetection.OnPush.
So form component passes some values down to questionComponent through the inputs, and some are using the async pipe to subscribe to their respective values in the store.
QuestionComponent dynamically creates different components, then places them on the page if they match (so many types of components, but each questionComponent only handles on one component.
some code:
#Input() normalValue
#Input() asyncPipedValue
#ViewChild('questionRef', {read: ViewContainerRef}) public questionRef: any;
private textBoxComponent: ComponentFactory<TextBoxComponent>;
ngOnInit() {
let component =
this.questionRef.createComponent(this.checkboxComponent);
component.instance.normalValue = this.normalValue;
component.instance. asyncPipedValue = this. asyncPipedValue;
}
This works fine for all instances of normalValues, but not for asyncValues. I can confirm in questionComponent’s ngOnChanges that the value is being updated, but that value is not passed to textBoxComponent.
What I basically need is the async pipe, but not for templates. I’ve tried multiple solutions to different ways to pass asyncValues, I’ve tried detecting when asyncPipeValue changes, and triggering changeDetectionRef.markForChanges() on the textBoxComponent, but that only works when I change the changeDetectionStrategy to normal, which kinda defeats the performance gains I get from using ngrx.
This seems like too big of an oversight to not already have a solution, so I’m assuming it’s just me not thinking of something. Any thoughts?
I do something similar, whereby I have forms populated from data coming from my Ngrx Store. My forms aren't dynamic so I'm not 100% sure if this will also work for you.
Define your input with just a setter, then call patchValue(), or setValue() on your form/ form control. Your root component stays the same, passing the data into your next component with the async pipe.
#Input() set asyncPipedValue(data) {
if (data) {
this.textBoxComponent.patchValue(data);
}
}
patchValue() is on the AbstractControl class. If you don't have access to that from your question component, your TextBoxComponent could expose a similar method, that can be called from your QuestionComponent, with the implementation performing the update of the control.
One thing to watch out for though, if you're also subscribing to valueChanges on your form/control, you may want to set the second parameter so the valueChanges event doesn't fire immediately.
this.textBoxComponent.patchValue(data, { emitEvent: false });
or
this.textBoxComponent.setValue(...same as above);
Then in your TextBoxComponent
this.myTextBox.valueChanges
.debounceTime(a couple of seconds maybe)
.distinctUntilChanged()
.map(changes => {
this.store.dispatch(changes);
})
.subscribe();
This approach is working pretty well, and removes the need to have save/update buttons everywhere.
I believe I have figured out a solution (with some help from the gitter.com/angular channel).
Since the values are coming in to the questionComponent can change, and trigger it's ngOnChanges to fire, whenever there is an event in ngOnChanges, it needs to parse through the event, and bind and changes to the dynamic child component.
ngOnChanges(event) {
if (this.component) {
_.forEach(event, (value, key) => {
if (value && value.currentValue) {
this.component.instance[key] = value.currentValue;
}
});
}
}
This is all in questionComponent, it resets the components instance variables if they have changed. The biggest problem with this so far, is that the child's ngOnChanges doesn't fire, so this isn't a full solution. I'll continue to dig into it.
Here are my thoughts on the question, taking into account limited code snippet.
First, provided example doesn't seem to have anything to do with ngrx. In this case, it is expected that ngOnInit runs only once and at that time this.asyncPipedValue value is undefined. Consequently, if changeDetection of this.checkboxComponent is ChangeDetection.OnPush the value won't get updated. I recommend reading one excellent article about change detection and passing async inputs. That article also contains other not less great resources on change detection. In addition, it seems that the same inputs are passed twice through the component tree which is not a good solution from my point of view.
Second, another approach would be to use ngrx and then you don't need to pass any async inputs at all. Especially, this way is good if two components do not have the parent-child relationship in the component tree. In this case, one component dispatches action to put data to Store and another component subscribes to that data from Store.
export class DataDispatcherCmp {
constructor(private store: Store<ApplicationState>) {
}
onNewData(data: SomeData) {
this.store.dispatch(new SetNewDataAction(data));
}
}
export class DataConsumerCmp implements OnInit {
newData$: Observable<SomeData>;
constructor(private store: Store<ApplicationState>) {
}
ngOnInit() {
this.newData$ = this.store.select('someData');
}
}
Hope this helps or gives some clues at least.

Agile Toolkit addExpression and DSQL

I have been following the book provided by the agile toolkit and am at this portion:
http://agiletoolkit.org/learn/app/logic
Unfortunately the dsql api lacks some clarity and the calculated fields example on that page is just not working.
I tried using:
class Model_DVD extends Model_Table {
public $table='dvd';
function init(){
parent::init();
$this->hasOne('Movie');
$this->addField('code');
$this->addfield('is_rented')
->type('boolean')
->calculated(true);
}
function calculate_is_rented(){
return $this->add('Model_Rental')
->dsql()
->field('id')
->where('rental.dvd_id=dvd.id')
->where('is_returned!=','Y')
->select()
;
}
}
This returns 1 every time so I tried to follow:
Calculated field always returns 1 - atk 4.2
And now I'm not sure how to create this properly; I know the following works but I kind of made it up as I went and have NO IDEA why it works, and why I need the count() - etc etc.
How do I go about getting the calculated field to work? Can someone please explain it in this example, I learn best by examples so I wanted to follow through with it.
My working code that I don't understand:
class Model_DVD extends Model_Table {
public $table='dvd';
function init(){
parent::init();
$this->hasOne('Movie');
$this->addField('code');
$this->debug();
$this->addExpression('is_rented')->set($this->add('Model_Rental')
->addCondition('dvd_id', $this->_dsql()->getField('id'))
->count()
->where('is_returned!=','Y'))
->datatype('boolean')
->enum(array('Y', 'N'));
}
}
Thanks!
DSQL APP tutorial is for Agile Toolkit 4.1.
Please watch the screencasts http://youtube.com/theagiletoolkit for updated and more extensive tutorial.