On the application I am working on I need to disable an element under one of two different circumstances, one if the logged in user only has read only access to the form, two if the conditional logic set up on the field is met. These forms are being generated dynamically, built on the admin panel with drag and drop fields.
The issue I am having is when the conditional logic case has been met but the form is not read only. In the case I am trying now it's to disable some of the fields if the form is being reopened (already saved).
The input looks like this:
<input name="userName" id="userName" maxlength="64" type="text" class="k-textbox custom-disable" data-bind="value: formData.userName, disabled: isFormDisabled">
The conditional logic sets the disabled property on the input, as well as adding the custom-disable class for CSS. isFormDisabled is a boolean property of the viewModel.
What I would like to do is create a function like this:
ViewModel = kendo.observable({
...,
isFormDisabled: false,
isElementDisabled: function (e) {
var customDisabled = $(e.target).hasClass( "custom-disable" );
return this.get("isEventDisabled")||customDisabled;
},
...
});
and change the binding of disabled to this new function, but since the disabled is not an event, when I log e it's undefined - how can I find the element being bound to this iteration of the callback?
Kendo Dojo
Currently Kendo has its own way for to enable/disable elements, for example, this explains how to enable/disable a Kendo Combobox
This is what I ended up figuring out - for the function:
isElementDisabled: function (eID) {
var me = this;
var element = $("#" + eID);
var customDisabled = $(element).hasClass("custom-disable");
return me.get("isEventDisabled") || customDisabled;
},
and the binding -
<input name="#: columnName #" id="#: columnName #"
data-bind='value: #: dataContainer #.#: columnName #, disabled: isElementDisabled("#: columnName #")'
/>
Since the ID of the element matches the column name and is unique, I can find it by passing that ID into the bound function as a property.
Related
The following gif should show the problem in action.
Typeahead seems to trigger the onChange function before actually setting the value from a click, for who knows what reason.
As you can see whatever is typed into the box is grabbed correctly, but if a user selects an option from the dropdown only what had been typed in up to that point is grabbed, rather than the appropriate behavior which is the value in the text-box.
The TypeAhead JS code looks like
$('#typeahead_id').typeahead({
name: 'typeahead',
remote: 'search.php?key=%QUERY,
limit: 10
});
And the html for the input
<label>Team Name:</label>
<input type='text' id='typeahead_id' onChange='update_params("team",typeahead_id)' class='typeahead tt-query' autocomplete='on' spellcheck='false' placeholder='Select Team'><br>`;
The update Param method
function update_params(val, id) {
let temp = nodes[id]
temp.params[val] = $("#" + val + id).val()
console.log(temp)
}
The oddness of this can be explained in that each of the blue boxes is referred to as a node, and can have their own unique typeahead.
I need a way in that users can type the name of the team into the input or select one from the dropdown and my code should be able to retrieve that value and set it in the appropriate spot.
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>
So I have a list of schedules that I need to display for a user and show which schedules he or she is currently on and give them the possibility to jump on and off said schedules.
My viewmodel looks like this
self = this;
self.shifts = ko.observableArray();
self.selectedShifts = ko.observableArray();
//I populate self.shifts here with a WEB API call
//Run through each shift and check if current user is on it and set checked / not checked value for checkbox
ko.utils.arrayForEach(self.shifts(), function(shift) {
//Clear array
self.usersOnShift([]);
//Populate array with all users on the shift
self.usersOnShift = ko.observableArray(WEB API CALL HERE);
var userInShift = ko.utils.arrayFirst(self.usersOnShift(), function(user) {
if (selectedUserId == user.ID) {
return true;
}
});
if (userInShift) {
self.selectedShifts.push(shift.ID);
}
});
ko.applyBindings(self);
My HTML looks like this
<div class="simple_overlay" id="shiftOverlay">
<div class="details">
<div data-bind="foreach: shifts">
<div><span class="staff-initials" data-bind="text:wardName"> </span><input type="checkbox" data-bind="value: ID, checked: $root.selectedShifts"/> </div>
</div>
<div>
Connect
Close
</div>
</div>
</div>
I can see that the value of the checkboxes are set correctly to the ID of the corresponding shifts. However a shift that I know the user in question is on is not checked and I know that the selectedShifts observableArray contains the value.
Somehow the "checked: $root.selectedShifts" call / check is not working but I know that it contains the right value. What am I doing wrong?
The problem is that your value is an integer, but when bound to the checkbox element, it becomes a string. When the checked binding tries to find the value in the array, it doesn't find a match because it uses strict equality for comparison and (2 === "2") is false.
The simplest way to work around this problem is to convert your values to string when you add them to the array:
self.selectedShifts.push("" + shift.ID);
Of course this means that your model has to change, and that might not be a great solution. I came up with a custom binding, checkedInArray that replaces checked and supports any type of value. You can learn about it, see it in action, and use it like this:
<input type="checkbox" data-bind="checkedInArray: {value: ID, array: $root.selectedShifts }" />
In Knockout 2.3.0 (which is still in development) there will be a new binding, checkedValue, that will allow you use any type of value with the checked binding. Using that version, you could update your HTML to use checkedValue:
<input type="checkbox" data-bind="checkedValue: ID, checked: $root.selectedShifts"/>
Is shift.ID an observable property? If it is, then you need to add it to the array like this:
self.selectedShifts.push(shift.ID());
Otherwise you're just adding the whole observable to the array, and not the value.
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!
For submitting the form data to the server from the AJAX call to bind Telerik MVC grid, we can set e.data in OnDataBinding event to a anonymous JavaScript object
<script type="text/javascript">
function Grid_onDataBinding(e) {
var categoryValue = "Beverages";
var priceValue = 3.14;
// pass additional values by setting the "data" field of the event argument
e.data = {
// the key ("category") specifies the variable name of the action method which will contain the specified value
category: categoryValue,
price: priceValue
};
}
</script>
To facilitate the model binding for Boolean, ASP.NET MVC generates checkboxes along with a hidden text field with the same name
<input name="myCheckBox" class="check-box" id="myCheckBox" type="checkbox" CHECKED="checked" value="true"/>
<input name="myCheckBox" type="hidden" value="false"/>
and when these are submitted the data submitted is
myCheckBox=true&MyCheckBox=false - when the checkbox is checked
myCheckBox=false - when the checkbox is not checked
For pages where there is no checkbox, the post data can be easily obtained by
e.data = form.serializeObject()
where serializeObject creates that object by looping thru all the form fields. How to construct that object in case of forms when there are checkboxes as described above? Basically how can a name-value pair list be represented in the object form when the names are allowed to be duplicate?
e.data = {
textBox1: "some value1",
myCheckBox: true //,
//myCheckBox: false // ???
};
The implementation of serializeObject creates an array for such form elements and those are submitted as myCheckBox[]=true&myCheckBox[]=false which breaks the model binding on the server side.
You can select specific form subelements to serialize, rather than just serializing the entire form. This allows you to filter out the ones you don't want:
$('form input:not([type=hidden])').serializeObject();
Edit: Per #amit_g's comment, you want the checkbox when it's checked or the hidden element when it's not. This requires a more complex filter than the :not selector:
$('form input')
.filter(function() {
if ($(this).attr('type') == 'hidden') {
// filter out those with checked checkboxes
var name = $(this).attr('name');
return !$('form input[type=checkbox][name=' + name +']')
.prop('checked');
} else {
// include all other input
return true;
}
})
.serializeObject();
See the working jsFiddle here: http://jsfiddle.net/nrabinowitz/nzmg7/4/
serializeObject uses serializeArray internally and serializeArray serializes only those elements that would be submitted actually. So using the following code, I have disabled the hidden fields corresponding to checkbox and added a change event to toggle the disbaled state on each hidden input. Since the inputs are disabled, they don't get serialized.
The .serializeArray() method uses the standard W3C rules for
successful controls to determine which elements it should include; in
particular the element cannot be disabled and must contain a name
attribute. No submit button value is serialized since the form was not
submitted using a button. Data from file select elements is not
serialized.
$('form.form-class :checkbox').change(function () {
enableDisableCorrespondingHiddenField($(this));
});
$('form.form-class :checkbox').each(function () {
enableDisableCorrespondingHiddenField($(this));
});
enableDisableCorrespondingHiddenField(checkbox) {
$(":hidden[name='" + checkbox.attr("name") + "']", checkbox.parent()).attr("disabled", checkbox.attr("checked"));
}