I extended the multiselect widget with nothing special. The issue is the binding of values no longer work. In a first sample, I'm using the native widget and binds values fine. The second is where I use an extended multiselect which fails on the value binding and is blank.
HTML:
<selectdata-role="multiselect"data-bind="source: selectData, value: selectedIDs"data-text-field="Name"data-value-field="ID"></select>
<selectdata-role="multiselectcustom"data-bind="source: selectData, value: selectedIDs"data-text-field="Name"data-value-field="ID"></select>
Javascript:
//EXTEND MULTISELECT WITH NOTHING MUCH
kendo.ui.plugin(kendo.ui.MultiSelect.extend({
init: function(element, options) {
kendo.ui.MultiSelect.fn.init.call(this, element, options);
},
options: {
name: 'MultiSelectCustom'
}
}));
varviewModel = kendo.observable({
selectedIDs: [ 1, 3 ],
selectData: [{
Name: 'Bill Smith',
ID: 1
}, {
Name: 'Jennifer Jones',
ID: 2
}, {
Name: 'Tim Philips',
ID: 3
}]
});
kendo.bind('body', viewModel);
I guess I can re-create the binder for "value" again, but is this indeed a bug? I have a jsFiddle that demonstrates this: http://jsfiddle.net/basememara/2Dacw/9/
this isn't a bug so much as that multiselect has custom binders set for it. You can try duplicating the binders for the multiselect for your new extended role.
try this:
kendo.data.binders.widget.multiselectcustom = kendo.data.binders.widget.multiselect;
you can place it before/after your widget extension code, but this should tell the bind function how to properly bind to your widget.
I would also take a look at the kendo docs for custom binders, be warned though it isn't very thourough
Related
I have a div and a following javascript:
let usersNotContributingIds = [ 19, 20, 21 ];
let usersNotContributingNames = [ "Flavius K.", "Pogchamp", "Lazy Gopnik" ];
let contributorToBeAddedId; // the value that will be used for further actions
$("#alpaca-search-contributing-users").alpaca({
data: null,
schema: {
type: "object",
enum: usersNotContributingIds,
},
options: {
name: "pls",
label: 'Contributor Fullname',
optionLabels: usersNotContributingNames,
helper: "Select user sou want to add as a contributor",
id: "select2-search",
focus: false,
events: {
change: function() {
console.log(this.getValue().value);
contributorToBeAddedId = this.getValue().value
},
focus: function() {
console.log(this.name);
},
blur: function() {
console.log(this.name + ": blur");
},
ready: function() {
console.log(this.name);
}
},
},
postRender: function(control) {
$('#select2-search').select2();
}
});
Obviously, I want to get the newly set value, or anyhow access the selected value and use it. For example with AJAX and a button.
The problem is, that when I have 3 or less options, Alpaca render the field not as a search, but as a radio-something and the this.getValue() is null.
Is there a way to force Alpaca to NOT USE THE RADIO BUTTONS? I dont want to use them, even if I had only 1 option. Documentation just promtly states, that if there are 3 or less options, it will generate radio buttons instead of select, but it says nothing about the fact, that it breaks everything and that I would not be able to retrieve the value the same way as with select field.
If I am doing something inefficiently or wrong, please tell me, I am new with Alpaca and I just want a neat select dropdown with search, that I can use to pick users from a list with any length. Also, I would like the "null" or "none" option to not be there.
To have your select component rendered you should use the option type and set it to "select".
The issue with the value is because you're using it wrong, to get the value in alpaca you only do this.getValue() and there's no need to add .value.
FYI: If you see the error "This field should have one of the values in Flavius K., Lazy Gopnik, Pogchamp. Current value is: 19" you should update your enum array to have strings instead of ints let usersNotContributingIds = [ "19", "20", "21" ];.
Here's a working fiddle for this.
I am trying to set up a basic server side vue-tables-2 with two filters - one is a dropdown and the other is a search field. I am having trouble detecting which of the two filters were applied within the requestFunction() so I can send a request over to the server. Currently, I am just trying to console log the input filter name and value as the filter is applied / input is changed.
JSFiddle:
https://jsfiddle.net/kbpq5vb3/39/
HTML
<h1 class="vue-title">Vue Tables 2 Demo</h1>
<div id="app">
<v-server-table url="https://jsonplaceholder.typicode.com/users" :columns="columns" :options="options"></v-server-table>
</div>
VueTable:
Vue.use(VueTables.ServerTable);
new Vue({
el: "#people",
data: {
columns: ['name', 'username'],
options: {
requestAdapter(data) {
console.log(data.query); // detect which filter was applied / which input changed
},
responseAdapter(resp) {
return {
data: resp,
count: resp.length
}
},
filterByColumn: true,
filterable: ['name', 'username'],
listColumns: {
name: [{
id: 'ervin',
text: 'Ervin'
}, {
id: 'chelsey',
text: 'Chelsey'
}]
}
}
}
});
According to the vue-tables-2 documentation:
vue-tables.filter/tableName/FILTER fires off when a filter is changed.
After looking a bit deeper, it really is as easy as listening for an event:
Vue developer tools really make this type of stuff easy to diagnose. Now, you'd want to listen to the custom event. You can learn how to do that over on the Vue documentation.
Finally, here is a working fiddle that shows how you listen to these events: https://jsfiddle.net/kbpq5vb3/55/
Hope this helps!
Firstly I would like to say Angular Formly is a fantastic library for novices such as myself. I am not a web developer, however find this library to be intuitive and powerful.
However I do need assistance with use of Expression Properties.
I have a model library which contains library items, for example:
{
"itemId":"STX001",
"title":"Grey Wolf",
"category":"White", etc.
}
{
"itemId":"STX002",
"title":"Noble Black",
"category":"Black", etc.
}
etc.
I also have a formly form which uses ui-select in top field to lookup all values from Library, select one of these (I will call this Item), and then populate remaining fields in the form with Items properties, then submit form to Catalogue model.
The problem I am facing is I cannot reference the properties of Item from within other fields. I have tried using expressionProperties but can only extract the valueProp value (which is uniqueID), however I am after Item.title, Item.category, etc.
Code below:
{
//This is form fields for creating a new Catalogue entry
key: 'libraryId',
type: 'ui-select',
templateOptions: {
label: gettextCatalog.getString('Search Library'),
options: [],
valueProp: 'itemId',
itemTitle: 'title',
itemCategory: 'category',
labelProp: 'title',
focus: true,
placeholder: 'Start typing keywords..'
},
controller: function ($scope) {
getLibrary().then(function(data){
$scope.options.templateOptions.options = data;
return data;
});
}
}
{
key: 'title',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Name'),
required: true
},
expressionProperties : {
//This is what i'm trying to achieve but doesn't work
'templateOptions.placeholder' : 'model.libraryId.itemTitle'
}
},
Use the call back function provided
expressionPropertyObj = {
'templateOptions.required': (model, formState: any, field: FormlyFieldConfig) => {
console.log('model',model);
console.log('state',formState);
console.log('field',field);
},
I'm trying to select a default select option based on one of the property with which I'm populating my select option.
This code is copied straight from #rneimeyer's fiddle. I did tweak it to do what I wanted to do.
So, I have choices as my observableArray.
var choices = [
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
];
function ViewModel(choices, choice) {
this.choices = ko.observableArray(choices);
};
The difference between rneimeyer's fiddle and mine is that I have choice property added on my object inside the observableArray instead of having a separate observable for the option that we want to be default.
Here's the fiddle on my attempt.
Now I'm checking in my select element tag whether the choice attribute is true or not. And if it is then I want to set the name to the value attribute so that it becomes the default.
<select data-bind="options: choices, optionsText: 'name', value: choice"></select>
I've tested this with simple data model in my fiddle here as well which is working just as I wanted.
I guess what my real query is how to check choice property in the data-bind. I see that optionText is being able to access the name property just fine. Not sure why it isn't same for choice property in value attribute.
I might have misdirected to some people. Also, I apologize for not mentioning the version that I'm using. I'm currently using Knockout 3.0.0 (you'll see why this is important later)
Also, just to note that I'm not saying #XGreen's method is wrong but that wasn't exactly what I was looking for and this might be due to my poor explanation.
Let me first try to clarify what I was trying to accomplish.
First of all, I will be having an array of object with the information for the options.
[
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
]
Now, what I wanted to do was to data-bind select option to that array with choice true being the default selected one.
I'm not intending to create any extra observable except the array itself which is going to be an observableArray.
After much research I finally found optionsAfterRender attribute for options property in Knockout's Docs.
<select data-bind="options: choices,
optionsValue: 'name',
optionsAfterRender: $root.selectDefault">
</select>
So what optionsAfterRender really does is, on each array element it calls custom function which I've set to check if the choice is true or not and make the value of select option that which has the true.
Do note that ko.applyBindingsToNode does not work on version 2.2.0 which I had in my original fiddle.
function ViewModel(choices) {
this.choices = ko.observableArray(choices);
this.selectDefault = function(option,item){
if(item.choice){
ko.applyBindingsToNode(option.parentElement, {value: item.name}, item);
}
};
};
ko.applyBindings(new ViewModel(choices));
And here's the fiddle for it.
Ok If I understand you want to set the true choice as your default selected value.
First you need to involve id in your drop down so it becomes the value of the options as we will filter our collection based on that unique id
<select data-bind="options: choices, optionsText: 'name', optionsValue: 'id', value: selectedChoice"></select>
As you see now you need to create a new observable called selectedChoice and we are going to populate that observable with the choice that is true using a computed.
var choices = [
{ id: 1, name: "one", choice: false },
{ id: 2, name: "two", choice: true },
{ id: 3, name: "three", choice: false }
];
function ViewModel(choices) {
var self = this;
self.choices = ko.observableArray(choices);
self.trueChoice = ko.computed(function(){
return ko.utils.arrayFirst(self.choices(), function(item){
return item.choice === true;
});
});
self.selectedChoice = ko.observable(self.trueChoice().id);
};
ko.applyBindings(new ViewModel(choices));
the new computed property trueChoice uses the arrayFirst method in order to return the first item in your choices collection that has its choice property set to true.
Now that we have our true choice all we have to do is to set the selected value of the dropdown aka selectedChoice to be the id of that true choice so the item becomes selected in the drop down.
Here is also a working fiddle for this
Added a Gist that disabled the first option in a select drop down list, and work nicely with KO's optionsCaption binding, using a optionsDisableDefault binding:
https://gist.github.com/garrypas/d2e72a54162787aca345e9ce35713f1f
HTML:
<select data-bind="value: MyValueField,
options:OptionsList,
optionsText: 'name',
optionsValue: 'value',
optionsCaption: 'Select an option',
optionsDisableDefault: true">
</select>
You could create a computed that holds the selected items
self.selected_options = ko.computed({
read: function() {
return self.choices.filter(function(item){ return item.choice });
},
write: function(value) {
self.choices.forEach(function(item) { item.choice = value.indexOf(item) > 0;});
}
})
Then bind to that as the selected options.
I am attempting to use knockout-kendo.js to declare a kendo dropdownlist control in a knockout forEach template, so that as new items are added to the knockout observable array, new kendo dropdownlists are rendered in the UI.
Initially, I come to realize that I can no longer bind the dropdownlist's selected value to an entire entry object in my dropdownlist's specified 'data' array.
To overcome this issue, I followed the RP Niemeyer's suggestion in the following thread:
Set the binding of a dropdown in knockout to an object
Now, this all works. Great.
My issue is when attempting to add second drop down list to the template, who's data is bound to an array property on the object being returned from the computed observable... (I need to chain the drop down lists so that the first displays all Students, second displays all classes for the student that is currently selected in the first drop down list, third displays all test grades for the class that is currently selected in the second drop down list, etc....)
I created a fiddle based on RP Niemeyer's original fiddle to demonstrate my issue:
Original Fiddle (RP Niemeyer's)
My Fiddle With Issues
I added the below lines to the fiddle:
HTML:
<input data-bind="kendoDropDownList: { dataTextField: 'caption', dataValueField: 'id', data: selectedChoice().shapes, value: selectedShapeId }" />
JS:
this.choices = ko.observableArray([
{ id: "1", name: "apple", shapes: ko.observableArray([ { id: "5", caption: "circle" }, { id: "6", caption: "square" }]) },
{ id: "2", name: "orange", shapes: ko.observableArray([ { id: "5", caption: "circle" }]) },
{ id: "3", name: "banana", shapes: ko.observableArray([ { id: "5", caption: "circle" }, { id: "6", caption: "square" }, { id: "7", caption: "triangle" }]) }
]);
Again, I was expecting that upon the selection changing in the first drop down list (causing selectedId to change, causing selectedChoice to change) would also cause any UI elements bound to 'selectedChoice' or any of selectedChoices' properties, to have their bindings re-evaluated and UI respectively updated.
Am I missing something? Or is there a better way to achieve this 'chaining of drop down list' behavior (while still utilizing a knockout template and kendo drop down list control)?
Let me offer you some advice. Try to avoid accessing properties of an observable's value, as you can see, the dependency detection will not always be able to detect the dependency. You should create a computed observable which does the accessing for you.
var ViewModel = function () {
// ...
this.selectedChoice = ko.computed(function () {
var id = this.selectedId();
return ko.utils.arrayFirst(this.choices(), function(choice) {
return choice.id === id;
});
}, this);
this.selectedChoiceShapes = ko.computed(function () {
var selectedChoice = this.selectedChoice();
return selectedChoice && selectedChoice.shapes;
}, this);
}
Then your bindings becomes:
<input data-bind="kendoDropDownList: {
dataTextField: 'name',
dataValueField: 'id',
data: choices,
value: selectedId }" />
<input data-bind="kendoDropDownList: {
dataTextField: 'caption',
dataValueField: 'id',
data: selectedChoiceShapes,
value: selectedShapeId }" />
updated fiddle
This appears to be a shortcoming of Kendo using Knockout. When Kendo evaluates selectedChoice().shapes it holds onto the array it finds, instead of keeping the entire expression. If you update that specific array with options, you can see them in the second dropdown. The problem is that when you update selectedChoice Kendo does not reevaluate the data to the new shapes array. You can see this behavior in this fiddle.
Open the JS console, set the context to the fiddle (it defaults to the top frame in Chrome`, and run this:
window.vm.choices()[1].shapes.push({"id": "6", "caption" : "Thing"})
And you will see the second dropdown update. Changing the first dropdown doesn't have an effect. You can see that in this fiddle Knockout without kendo reevaluates the entire expression, properly updating the second select options.