Vuejs #click event works wrong from time to time - vue.js

Recenty, I noticed that the checkboxes that I wrote a click event worked incorrectly from time to time. Not everytime but sometimes their #click event works in reverse. Here is what I am trying to tell;
<label class="form-switch">
<input type="checkbox" #click="showElement = !showElement"/>
</label>
I have a simple form switch and there are some css on it which I didn't put here, it looks like a toggle switch. It toggles a data which is showElement. The default state of the data is false and when you click on the toggle it becomes true and false respectively.
<div v-show="showElement>
some content here
</div>
When the showElement is true I want to display the above div, and when it becomes false, I want it to be hidden. There is no problem with that. But here is my question;
If my observation is corret, usually when the project is started for the first time, in other words, when I type npm run serve and start the project, I immadiately go and check if it is working fine or not so I click on the checkbox very quickly over and over andsometimes the click event breaks down and starts working backwards. I mean, when the switch is off, the content is visible, when it is false, the div is showing, but it should be reversed. This happens sometimes when I browse the other pages of the project and return to this component. Is this a bug? Or am I doing something wrong? In the first version of the code it was like below;
<label class="form-switch" #click="showElement = !showElement>
<input type="checkbox"/>
</label>
I accidentally typed the click event to the label element instead of input. I thought that might be the problem. I am still thinking that is the problem but the bug that I explained above still happens sometimes. Do you know why? Can anyone explain?

Usually i find that with checkboxes in VueJS #Click is not the way to go. Try to use #Change event instead. This should make it more consistent.
The reason behind this is that the click event triggers before the value has been updated. Therefore creating the risk of it overwriting the old value instead of the newly updated one
EDIT:
I actually think in this case you might even be able to get around this by simply adding a v-model to the checkbox like so:
v-model="showElement" instead of having either #click or #change.
Verison 1:
<label class="form-switch">
<input type="checkbox" #change="showElement = !showElement"/>
</label>
Version 2:
<label class="form-switch">
<input type="checkbox" v-model="showElement"/>
</label>
Check this fiddle: https://jsfiddle.net/x4wykp2u/4/
Hope this makes sense

Related

Can't go to next step after going back using vee-validate

I'm using vee-validate v3 to validate a multi step form then call axios. I use handleSubmit and added ValidationObserver with unique keys for each step. Now my issue is after moving to the next step and then going back to step 1, the next button is not firing even if there's no field errors found.
Here's the code structure: https://codesandbox.io/s/codesandbox-forked-6hrh4?file=/src/Demo.vue
Note:
the currentStep on my real project is from local storage.
The sample code is just the structure since the actual fields are too long.
A <button> placed inside a <form> element defaults to type="submit".
Which means in any <form> you need to call preventDefault() on a <button> click if you don't want the form submitted.
Changing #click into #click.prevent fixes the issue (because the form is no longer submitted when clicking Previous, therefore currentStep is no longer increased by 1 right after it is decreased by the #click expression - making it look like nothing happened. In fact, both the #click expression and the onSubmit get executed if you don't use .prevent modifier).
As an alternative, you can change the <button> into an anchor:
<a href class="btn" #click="currentStep--" v-text="Previous" />
although I'd personally go with preventing default.

vue-js button element with no type --> submit

