KnockoutJS: cannot update the option value - javascript

i am working on a webpage that consists a JQuery item and a Knockout Item.
basically the view has a select field and a sevond view that is being updated by the select value change.
also i have a textbox search field with jquery autocomplete.
What i want to do is when i press enter after on the search box, the javascript will update the ko.observable value and trigger the ther updates yet its not working. i've managed to trigger keypress but i cant update and trigger the update..
Heres the code:
function Station(data){
var self = this;
this.userId = ko.observable(data.userid);
this.displayName = ko.observable(data.displayName);
this.email = ko.observable(data.email);
this.redirectURL = ko.computed(function(){
return "/someurl/somerequest?userId="+self.userId();
});
this.selectText = ko.computed(function(){
return self.displayName();
})
}
function currentStation(index)
{
return self.stations()[index];
}
function StationViewModel(){
var self = this;
self.stations = ko.observableArray([]);
$("#stationSelect").attr("disabled,true");
$.getJSON("#{someurl.getStationList()}",function(allData){
var mappedStations = $.map(allData,function(item)
{
return new Station(item);
});
self.stations(mappedStations);
$("#stationSelect").attr("disabled,false");
});
url = "/someurl/somerequest?userId=";
this.selectedStation = ko.observable();
this.redirectToStation = function(){
var linkToSend =
alert(self.selectedStation.redirectURL());
}
<!-- THIS IS THE CODE THAT HAS TO UPDATE THE FIELD BUT IT DOESN'T-->
this.getStation = function(event)
{
for(i = 0; i<this.stations().length;i++)
{
if(this.stations()[i].userId()==$("#search").val())
{
self.selectedStation = ko.observable(this.stations()[i]); //Am i doing it right?
}
}
};
}
<!-- This is the code That handles the click event inside the textbox. its working -->
ko.bindingHandlers.executeOnEnter = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var allBindings = allBindingsAccessor();
$(element).keypress(function (event) {
var keyCode = (event.which ? event.which : event.keyCode);
if (keyCode === 13) {
allBindings.executeOnEnter.call(viewModel);
return false;
}
return true;
});
}
};
ko.applyBindings(new StationViewModel());
</script>

Instead of
self.selectedStation = ko.observable(this.stations()[i]);
do
self.selectedStation(this.stations()[i]);
Hope this helps!

What I have done in the past to get the Enter key working is to wrap the <input> in a <form> tag
so from
<input type="text" data-bind="value:myValue"></input>
to
<form>
<input type="text" data-bind="value:myValue"></input>
</form>

Related

onchange event not handling button click event

