How can i set a default value in component to a Mat-select triggering (change) - angular5

Hello i am trying to set a default value in a mat-select with multiple options in the component.ts, and i am managing to do so, but when i set a default value programatically the method that is supposed to execute when the value changes, does not execute, unless i change the value manually, i'm using (change) directive trigger to the method, is there anything else that i can use? like another directive.
I´m using ngModel to set the default value at the moment, setting a value to object.attribute
<mat-select [compareWith]="compareBasic" id="object" name="object"
[(ngModel)]="object.attribute" (change)="methodThatIWantToTrigger" class="full-width"
required> </mat-select>
I should have multiple options in the mat-select, but if there is only one option i want to select that value by default, and i need to know that value that is select in order to get some data from the database. i'm doing something like so:
if (this.array.length == 1){ // if it has only one option
this.object.attribute = this.array[0];
}
but this leads to some errors or fails.

Since you have 2 way data binding with [(ngModel)] you can use (ngModelChange) instead of (change) . It fires when the value changes.
this is stackblitz example

It's because mat-select has selectionChange event and it's only fired up when user change it
Event emitted when the selected value has been changed by the user.
(angular material docs)
If you want to emit this selectionChange you would have to try with programatically fire event, but you should avoid it. Maybe you can call this method inside your code? Give us your use case, maybe there is a better way.
EDIT
Due to your use case, you could go with ChangeDetectorRef.detectChanges(), so basicly, if you set your default value and you want to be sure, that it is selected in UI you can do something like this. It's a bit hacky, but it will work.
if (this.array.length == 1){ // if it has only one option
this.object.attribute = this.array[0];
// this will force to refresh value in mat-select
this.cdr.detectChanges()
// here you can call your `methodThatIWantToTrigger` becaucse the view is updated.
}
Also don't forget to add it in your constructor in component.ts and import it Change
import { ChangeDetectorRef } from '#angular/core';
constructor(private cdr: ChangeDetectorRef) { }

Related

Quasar: Is there any way to get the value typed into a QSelect with use-input before it gets removed on blur?

Per the docs you can add the attribute use-input to a QSelect component to introduce filtering and things of that nature: https://quasar.dev/vue-components/select#Native-attributes-with-use-input.
However, if you type something into one of these fields and click outside of it, the text gets removed.
Is there any way to grab that text in Vue before it gets removed and do something with it?
Since v1.9.9 there is also a #input-value event described in the q-select api.
As the api says it's emitted when the value in the text input changes. The new value is passed as parameter.
In the examples there's a filter function, so there you can save it in a data variable:
methods: {
filterFn (val, update, abort) {
update(() => {
this.myDataVariable = val;
})
}
}

VueJs 2 - Force Checkbox DOM value to respect data value

I cannot for the life of me figure this out.
Fiddle at: https://jsfiddle.net/s8xvkt10/
I want
User clicks checkbox
Then based on separate conditions
Checkbox calculatedCheckedValue returns a data property /v-model of true/false
And the checked state reflects the calculatedCheckedValue
I can get:
The calculatedCheckedValue calculates and interpolates properly
But I fail at:
Having the :checked attribute render the calculatedCheckedValue properly in the DOM
e.g. If a false checkbox is clicked and the calculatedCheckedValue still returns false, the checkbox toggles onscreen between checked and unchecked
I’ve tried:
Using a v-model with a custom set that does the calculation and sets the local state which the custom get returns
Imitating a v-model using #change.prevent.stop="updateCalculatedValue" and :checked="calculatedValue"
Assuming the #change happens after the #click (which i think is wrong) and using #click.prevent.stop="updateCalculatedValue" and :checked="calculatedValue"
The model is working and rendering the calculated value as string in a DOM span,
but the checked value doesn't seem to respect the model value
Can someone please help me out?
I've solved the problem at: https://jsfiddle.net/pc7y2kwg/2/
As far as I can tell, the click event is triggered even by keyboard events (src)
And happens before the #change or #input events (src)
So you can click.prevent the event, but you need to either prevent it selectively or retrigger the event after the calculation.
//HTML Template
<input :checked="calculatedValue5" #click="correctAnswer" type="checkbox">
//VUE component
data() {
return {
calculatedValue5: false,
};
},
methods: {
correctAnswer(e){
const newDomValue = e.target.checked;
this.calculatedValue5 = this._calculateNewValue(newDomValue);
if(this.calculatedValue5 !== newDomValue){
e.preventDefault();
}
}
}
I think this is preventing checkbox value from updating before the data value, which seems to break the reactivity. I'm not sure the technical reason why, but the demo does work

How to refactor repetitive attributes in Vue.js

Suppose I have a form and many fields in it. I want to subscribe to change for each form field. I will have to add #change="doSome" for every field. If I have many fields it gets somewhat repetitive. How do I refactor it?
You can listen for the change event on the form tag itself instead of listening on the individual inputs.
<form #change="doSomething"> will run the function doSomething() when something inside the form has changed eg: if you type in an input and release focus
In the doSomething function, you want to find out what element changed. We get this info from the event parameter provided from the input event:
methods: {
doSomething(event) {
this.lastEvent = event.target.value;
}
}
You can see this in effect on this Codepen example
If the form element is a child of an element inside a component like so:
<template>
<div>
<form></form>
</div>
</template>
the #changeevent-listener will not work as there is nothing that changes on the root element (div) on the component.
In this case, we need to add the .native modifier, like so: #change.native="doSomething".

