In my markup I have a simple <select> tag hat I've bound with Knockout JS, like so:
<select data-bind="options: CountriesOptions, optionsText: 'Text', optionsValue: 'Value', selectedOptions: SelectedCountries" multiple></select>
My view-model looks like this (simplified)
public class CountryViewModel
{
public List<SelectListItem> CountriesOptions { get; set; }
public int[] SelectedCountries { get; set; }
}
And in my controller I'm populating the model like so:
public ActionResult Edit()
{
using(var db = new DatabaseContext())
{
var model = new CountryViewModel();
model.CountriesOptions = db.Countries.Select(c => new SelectListItem
{
Value = "" + c.CountryId,
Text: c.Name
});
model.SelectedCountries = new int[]{1, 2, 3}
return View(model);
}
}
This example is very simplified but it does the job.
Problem
Whenever I load page and the binding is done, the <select> is filled with all the countries, which is the way I want it, but the selectedOptions is not working. Nothing is selected at all, thought my array looks exactly like int[]{1, 2, 3}. I'm not quite sure why this isn't working.
EDIT
JS scribbles
#using (Html.BeginScripts())
{
#Scripts.Render("~/bundles/knockout")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
#Styles.Render("~/Content/themes/base/jqueryui-datepicker")
<script type="text/javascript">
// Create ViewModel
var vm = ko.mapping.fromJS(#Html.Raw(jsonData));
vm.loading = ko.observable(false);
// Init validation
ko.validation.init({ insertMessages: true, parseInputAttributes: true, messagesOnModified: true });
vm.errors = ko.validation.group(vm, { deep: true, observable: true, live: true });
ko.applyBindings(vm);
</script>
}
EDIT
I just found yet another issue with this binding. If I choose 2 countries from the select, they are NOT returned as selected. Bear in mind that this is SelectListItem's so they have a property called Selected
I'm trying to get all selected items when they return to the controller (when the user presses submit):
[HttpPost]
public ActionResult Edit()
{
var selectedCountries = model.Countriesoptions.Where(x => x.Selected).ToList();
}
But selectedCountries is an empty list.
The problem is probably that you miss the multiple attribute on your select. With it, things work as expected. Here's a demo with your code, with that attribute added and missing bits of code stubbed:
var data = {
CountriesOptions: [
{ Text: "USA", Value: 1 },
{ Text: "Greenland", Value: 4 },
{ Text: "Canada", Value: 3 },
{ Text: "Mexico", Value: 2 }
],
SelectedCountries: [ 1, 2, 3 ]
}
// Create ViewModel
var vm = ko.mapping.fromJS(data);
vm.loading = ko.observable(false);
// Init validation
ko.validation.init({ insertMessages: true, parseInputAttributes: true, messagesOnModified: true });
vm.errors = ko.validation.group(vm, { deep: true, observable: true, live: true });
ko.applyBindings(vm);
pre { background: white; padding: 10px; color: #333; font: 11px consolas; border: 1px solid #ddd; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
<select data-bind="options: CountriesOptions,
optionsText: 'Text',
optionsValue: 'Value',
selectedOptions: SelectedCountries"
multiple>
</select>
<hr>Debug info: <pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
Related
I need to display some odata's data in a sap.m.Select but don't know why is not working, This is the code I have so far
var oModel = new sap.ui.model.json.JSONModel();
var data = [];
var sUrlCard = "odata's url";
var oDataModel = new sap.ui.model.odata.ODataModel(sUrlCard, true);
oDataModel.read("CardBrandCollectionSet", {
async: false,
success: function(oData, response) {
$.each(oData.results, function(i, val) {
data.push(val);
});
oModel.setData({
'card': data
});
sap.ui.getCore().setModel(oModel, "card");
},
error: function(oError) {
console.log(oError);
}
});
table where the select input is located
var oTable = new sap.m.Table({
mode: oMode,
columns: [
{
hAlign: 'Center',
header: new Text({
text: "Card"
})
}
]
});
Select input I need to fill with data
var oSelectMarca = new sap.m.Select({
items: {
path: "/card",
template: new sap.ui.core.ListItem({
key: '{Codcard}',
text: '{Descript}'
}),
templateShareable: true
},
selectedKey: '{Marca}'
});
The binding path of the select control is wrong:
sap.ui.getCore().setModel(oModel, "card"); // model is set at core with name as card
$.each(oData.results, function(i, val) {
data.push(val);
});
oModel.setData({
'card': data // setting data in an object with name as card
});
var oSelectMarca = new sap.m.Select({
items: {
path: "card>/card/", //Binding path model name along with array name
template: new sap.ui.core.ListItem({
key: '{card>Codcard}', // model name with property name
text: '{card>Descript}' // model name with property name
}),
templateShareable: true
},
selectedKey: '{card>Marca}' // model name with property name
});
fist of all you do not want to create your odata model like that, please specify it in your manifest:
in "sap.app" part:
"dataSources": {
"yourRemote": {
"uri": "YOUR_URI",
"type": "OData",
"settings": {
"odataVersion": "2.0"
}
}
}
in "sap.ui5" part:
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "YOUR_i18n"
}
},
"remoteModel": {
"dataSource": "yourRemote"
}
}
2nd you dont want to create controls in js, you do that in your xml files:
https://sapui5.hana.ondemand.com/#/topic/1409791afe4747319a3b23a1e2fc7064
https://blogs.sap.com/2018/05/01/why-do-we-use-xml-views-rather-js-views-in-sapui5/
in that your select needs to be bound like that:
<Select
id="anID"
valueState="{vsModel>/otherText}"
valueStateText="{i18n>someText}"
forceSelection="false"
items="{
path: 'remoteModel>/CardBrandCollectionSet',
sorter: {
path: 'Descript'
}
}">
<core:Item key="{remoteModel>Codcard}" text="{remoteModel>Descript}" />
</Select>
I want the Zulu button to be disabled until the item inside the change function has a newVal set to it. Currently the Zulu button is enabled and this is an issue because I want it to be disabled until a certain condition, ie. item.set('textstuff', newVal); is met.
What is wrong with my code? I used this post: ExtJS 6 - Bind disabled property to new records in a store for some inspiration but still need some help.
title: 'Foo',
xtype: 'Bar',
autoSelect: true,
labelWidth: 150,
listeners: {
change: function(component, newVal, OldVal) {
var view = this.up('uploadthings');
var items = view.getViewModel().getStore('things').data.items;
items.forEach(function(item) {
item.set('textstuff', newVal);
});
view.getViewModel('bindBool', true);
}
}
}, {
items: [{
xtype: 'panel',
buttons: [{
style: 'margin-right: 30px',
text: 'Zulu',
iconCls: 'x-fa fa-upload',
handler: 'xray',
bind: {
disabled: '{bindBool}'
}
}]
}]
or in your ViewModel.Js file you can add formulas and configure your bondbool,
lets say you set the bindbool at the controller : me.getViewModel().set('bindbool', true)
in your ViewModel :
formulas : {
bindbool : {
get : function(get){
var tempVar = get('bindbool'); //--> true because you set it to true
return tempVar == true ? tempVar : false;
}
}
}
By doing this you can control your viewModel outputs,
Goodluck and have fun coding Extjs
Is there any possible way to create a dropdown that has a diffrent icon from fontawesome for each individuel option value? Or can I change the brackground color of each value? I prefer the circle icon from fontawesome where I can change the color of every record in the list.
I tried to add the Fontawesomecode of an Icon in the html part
<select data-bind="options: someList, optionsText: 'dropdownItemName', optionsValue: 'dropdownItemId', value: selectedSomeList, optionsCaption: ''" style="font-family: FontAwesome"></select>
I also tried to add it into the a <i></i> tag but it does nothing.
Does someone have an idea? Thanks you for the help
You can add the Unicode for the icon in the optionText binding (Unicode values are specified for each icon on font-awesome's site):
var viewModel = function() {
this.values = ko.observableArray([{
text: 'address-book \uf2b9',
value: 1
}, {
text: 'bookmark \uf02e',
value: 2
}, {
text: 'location-arrow \uf124',
value: 3
}]);
this.selectedValue = ko.observable(2);
}
ko.applyBindings(new viewModel());
select {
font-family: 'FontAwesome', 'Second Font name'
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<select data-bind="options: values,
optionsText: 'text',
optionsValue: 'value',
value: selectedValue">
</select>
(I borrowed the idea from this answer. But, it was being displayed as instead of the icon. It took a while to figure out)
I might be late. But try this one. jsfiddle
var viewModel = function() {
this.values = ko.observableArray([{
text: 'Visa',
value: 1,
icon:'fa-cc-visa'
}, {
text: 'Discover',
value: 2,
icon:'fa-cc-discover'
}, {
text: 'Amex',
value: 3,
icon:'fa-cc-amex'
}]);
OptionsAfterRender = (option, item) => {
ko.applyBindingsToNode(option, { css: item.icon }, item);
};
this.selectedValue = ko.observable(2);
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<select style="font-family:fontAwesome" multiple="multiple" size="3" data-bind="options: values,
optionsText: 'text',
optionsValue: 'value',
value: selectedValue,
optionsAfterRender:OptionsAfterRender">
</select>
I'm currently trying to get a kendo ColorPalette to work with inline editing on my grid. I've pretty much got it all figured out except that I'm having some difficulties posting the selected color value to my controller.
Kendo Grid:
$("#ContactTagsGrid").kendoGrid({
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: "/Admin/Tag/GetTagsByOrg",
dataType: "json",
data: {
orgId: OrganizationId
}
},
create: {
url: "/Admin/Tag/Create",
dataType: "json",
type: "POST",
data: function () {
return kendo.antiForgeryTokens();
}
}
},
schema: {
model: {
id: "Id",
fields: {
Id: { type: "number", nullable: true },
OrgId: { type: "number" },
Name: { type: "string" },
Color: { type: "string", defaultValue: "#f20000", validation: { required: true } }
}
}
},
pageSize: 20
}),
pageable: true,
sortable: true,
filterable: {
extra: false
},
scrollable: false,
columns: [
{
field: "Id",
hidden: true
},
{
field: "Name"
},
{
field: "Color",
editor: colorEditor,
template: function(dataItem) {
return "<div style='width: 25px; background-color: " + dataItem.Color + ";'> </div>";
},
width: "500px"
},
{
command: [
{
name: "Edit",
template:
"<a href='\\#' class='small btn btn-link k-grid-edit edit-text'><span class='fa fa-pencil'></span>Edit</a>",
text: "",
className: "fa fa-pencil"
},
{
template:
"<a href='\\#' class='small btn btn-link danger delete-text k-grid-delete'><span class='fa fa-trash-o'></span>Delete</a>",
name: "Delete",
text: " Delete",
className: "fa fa-trash-o"
}
],
width: "170px"
}
],
editable: {
mode: "inline",
destroy: false // don't use the kendo destroy method since we're using bootbox
},
// Custom save/cancel buttons
edit: function (e) {
var command = e.container.find("td:last");
command.html("<a href='\\#' class='small btn btn-primary k-grid-update'>Save</a><a href='\\#' class='small btn btn-default k-grid-cancel' style='margin-left: 5px'>Cancel</a");
}
});
Javascript to replace the default grid inline-editor with the Kendo ColorPalette:
function colorEditor(container, options) {
// create an input element
var div = $("<div></div>");
var input = $("<input />");
input.attr("name", "Color");
// append it to the container
div.appendTo(container);
input.appendTo(div);
// initialize a Kendo UI ColorPicker
div.kendoColorPalette({
palette: [
"#f20000", "#c60000", "#337a00"
],
columns: 3,
change: function () {
var color = this.value();
$("input[name=Color]").val(color);
}
});
}
When I click on the Save button, the only values that are posted to my controller are the Name and the OrgId.
If I set a defaultValue in the schema of my model like I did in the code above, then the default value for Color is always posted no matter if I select a different color or not.
If I do not set a defaultValue in the schema, then the value that is posted for Color is null.
So basically, I just need help updating my model so that it is correct when posting to the controller. I can see that the value of my input <input name="Color" /> is being updated correctly every time that I select a new color but again, it's not actually posting the value that it contains.
Not sure if this is needed, but here is what my model looks like:
public class TagCreateViewModel
{
public int OrgId { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
I ended up stumbling across this question on SO that helped me solve my problem. I just adapted it to use the Kendo Color Palette instead and it works perfectly now.
function colorEditor(container, options) {
$("<div type='color' data-bind='value:" + options.field + "'/>")
.appendTo(container)
.kendoColorPalette({
palette: [
"#f20000", "#c60000", "#337a00", "#54b010", "#9adc0d", "#28cb9b", "#0eac98", "#0ed6e8", "#14a7d1",
"#bc0aef", "#560ea7", "#2713bc", "#1457d1"
],
columns: 7
});
}
Javascript
var tiny_options = {
height: 120,
width: 300,
mode: 'textareas',
theme: 'advanced',
theme_advanced_buttons1: 'bold,italic,underline',
theme_advanced_buttons2: '',
theme_advanced_fonts: 'Arial=arial,helvetica,sans-serif,Courier New=courier new,courier,monospace,Georgia=georgia,times new roman,times,serif,Tahoma=tahoma,arial,helvetica,sans-serif,Times=times new roman,times,serif,Verdana=verdana,arial,helvetica,sans-serif',
theme_advanced_toolbar_location: 'top',
theme_advanced_toolbar_align: 'left'
};
//tinymce.init(tiny_options); // Please, remove comment to activate the tinymce
var initData = function (d) {
this.id = ko.observable(d.id);
this.text = ko.observable(d.text);
};
var viewModel = function () {
var self = this,
data = [{
id: 1,
text: 'some text 1'
}, {
id: 2,
text: 'some text 2'
}];
self.dataSet = ko.observableArray([]);
$.each(data, function (i, d) {
self.dataSet.push(new initData(d));
});
};
var model = new viewModel();
ko.applyBindings(model);
UI
<!-- ko foreach : dataSet -->
<br>
<textarea data-bind="value: text, valueUpdate : 'change'"></textarea>
<br>
<!-- /ko -->
Link to Demo
Above, code is working fine, i.e. model data is updating nicely without tinymce binding, but when I activate the tinymce, view model observable is not updating. I tried this also, but no result.
So, Please help me to configure, how can I update the view model observables using tinymce binding?
Looks like you need a custom binding that will bind up the value and apply the TinyMCE Editor to your <textarea>. The net result looking something like this;
<textarea data-bind="wysiwyg: text"></textarea>
Give the one I've put together on Github a try https://github.com/michaelpapworth/tinymce-knockout-binding
This is a simple custom binding to update observable:
ko.bindingHandlers.richTextEditor = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
window.tinymce.init({
target: element,
skin: "lightgray",
menubar: false,
statusbar: false,
forced_root_block: false,
browser_spellcheck: true,
toolbar: "bold italic underline",
valid_elements: "strong,br,em,span[style|class|id|data],i[class]",
formats: {
bold: { inline: "strong" },
italic: { inline: "em" },
underline: { inline: "span", styles: { "text-decoration": "underline" } }
},
plugins: "paste",
resize: false,
setup: function (editor) {
editor.on("change", function () {
var textInputBinding = allBindings().textInput;
var content = this.getContent();
textInputBinding && textInputBinding(content);
});
}
});
}
};
the binding on textarea should be data-bind="textInput: yourObservable".