I'm working on saving textarea value on browser refresh. When there's a change to textarea by keyboard events, the value gets stored in local storage. However, When the textarea value gets changed indirectly...like button clicks, the value doesn't get saved. That's the problem, So, I changed my event handler from onkeyup to onchange. Still, This remains a problem. Do anyone have a better idea for this. Here's the code -
<textarea id="thetext" class="" value="Write some value, and off focus this input. Refresh the browser, and see the text getting saved." onchange="saveValue(this);"></textarea><br/><br/>
<button id="thebutton" onclick="change()">Change</button>
<script>
document.getElementById("thetext").value = getSavedValue("thetext");
function saveValue(e){
var id = e.id;
var val = e.value;
localStorage.setItem(id, val);
}
function getSavedValue (v) {
if (!localStorage.getItem(v)) {
return "";
}
return localStorage.getItem(v);
};
function change() {
document.getElementById("thetext").value = "This value doesn't get saved if there's no keyboard action. But I want this to get saved on button click."
}
</script>
<style>
textarea{height:100px;width:100%}
</style>
Edit - Above code is working well on my environment.
you can do in easy way
function change() {
let newValue = "This value doesn't get saved if there's no keyboard action. But I want this to get saved on button click."
var event = new Event('input');
thetext.dispatchEvent(event);
// or
// document.getElementById("thetext").value = newValue;
// localStorage.setItem("thetext", newValue );
}
or complex usinng Object.defineProperty
thetext = document.getElementById("thetext")
thetext.value = getSavedValue("thetext");
function saveValue(e) {
console.log(e)
var id = e.id;
var val = e.value;
localStorage.setItem(id, val);
}
function getSavedValue(v) {
return localStorage.getItem(v) || "";
}
function change() {
document.getElementById("thetext").value = "This value doesn't get saved if there's no keyboard action. But I want this to get saved on button click."
}
function monitorValue(element, property) {
let descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), property);
Object.defineProperty(element, property, {
get: function() {
return descriptor.get.apply(this, arguments);
},
set: function() {
descriptor.set.apply(this, arguments);
// create and dispatch input event
var event = new Event('input');
element.dispatchEvent(event);
return this.value;
}
});
}
monitorValue(thetext, "value")
<style>textarea{height:80vh;width:100%}</style>
<textarea id="thetext" class=""
value="Write some value, and off focus this input. Refresh the browser, and see the text getting saved."
oninput="saveValue(this);
"></textarea><br/><br/>
<button id="thebutton" onclick="change()">Change</button>
So what you want is that, the <textarea> value be stored in the localStorage every time it's changed.
I'd prefer defining a setter property on the textarea.
var ta = document.getElementById('thetext');
Object.defineProperty(ta, 'savedValue' , {
set: (value)=> {
ta.savedValue = ta.value = value;
localStorage.setItem(ta.id, value);
}
};
thebutton.addEventListener('click', (evt)=> {
ta.savedValue = 'my sample click value';
});
ta.addEventListener('change', (evt)=> {
ta.savedValue = evt.target.value;
};

Modified code is not correct for the getvalue and setvalue

My code was working fine but they wanted to change my code....
they wanted to attach setValue and getValue added directly to
footballPanel instead of sports grid,
but after adding it the code is not working fine...
can you tell me why its not working....
providing my modified code below...
the UI action here I am performing is there are two radio buttons,
when I click each radio button two different grids open
in one of the grid we add value, when i switch back to another radio
button the values in another grid disappears but it should not
disappear...
after I modified the code the values disappear, can you tell me why?
Only part of modified code here
else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue();
sportsGrid.addCls("sports-item-disabled");
}
}
Whole modified code:
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
**if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue();
sportsGrid.addCls("sports-item-disabled");
}**
}
},
Working code without modification
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
sportsGrid.holdValue = sportsGrid.store.data.items;
sportsGrid.store.loadData([]);
sportsGrid.addCls("sports-item-disabled");
}
}
},
getValue() is not a method of ExtJS Panel class.
The change in your code, from sportsGrid (Ext.grid.Panel) to footbalPanel (Ext.panel.Panel) won't work, because they are from different classes and therefore have different properties and methods.
If you want this code to work, you'll need to implement getValue() and setValue(). For example, something like:
On FootballPanel class:
getValue: function () {
return this.down('grid').store.data.items;
},
setValue: function (newValue) {
if (!newValue)
newValue = new Array();
this.down('grid').store.loadData(newValue);
},
And use your modified code:
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue([]);
sportsGrid.addCls("sports-item-disabled");
}
}
},

How to initialise a KO observable from the server

If I have an HTML element that is pre-populated from the server (e.g. PHP or ASP.NET) like this:
<input type="text" data-bind="value:TheMeaningOfLife" data-revert-to="42" />
and I declare my model as
var TheMeaningOfLife = ko.observable(69);
how can I get the model to initialise with the value of data-revert-to? I started with the following custom binding:
ko.bindingHandlers.serverValue = {
init: function (element, valueAccesor, allBindingsAccessor, viewModel) {
var el = $(element);
var serverValue = el.data("revert-to");
if (serverValue == undefined) {
return;
}
el.val(serverValue);
}
}
and update my control to
<input type="text" data-bind="serverValue:TheMeaningOfLife" data-revert-to="42" />
but that seems to kill the update event, because when I change the value in the textbox, the viewmodel does not update.
Solution as per segfault's answer
I tweaked segfault's answer a little to follow more closely how the server code is emitted:
ko.bindingHandlers.serverValue = {
init: function (element, valueAccesor, allBindingsAccessor, viewModel) {
var observable = valueAccesor();
if (observable() == undefined) {
var el = $(element);
var serverValue = el.data("revert-to");
observable(serverValue);
}
ko.applyBindingsToNode(element, { value: observable });
}
}
and this works just fine. I just need to make sure that the observable is initialised with a null, rather than an empty string.
It looks like you never check the observable so you are always using the revert to value. If you do a check on the value accesor and if its provided dont overwrite it.
<input type="text" data-bind="serverValue:TheMeaningOfLife, defaultValue='42'" />
ko.bindingHandlers.serverValue = {
init: function (element, valueAccesor, allBindingsAccessor, viewModel) {
var myValue = null;
var observable = valueAccesor();
if (observable() == undefined) {
observable(allBindingsAccessor.defaultValue);
}
ko.applyBindingsToNode(element, { value: observable });
}
}

