geocomplete with Vue js -- location being erased - javascript

I'm trying to use jQuery geocomplete along with Vue.js to populate a form with geo data.
My code contains this form:
<form>
<label for="get-offer-location">location: </label><input id="get-offer-location" v-model="newoffer.location" type="text"/>
<div style="visibility:hidden">
<input name="lat" type="text" value=""/>
<input name="lng" type="text" value=""/>
</div>
</form>
After I click on a suggested location from the get-offer-location input, it looks like the field is filled out -- but then when I start typing in another field, the location input field reverts to just the letters I typed in for the location search. Try it out here by clicking "post", then "news" or "offers":
https://jsfiddle.net/dvdgdnjsph/157w6tk8/
Can anyone tell me what's wrong?

The problem you are having is that v-model binds on input, since the geolocation dropdown is a plugin that changes the value programatically the input event is not fired, so v-model is not updated. As a case, try typing a space after selecting a location, you will see that it sticks.
Fortunately, v-model is nothing more than syntactic sugar for v-on:input, so you can use v-on to fire your event instead. Considering that you are going to need to unfocus to get out of the box, the blur event is likely to be your best option, so you can simply do:
v-on:blur="newarticle.location = $event.target.value"
Unfortunately, JSFiddle won't let me save or update your Fiddle, but I did get it working with the code above.
For completeness, in case you want to use this behavior extensively, and because the Vue docs are fairly limited in this regard, you may write a custom directive to encapsulate the code:
Vue.directive('model-blur', {
bind: function(el, binding, vnode) {
el.addEventListener('blur', function() {
vnode.context[binding.expression] = el.value;
});
}
});
Then you can use like so:
<input v-model-blur="myVar" />
Here's the JSFiddle: https://jsfiddle.net/4vp6Lvmc/

Can't tell for sure. But it looks like jQuery plugin just changes input#get-article-location value, but not the Vue model. So when you trigger model update (e.g. editing headline) it overwrites complete location with whatever you typed in.

I have something like this for catch the geocomplete event and try to set the vueJS value :
$("#get-article-location")
.geocomplete({details: ".details"})
.bind("geocode:result", function (event, result) {
vm.newoffer.location = result.formatted_address;
console.log('done');
});
But something still appears wrong, I think you should really change the name of your vueJS instance (var vm) it may be use by another script and make troubles.