We have been having a very strange problem in a vue-js (#vue-cli, Build version) application. There was a button in App.vue that had an onclick
<button #click="goIRList" id="irlistBtn">IR List</button>
that was working for fine for some users, every time, and not others. For the others, I checked (with alerts) that the goIRList code was not reached at all. These others had this issue with any browser we tried. They did not have the issue when Vue+Developer Tools was open - making it harder for me to debug it.
Instead of the goIRList code, these users got a page with just the error message
Cannot GET /menu.csp
That was especially perplexing, because menu.csp is code from a completely different system, though one also associated with the same computer. It is never referenced in the vue-js application.
Eventually I realized that what was happening was a submit, even though the button was not in any form on the page [I had thought that a button is only a submit type by default, if it is inside a form]. As soon as I added a type, <button type="button"...>, the issue went away completely.
Could someone explain why this was happening? And why did it work for some users? Thanks!
Update: and now the problem is back, same conditions: for them and not for me. Nothing changed. I also added .prevent to the button's onclick:
<button type="button" #click.prevent="goIRList" id="irlistBtn">IR List
and no help. So maybe that wasn't the issue. Anyhow, now I'm asking for a fix as well as an explanation. Thanks!
Add a "submit" event catcher in the form, and prevent the default action.
<template>
<div id="app">
<form #submit="submit">
<input value="Tom">
<button>Submit</button>
</form>
</div>
</template>
<script>
export default {
name: "App",
methods: {
submit(e) {
e.preventDefault();
console.log("Success!");
}
}
};
</script>
And someone pointed out the answer to me. Ridiculous: those two buttons were hidden underneath a logo image that was higher up on the page. It was the image that contained the bogus link.
I never got the error, because I can't see so well, so I keep the sizing high, and the buttons were far away from the logo. Others had a more normal page sizing, and everything got smaller except for that image; the invisible margin of the image went over the buttons, and someone trying to click on the buttons triggered the link.
I set the image to resize with the page and all is well.

Adding a loading indicator to Material Autocomplete

I'm working with Material Components for Angular 5, and have a form control that is an autocomplete for that is powered by a service call. Unfortunately, until the service call is complete, the autocomplete does not show, and due to some network latency issues we are experiencing at the moment, most users are finished typing before the service call completes. I'd like to show some kind of visual indicator that the autocomplete is loading, but cannot figure out how to do it.
The cheap solution we tried was to populate the autocomplete menu with the work "Loading" however this would require tweaking our filtering so that it didn't immediately get squashed when someone started typing a value that didn't start with l.
This is the basics of our HTML for one of the fields:
<mat-form-field [hideRequiredMarker]="true">
<input matInput [(ngModel)]="size" type="text"
placeholder="{{'OPTIONS.SIZE' | translate}}"
[matAutocomplete]="autoSize"
readonly="{{!model}}"
(keyup)="filterSizes()"
matTooltip="{{ 'OPTIONS.MODEL_REQUIRED' | translate }}"
[matTooltipDisabled]="model != ''" />
<mat-autocomplete #autoSize="matAutocomplete">
<mat-option *ngFor="let completeSize of filteredSizeNames" [value]="completeSize">
{{completeSize}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
The service call populates the filteredSizeNames variable when it completes. I looked through the material component pages without much luck figured out a soluation.
Use an indeterminate progress indicator bound to a "loading" variable that gets "turned on" when the service call starts and turned off when the service returns. One way is to place a progress circle in the form field as a prefix or suffix. You could also use a progress bar that is styled to position it directly over the form-field's underline (this is a lot of work to get the position just right, but it can be done and looks quite slick).
An easier solution would be to just use the form field label text updated the same way.

Angular 2 - display one component as drop down in another and grabbing the selected value in parent component

This is my very first Angular 2 project. I created a component (category) that would pull a list of categories from a service stack API and show as a dropdown like this:
<select (change)="onSelect($event.target.value)" [(ngModel)]="selectedCategory.Id">
<option *ngFor="let category of categoriesToDisplay"
value={{category.Id}}>{{category.Name}}
</option>
</select>
Selected Category is: {{selectedCategory.Id}} and Name is {{selectedCategory.Name}}
I have another component (AddExpense) which is a form, where the user can add in the amount, category and hit submit that would POST to another endpoint. For AddExpense component, this is how the .html looks
<form [formGroup]="expense" (ngSubmit)="fileExpense($event)">
Spent <input type="number" formControlName="amt" />
for <input type="text" formControlName="name" />
on <input type="date" formControlName="transdate" />
and file it under category <show-category></show-category>
<button type="submit">Add Expense</button>
</form>
My question is how do I figure out which category from the drop down was selected in the add expense form, when the drop down itself is rendered via the show-category component and pass it on as a form control item for add-expense component's .ts to use?
I might be needing to use the Input and output decorator, but not sure how to nab that particular item thats selected in the dropdown and pass it on as input to the add expense component.
Sounds like you're displaying your first component (ShowCategory) in the second (AddExpense).
Now there is a couple way to do this,
The most obvious is to use Output, ShowCategory will then emit (See EventEmitter and Output example) whenever the selected value is changed.
In the template of AddExpense, you'd simply have to write something like this,
<show-category (change)="category = $event.category"></show-category>
But that's ugly, and frankly it would've been cool if we can do this and let Angular handle the two-way binding,
<show-category [(ngModel)]="category"></show-category>
Turns out it's doable, you can check out documentation for NgModel here, but what's actually useful is an example implementation for a real control like a checkbox
You might've noticed in the example that NgModel is not mentioned anywhere, and that's because we only have to create the mechanism for NgModel to write/read value from your component.
If you search on Google for NgModel ValueAccessor, there's a bunch of blog posts to help you out.
Lastly, I personally doesn't suggest doing what you're doing now if the ShowCategory component is that simple, that increases complexity a little bit, and ShowCategory isn't doing much for now. But what you provided might very well be a minimum working example, so what do I know.
Happy coding!

How to forbid the browser from caching the status of a radio input?

I have listeners on the event of radio being checked, which works perfect for the first time.
But if I refresh the page,the radio input is checked because of browser cache,and the event is not fired at all.
How to avoid this trouble?
I've run into this a lot, especially with Firefox. I'm not sure its necessarily valid but i read somewhere recently that adding autocomplete="off" to your form tag will prevent any form element value caching.
There is a post here that provides a JavaScript solution that looks like this:
setTimeout(
function(){
document.getElementById( "form" ).reset();
},
5
);
EDIT
As your radio's arent in a form this should work:
<input type="radio" name="foo" value="bar" autocomplete="off">Bar
<input type="radio" name="foo" value="baz" autocomplete="off">Baz
You could propably unselect everything using javascript when the beforeunload event fires.