Getting KnockoutJS Validation working with Bootstrap Popover

Almost there, just need the popover to appear only after submit button is clicked and the field has an alert class added to it.
Note: Hover only works for isValid is true in the binding handler, otherwise testing for not true requires a click rather than a hover :S
http://jsfiddle.net/hotdiggity/UUb4M/
HTML:
<input data-bind="value: lastName, popover: isValidField" data-placement="below" data-title="Alert" data-content="We have identified this information is incorrect and must be updated."/>
JS:
self.lastName = ko.observable().extend({ required: true });
self.isValidField = ko.observable();
this.submit = function () {
if (self.errors().length === 0) {
alert('Thank you.');
} else {
self.errors.showAllMessages();
self.isValidField(self.lastName.isValid());
}
};
Binding:
ko.bindingHandlers.popover = {
init: function (element, valueAccessor, allBindingsAccessor) {
var value = valueAccessor(),
valueUnwrap = ko.unwrap(value),
allBindings = allBindingsAccessor(),
isValid = allBindings.value;
if (isValid) {
$(element).popover({
trigger: "hover"
});
} else {
$(element).popover("hide");
}
},
Update: //See jsfiddle link above (the same code)
Here is a working version for you. The popover will appear only if the validation for the lastName field is not met and only when the submit button is clicked. It will go away if the user types something into the lastName field. See this updated fiddle
The update function of a binding handler creates dependencies on observables that it accesses, so essentially the update of a binding handler will be triggered when that observable is changed. In your case, I did it with the isValidField observable. See notes in the code
var viewModel = function () {
var self = this;
self.lastName = ko.observable().extend({ required: true });
self.isValidField = ko.observable();
// once the popover is shown, we want it to go away if the user types something
// into the lastName field. We do this by triggering the popover binding handler
// by changing the value of isValidField
self.lastName.subscribe(function(val){
if(val && val.length > 0){ self.isValidField(true); }
});
// need to create your errors object
self.errors = ko.validation.group(self);
this.submit = function () {
if (self.errors().length === 0) {
alert('Thank you.');
} else {
self.errors.showAllMessages();
// change the value of isValidField to trigger the popover binding handler's
// update
self.isValidField(self.lastName.isValid());
}
};
};
ko.bindingHandlers.popover = {
update: function (element, valueAccessor, allBindingsAccessor) {
var value = valueAccessor(),
valueUnwrap = ko.unwrap(value);
if (valueUnwrap === false) {
$(element).popover('show');
} else {
$(element).popover("destroy");
}
}
};

NotFoundError: DOM Exception 8 when substituting innerHTML

I'm new to js-development. I have the following code:
<html>
<body>
<div><span id="inline">Click here to start editing</span></div>
<script>
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
var value = inline_editable.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
inline_editable.onblur();
}
}
}
}
</script>
</body>
</html>
Which shows some text inside span and allows inline editing. When I finish editing within just onblur event it work perfectly fine. But if I want to terminate editing by Enter and use the same hander I get an error NotFoundError: DOM Exception 8 in this line:
inline.innerHTML = value;
Nevertheless everything works as I expect. Can anyone help me to avoid this error?
I assume that is happened because I destroy inline-editable element while event handling is not finished and it wants to invoke onchange maybe. Should I have 2 controls all the time an switch their visibility instead?
Problem here is the onblur is triggered twice, the second time, the element is not there which causes the problem. Kill the events
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
this.onblur = function(){};
var value = this.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
this.onblur();
}
}
}
}

Categories

Resources