This is because v-model, as two-way binding, on the receiving-user-input way, listens to the input event on the input element, while js plugins (like jquery-geocomplete) obviously set input values via js, which leads to the view model's data not changing as we discussed in other answers.
To fix this, just listen to the geocode:result event of the plugin and manually change the data with code (there seems to be something wrong with jsfiddle so I'm posting it here):
var vueVM = this;
$("#get-offer-location").geocomplete({ details: ".details" });
$("#get-article-location")
.geocomplete({ details: ".details" })
/***** highlight start *****/
.bind("geocode:result", function(event, result){
console.log(result);
//tried result.something but couldn't find the the same thing as `this.value`
vueVM.newarticle.location = this.value;
});
/***** highlight end *****/
extra knowledge: the mechanism of v-model stated above is usually used in making reusable components, by receiving a value prop from the parent, and emitting an input event with the user-input value when detecting change on the input element in the component. And then we can use <my-component v-model='parentData'></my-component> as the child behaves exactly like a simple input element from the parent's perspective. example

Related

How to force dijit/form/TextBox to update the variable bound to it immediately - binding does not work as expected

I have an
<input id="myTextBox" data-dojo-type="dijit/form/TextBox" data-dojo-props="value: at(model, myValue)" />
When I try to copy the value from model.myValue to another input (e.g. by
window.setInterval(
function() { document.getElementById("theOtherInput").value = model.myValue; }, 1000);
I notice, that the value of myTextBox isn't synchronized with model.myValue until the <input> of myTextBox lost focus. I.e. - theOtherInput won't be updated unless I leave the input field myTextBox.
How to force dojo to synchronize the input with the bound variable with every keystroke? I thought that this should have been the default behavior or at least it should be possible with little effort, but I don't find the right attribute to do it. Any ideas?
The main problem for me is, when I submit the form by the enter key, the model.myValue has still the value before the input myTextBox got the focus. (and I don't want a hack with setInterval - I just want to have dojo doing its job of synchronizing the input with the model)
The closest you can get is setting intermediateChanges property to true and attach an onChange event. Something like this:
<input id="myTextBox" data-dojo-type="dijit/form/TextBox"
data-dojo-props="intermediateChanges: true"
data-dojo-attach-event="onChange: _myOnChange"
data-dojo-props="value: at(model, myValue)" />
Now, inside your widget, the _myOnChange function will run with each valuable keystroke or if the content is changed.
_myOnChange: function(newValue){
console.log(newValue);
document.getElementById("theOtherInput").value = newValue;
}

How to manipulate data-binding, knockoutJs

I have a customer who is a member of a web site. He has to fill a form every time which is really very often. That's why he wants me to develop an application for him to make this process automatic. When I use the webBrowser control to manipulate it, I am able to login but after that there are fields that contains data-binding. These fields are the ones I need to manipulate. When I push the data to necessary fields, it's not working, because in the html tag, there is no value attribute, instead it has data-binding. So my question is how can I manipulate and push data to these fields?
Thank you so much for your all help in advance.
Knockout uses data-binds to listen to changes in an input and update an underlying model. For example, the value binding listens to change events and writes the new value to a data-bound observable.
If you update a value attribute through code, the change event isn't triggered. You'll see the new value in the UI, but the javascript model won't be updated.
You can combat this by explicitly triggering a change. Here's an example:
Type in the input: you'll see a console.log that shows knockout gets updated
Press the button to inject a new value: you won't see a log: knockout isn't updated
Press the last button to trigger a change event. You'll notice knockout now updates the model.
Of course, you can combine the two click listeners into one function. I've separated them to get the point across.
// Hidden knockout code:
(function() {
var label = ko.observable("test");
label.subscribe(console.log.bind(console));
ko.applyBindings({ label: label });
}());
// Your code
var buttons = document.querySelectorAll("button");
var input = document.querySelector("input");
buttons[0].addEventListener("click", function() {
input.value = "generated value";
});
buttons[1].addEventListener("click", function() {
// http://stackoverflow.com/a/2856602/3297291
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
input.dispatchEvent(evt);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="text" data-bind="value: label">
<button>inject value from outside</button>
<button>let knockout know something changed</button>

Updating array model deselects iterated <Input> elements

Trying to implement a add/remove sub-item entry; increment/decrement buttons add slots into the array and input fields are added/removed automatically:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input [(ngModel)]="itemsInNewOrder[i]" xtype="text" name="order" title="order" />
</div>
This is working functionally, but every time a letter is entered into the input, the element is deselected and must be clicked again to enter yet one more letter. How can I solve this?
http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
The link above explains change detection in Angular 2. It also describes some other change detection strategies that you might find useful.
Basically since you did a 2 way binding [(ngModel)]="itemsInNewOrder[i]" to the itemsInNewOrder array, Angular is trying to be helpful and update the view for you.
Each keystroke updates your model (itemsInNewOrder)
Angular detects the update
Angular renders the view again with new form controls
Again, these are new form controls so the user hasn't focused one yet. This is why you have to click into the control after every entered character.
You can fix this by removing your 2 way binding and instead listening to the blur event for each input or implementing something described in the link above.
Blur event listening example:
<input (blur)="blurHandler(item,i)" type="text" name="order" title="order" />
Component
blurHandler(val:string,ndx:number) {
this.itemsInNewOrder[ndx] = val;
}
This might help too
Angular 2 change event - model changes
You can also wrap the values into objects. I don't know why you don't lose focus if you do it like this.
Component:
itemsInNewOrder = [{value: 'Soda'}, {value: 'Burger'}, {value: 'Fries'}];
template:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input
type="text"
name="order"
title="order"
[(ngModel)]="itemsInNewOrder[i].value"/>
</div>
The simplest answer to this was that I needed to learn and implement Angular 2's reactive forms library, which is part of Angular 2. Using this, I ran into none of difficulties I was experiencing.

Checked state not getting populated in viewmodel when set programatically

I believe this question is similar to this one but as far as could see in the rules, if there is no answer and it is not the same scenario, I'm allowed to ask.
I've simplified my real scenario with the following, basically, the checkbox is getting checked through some unaccessible code which doesn't get the view model of knockout.js to detect. Is there a work around?
HMTL:
<input id="myCheckbox" type="checkbox" data-bind="checked: myValue" />
<div data-bind="text: myValue"></div>
javascript:
var viewModel = {
myValue: ko.observable(false)
};
ko.applyBindings(viewModel);
setTimeout(function() {
$("#myCheckbox").attr("checked", "checked");
}, 1000);
When a checkbox is modified using the setAttribute function or the checked property, as jqGrid does, it doesn't trigger the click event that Knockout's checked binding uses; neither does it trigger a change event. To be able to detect these changes, you have different options depending on the browser/version: MutationObserver, DOMAttrModified, and/or onpropertychange.
But I'd suggest avoiding those solutions and using what jqGrid gives you: either the jqGridSelectRow event or the onSelectRow callback. You might want to check out the Knockout-jqGridBinding plugin that should give you a good starting point. It includes a selectedItems option that lets you bind an observable array to jqGrid's selected items (using the onSelectRow callback internally).
EDIT:
To re-iterate, I suggest you don't try to solve the problem by watching the checkboxes. But if that's the way you want to go, there's a jQuery plugin, attrchange that provides cross-browser support for this.
Resources:
Knockout-jqGridBinding: https://github.com/CraigCav/Knockout-jqGridBinding
attrchange: http://meetselva.github.io/attrchange/
The answer to which you linked explained that Knockout needs to be alerted of the change through a usual event, such as "click". Here is the idea posted there by Rustam:
function update(){
var $cb = $(':checkbox');
var cb = $cb[0];
// change value directly on element
cb.checked = !cb.checked;
// propagate changes to KO
$cb.triggerHandler('click');
}
setTimeout(update, 1000);
Of course the method more native to KO would be to change the observable on the model, like so:
var update = function() {
viewModel.myValue(!viewModel.myValue)
};
I was not able to fix this on my code in which Foundation was taking control of the checkbox. I ended up binding a click event which then checked on the checkbox to see if it was checked (we only had 1 checkbox that we were trying to keep track of). I then updated the observable from that click event based on whether it was checked or not.
No way around this that I know of.

How to detect changes besides the change event in knockout.js

I am using knockout.js. I created a view model say testViewModel with only 1 observable property testProperty.
function testViewModel()
{
var self = this;
self.testProperty = ko.observable("Initial");
}
than i created a span in which the changed value of testProperty is reflected and a input text field by which we can change the testProperty value.
<span data-bind="text: testProperty"></span><br />
<input type="text" data-bind="value: testProperty" />
I created an Example Fiddle.It seems that the observable property value is updated when the focusout event is executed on the input text field.
Now my question is that can we change the observable property value update event from focusout to something else. I created a save button also. Is there any way to update the observable property value only on save button press.
I am trying to create an application in which a user can create and save its profile and can edit the saved profile.I am using the same observable properties in create and edit form and these properties are observable. So when user edit its profile the ui should not
be updated until user press the save button. This is my goal. Please help me to solve this issue ?
I would suggest have testProperty and testProperty_temp. Bind the input to temp and when the button is clicked, set testProperty to the testProperty_temp
function testViewModel()
{
var self = this;
self.testProperty = ko.observable("Initial");
self.testProperty_temp = ko.obserable("");
self.save = function() { self.testProperty(self.testProperty_temp()); }
}
Hope this helps
Another means, along the same lines of what Matt Burland suggested:
http://jsfiddle.net/mori57/PQxJC/
Basically, wrap your input and button in a form, and bind the form to submit: which is handled by a method on your ViewModel. See the comments I've made inline, but here it is for people who don't want to go out to jsFiddle:
<span data-bind="text: testProperty"></span><br />
<!-- wrap the input and button in a form and
data-bind to submit, with a reference
to a handler on your viewmodel -->
<form data-bind="submit: updateProfile">
<!-- this must be bound to your shadow value -->
<input type="text" data-bind="value: _tmpTestProperty" />
<button type="submit">save</button>
</form>​
and in your javascript
function testViewModel()
{
var self = this;
self.testProperty = ko.observable("Initial");
// Create the "shadow" property
// and prepopulate it with testProperty's value
self._tmpTestProperty = ko.observable(self.testProperty());
// Create our form handler
self.updateProfile = function(val){
// set the testProperty value to the
// value of the shadow property
self.testProperty(self._tmpTestProperty());
};
}
ko.applyBindings(new testViewModel());​
In this way, your value doesn't change when you lose focus on the text input box, but is only updated when you submit the form.
Your simplest approach would be to have a shadow property for each of your properties. So you bind one to your text boxes and only copy the value to the other property, the one bound to the other UI elements, when save is clicked.
See here: http://jsbin.com/aguyud/5/edit
An easier way using two models and $.extend to copy from one to the other:
http://jsbin.com/aguyud/7/edit
Update, actually scratch that, that doesn't seem to work. I tried this instead:
http://jsbin.com/aguyud/22/edit
which works the first time, but after copying the model with $.extend it seem it's copied all the bindings too, so it only works once!

Categories

Resources