vue.js element selected by focus is not reactive - javascript

I have a listener to check what input was selected at last to add some kind of string/variable later into it.
created: function () {
document.addEventListener('focusin', this.focusChanged);
}
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target;
}
}
This seems to work fine, and when I click on an input field this.lastElement gets updated with the focused element. All these inputs have a v-model which can be a string in an object or just a plain string.
Now the thing is when I try to update the value by:
this.lastElement.value += variable;
Vue won't detect its changes, also in the Vue Developer tools the string won't get updated. But in the input field it does get updated. So this should be a reactivity thing.
When I add a new character into the input field (v-model) it does update again. So it's just when I update the string by this.lastElement it won't register its changes.
The thing is that the input fields are dynamic, so I don't know how many input fields are here and how many lists etc. So I need Vue to re-render the variable after the value of lastElement is updated.
Edit
I just tried it with an #focus here an example
<input v-model="testVar" #focus="lastElement = testVar">
If I update lastElement later on it doesn't update it for testVar but just for lastElement.

Changing values in DOM elements programmatically does not cause DOM events to fire. v-model relies on input (or change when using .lazy) events to update its bound variable. If you dispatch those events when you update the value in an input, the variable will react.
new Vue({
el: '#app',
data: {
items: ['one','two','three']
},
methods: {
addAddress() {
this.lastElement.value += 'address';
this.lastElement.dispatchEvent(new Event('input'));
this.lastElement.dispatchEvent(new Event('change'));
},
focusChanged(event) {
this.lastElement = event.target;
}
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item, index in items">
<input v-model="items[index]" #focus="focusChanged">
{{item}}
</div>
<button type="button" #click="addAddress">+address</button>
</div>

You could add a ref attribute to each of the inputs and use the ref to update their values. For example, an input element could be:
<input v-model="testVar" ref="input1" id="input1" #focus="focusChanged">
In your methods:
methods: {
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target.id;
}
},
}
And where you want to update the value: this.$refs[this.lastElement].value += variable;

Related

vue.js keyup, keydown events one character behind

I'm using the keydown/keyup events which call a javascript function that prints the value of input box to the console (and also the value of the currentTarget field of the event), and I am noticing it is a character late. For example, if I type hello into the input box, I only see hell in the console, until I press another key and then I see hello, even though by this point I've typed hello1. Why is this? And is there anyway around it?
Here's the HTML:
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
And the JS:
queryForKeywords: function(event) {
var self = this;
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
console.log("event value: " + event.currentTarget.value);
}
Because you are depending on the input's v-model to update the keywords property, the value won't update until the Vue component has re-rendered.
You can access the updated value of keywords in a callback passed to this.$nextTick like in this example:
new Vue({
el: '#app',
data() {
return { keywords: '' }
},
methods: {
queryForKeywords: function(event) {
this.$nextTick(() => {
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
}
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
</div>
The real problem doesn't has to do with vue.js at all
The problem hides behind the keydown event!
So when the event fires, the input value is NOT updated yet. Fiddle example
MDN - keydown event
In general, keydown it is used for informing you which key is pressed. And you can access it like this:
document.addEventListener('keydown', logKey);
function logKey(e) {
console.log(e.key)
}
As solution, you can use the keyup event: Fiddle
My recommendation is to use a custom v-model using :value and the #input event.
<input type="text" :value="keywords" #input="queryForKeywords">
And the script:
data: {
keywords: ''
},
methods: {
queryForKeywords(event) {
const value = event.target.value
this.keywords = value
if (value.length > 2) {
console.log("keywords value: " + this.keywords);
}
}
}
See it in action
The currently accepted answer is for an old version of vue, in the latest versions should be used #input instead of keypress or keyup.

How does ngModelChange works without providing model name in ngModel directive

How does ngModelChange() works?
(ngModelChange) is the #Output of ngModel directive. It fires when the model changes. You cannot use this event without ngModel directive
but I am not sure, how does(ngModelChange) it works, if I am use ngModelChange() event, even i am not providing model name to ngModel.
<input #gb type="text" pInputText class="ui-widget ui-text" **ngModel**
(ngModelChange)="functionName($event)">
Yes, ngModelChange() work without providing model name to ngModel.
cause of this happen, (ngModelChange) is the #Output of ngModel directive.
when insert some value in input that time emitEvent is become true which is by default false (so it not fire page load on initial time).
_this.updateValueAndValidity({ emitEvent: false });
you can find at \#angular\forms\esm5\forms.js ► line no 3850
If emitEvent is true, this
change will cause a valueChanges event on the FormControl to be emitted. This defaults
to true (as it falls through to updateValueAndValidity).
If emitViewToModelChange is true, an ngModelChange event will be fired to update the
model. This is the default behavior if emitViewToModelChange is not specified.
If emitModelToViewChange is true, the view will be notified about the new value
via an onChange event.
now question is that why get same value in $event which is inserted in input instead of ture, that cause
FormControl.prototype.setValue = /**
function (value, options) {
var _this = this;
if (options === void 0) { options = {}; }
(/** #type {?} */ (this)).value = this._pendingValue = value;
if (this._onChange.length && options.emitModelToViewChange !== false) {
this._onChange.forEach(function (changeFn) { return changeFn(_this.value, options.emitViewToModelChange !== false); });
}
this.updateValueAndValidity(options);
};
same file line no 3911 to 3919
In the Source code ngModelChange is just an event emitter.
#Output('ngModelChange') update = new EventEmitter();
It fires when the viewToModelUpdate function is executed.
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
ngModel can be anything and does not have a direct link to anything else. In the code it is declared and it is only used in a function called ngOnChanges
#Input('ngModel') model: any;
ngOnChanges(changes: SimpleChanges) {
this._checkForErrors();
if (!this._registered) this._setUpControl();
if ('isDisabled' in changes) {
this._updateDisabled(changes);
}
if (isPropertyUpdated(changes, this.viewModel)) {
this._updateValue(this.model);
this.viewModel = this.model;
}
}
I could be wrong here but it looks to me that ngModel is not the single source of truth but this.viewModel seems to be, because of this ngModel does not need a value for ngModelChange to work as it opporates seporatetly from the ngModel value.
Hope this helps.
you try it without ngModel
<select (change)="changed($event)">
<option *ngFor="let data of allData" [value]="data.id">
{{data.name}}
</option>
</select>
changed(e){
//event comes as parameter and then find data manually
//by using e.target.data
}
OR BY ID
<inputtype="text" #byid (change)="onChange(byid.value)" />
onChange(title:string){
alert(title);
}
You can try by passing id into input

DOM equivalent to jQuery `.detach()`?

I’m trying to remove an input field by clicking an “X button”. After it is removed it will not post its value when the form is submitted. A “+ button” appears that allows the user to add said input again. The input has an onclick event that opens a calendar and after reattaching, the calendar does not open on click anymore. I can’t use jQuery.
adderBtn.onclick = function (e) {
var elem = that.hiddenElems.shift();
that.collectionItemContainer.append(elem);
}
removerBtn.onclick = function (e) {
collectionItemElem.remove();
that.hiddenElems.push(collectionItemElem);
}
The question is how do I remove and reattach DOM nodes without losing the Events.
When you remove an element, as long as you keep a reference to it, you can put it back. So:
var input = /*...code to get the input element*/;
input.parentNode.removeChild(input); // Or on modern browsers: `input.remove();`
later if you want to put it back
someParentElement.appendChild(input);
Unlike jQuery, the DOM doesn't distinguish between "remove" and "detach" — the DOM operation is always the equivalent of "detach," meaning if you add the element back, it still has its handlers:
Live Example:
var input = document.querySelector("input[type=text]");
input.addEventListener("input", function() {
console.log("input event: " + this.value);
});
input.focus();
var parent = input.parentNode;
document.querySelector("input[type=button]").addEventListener("click", function() {
if (input.parentNode) {
// Remove it
parent.removeChild(input);
} else {
// Put it back
parent.appendChild(input);
}
});
<form>
<div>
Type in the input to see events from it
</div>
<label>
Input:
<input type="text">
</label>
<div>
<input type="button" value="Toggle Field">
</div>
</form>
If you remove the element without keeping any reference to it, it is eligible for garbage collection, as are any handlers attached to it (provided nothing else refers to them, and ignoring some historic IE bugs in that regard...).
To detach an element in function form:
function detatch(elem) {
return elem.parentElement.removeChild(elem);
}
This will return the 'detached' element

How to call a function when input is cleared in AngularJS?

I know Angular has simple syntax to display messages or update css, but what I'm trying to do is actually call a function.
<input ng-model="somefield">
<span ng-show="!somefield.length">Please enter something!</span>
<span ng-show="somefield.length">Good boy!</span>
This is my model vm.tagSearching = '' I can detect when I start typing in the input and see the value update. However once I get to the last letter, and I delete that I don't get an update.
I tried using $scope.watch
$scope.$watch('vm.tagSearching', function() {
alert('hey, var has changed!');
});
However this only fires once as the app initializes, but never again, even while typing.
Markup
<input class="tag-search-input"
type="text"
placeholder="Search Tags"
ng-model="tgs.tagSearching"
typeahead="t for t in tgs.fuzzyTagSearch($viewValue)">
Controller
function fuzzyTagSearch(word) {
console.log('fuzzyTagSearch',word);
if (word.length > 2) {
ApiFactory.getSearchResults(word).then(function(data) {
console.log('data',data.data.ticker_tags);
vm.terms = data.data.ticker_tags;
});
}
}
How would you accomplish this? I need to detect when the input is clear when the user backspaces / deletes all the letters so that I can reset the table.
You can simply set up an ng-change directive.
<input ng-model="tgs.tagSearching" ng-change="tgs.detectEmpty()">
vm.detectEmpty = function() {
if (vm.tagSearching.trim().length === 0) {
// it's empty
}
}

javascript: How to clean an input box when another input value changed?

I made one input (#child)auto suggestion base on another input(#parent). When the #parent input value changed, I want to clean the #child's value. I would like to learn how to do this, thanks!
You could subscribe to the onchange event of the parent and then clear the value of the child:
window.onload = function() {
// the document has loaded => we could access the DOM
// we subscribe to the onchange event pf the parent:
document.getElementById('parentId').onchange = function() {
// and when this event is triggered we clean the child
// element value
document.getElementById('childId').value = '';
};
};

Categories

Resources