How to bind custom event handler from vue-touch-events to a custom component in Vue.js

I'm currently implementing a classical Minesweeper game in Vue.js which is working fine so far https://github.com/franktopel/defuse, Demo http://connexo.de/defuse/.
Now I would like to add touch support because as of now, to mark a field as "contains a mine" it is required that you right-click the field. Right-clicking is obviously not available on touch devices, so I would like to add longtap support. I'm using native events click and click.right from the parent component for each field, so the field does not handle the events, but the parent component that instantiates these fields does.
I've found this https://github.com/jerrybendy/vue-touch-events and added it to my project, yet it seems I cannot use this on the component tag (see https://github.com/franktopel/defuse/blob/master/src/components/Defuse.vue):
<m-field
v-for="field in row"
:field="field"
:key="`${field.x},${field.y}`"
#click.native="open(field)"
#click.right.native.prevent="toggleBombMarker(field)"
v-touch:longtap="toggleBombMarker(field)"
></m-field>
because that marks all fields without any user interaction and the console keeps producing
You may have an infinite update loop in a component render function.
This is how my field objects (which I'm passing to the field component) are created:
let Field = function (x, y) {
this.x = x
this.y = y
this.isOpen = false
this.hasBomb = false
this.isMarked = false
this.numNeighbourBombs = null
}
module.exports = Field
I have also tried emitting a custom event from inside my field component, yet I don't know how I can pass the triggering field to the event handler from there.
Can anyone push me in the right direction?
According to the vue-touch-events docs, the v-touch directive doesn't work in the same way as v-on; v-touch must be given a function to execute, whereas the toggleBombMarker(field) expression probably returns undefined.
If you want to pass extra parameters to the v-touch handler, your handler must return a function like this:
methods: {
toggleBombMarker(field) {
return () => {
// your handler code here
}
}
}

Aurelia: Programmatically changing reference value doesn't change model

We have a situation where we utilize a component in multiple web pages (a note editor). This note editor takes a value from an input element on the page and places that value into a component where the user can modify it. The user types in the note editor and clicks Submit. The note editor component then passes the new value back to the input on the original page.
We are using the "ref" to pass the value back and forth. Everything works fine except that the model doesn't update when we set the value of the ref from the note editor component. We find we need to type once in order for it update the model. Here is a simple Gist to illustrate our example:
This is just a simple example of programmatically setting the ref's value.
Note how "The value of my input is" stays as "Hello" but the input field changes to "Value Changed!!!!" when you press the button.
https://gist.run/?id=fab025d6b99a93f9951b1a6e20efeb5e
A few things to note:
1) We'd like to use Aurelia's "ref" instead of an "id" or "name".
2) We've tried to pass the model instead of the "ref". We can get the value of the model successfully and put it in the note editor, but the model doesn't update when we pass it back.
UPDATE:
We have an answer (thanks!). Here is the code we tried to pass the model (so we wouldn't even need to use the ref). This failed for us.
Here is the View of the page.html
<TextArea value.bind="main.vAffectedClients" style="width:94%;" class="editfld" type="text" rows="6"></TextArea>
<input class="butn xxsBtn" type="button" value="..." click.trigger="OpenNoteDivAurelia(main.vAffectedClients)" />
Here is the View-Model of the page.js
import {NoteEditor} from './SmallDivs/note-editor';
...
#inject(NoteEditor, ...)
export class PageName {
constructor(NoteEditor, ...)
{
this.note = NoteEditor;
...
}
OpenNoteDivAurelia(myTargetFld)
{
this.note.targetFld = myTargetFld;
this.note.vHidTextArea.value = myTargetFld;
this.note.show();
}
}
This part opens our component (note-editor) and successfully places the targetFld value inot our TextArea in the component.
Here is the View-Model when placing the value BACK to page.js/page.html
CloseNote(populateFld)
{
if (populateFld)
{
//This is the line that doesn't seem to work
this.targetFld = vHidTextArea.value;
}
this.isVisible = false;
}
This last function "CloseNote" is the one that doesn't work. The model (which we believe is pointed at this.targetFld) does not get the value of the textarea. It does not error, it simply does not do anything.
The events that Aurelia attaches to be notified of an input changing don't fire when you programmatically set the value property on an input. It's simple enough to fire one of these events yourself, though.
export class App {
mymodel = {"test":"Hello"};
changeValByRef(myinput)
{
myinput.value = "Value Changed!!!!";
myinput.dispatchEvent(new Event('change'));
}
}
You actually don't need to pass the ref in to the function, anytime you use the ref attribute, a property is added to your VM w/the name you use in the ref attribute. So the above example could have been accomplished using this.myinput instead of the passed in object, since they're the same.
Here's a working example: https://gist.run/?id=7f96df1217ac2104de1b8595c4ae0447
I'd be interested to look at your code where you're having issues passing the model around. I could probably help you figure out what's going wrong so you don't need to use ref to accomplish something like this.