I finally got bindings for the options and value to work on my dropdown component. What I have now works except that all javascript after the applyBinding for the value is not executed. This is not a problem when I only have one dropdown on the page at a time.
However, most of the time, I need to use more than one. When I step through the script on such a page, it executes the applyBinding for the first dropdown and stops. No further script runs, and only the first dropdown works.
Why is this? More importantly, how do I fix it?
Here are the relevant lines of code:
$(function () {
var $thisdd = $("##ddname"); //the JQuery selector for my dropdown
var dropdownItems = getDropdownItemsFromDl("#ddname");
var newitem = ko.observable({ cname: ddcname, cvalue: ko.observable($thisdd.val()), cpublishtopic: "" });
classificationsViewModel.push(newitem());
var viewModel =
{
dditems : dropdownItems
};
$("##ddname").attr("data-bind", "value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue, options: dditems, optionsText: 'value', optionsValue: 'key'");
ko.applyBindings(viewModel);
ko.applyBindings(classificationsViewModel()[classificationsViewModel.indexOf((newitem())].cvalue);
});
When you call ko.applyBindings, Knockout will apply the model to the entire document.
This is great if you have one model for the entire page.
It seems you want one model per dropdown. See the optional parameter on http://knockoutjs.com/documentation/observables.html#activating_knockout
You can then bind to each dropdown a separate model via something like ko.applyBindings(viewModel, $('##ddname')[0]);
I don't understand what this blurb is trying to do:
value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue
It seems you're trying to use two different models to control the dropdown. I am fairly certain that won't end well. Try using one model to rule them all to handle all the dropdowns. I think you'll find it much easier to maintain and logic about.
Related
I'm attempting to highlight 1 or more items in a select box in a unit test. I'm using Karma, Jasmine, and PhantomJS, AngularJS, JQLite, CoffeeScript.
My list has items ["banana", "apple", "orange"].
I tried setting the value directly:
sourceList = element.find('select').eq(1)
sourceList.val("[banana]").triggerHandler('change');
// Or
sourceList.val("banana").triggerHandler('change');
When I get sourceList.val() it's not set.
I tried triggering events to select it. Note I can't do a "click" event because I have another event fire on click.
sourceList.find('option').eq(0).triggerHandler("active");
sourceList.find('option').eq(0).triggerHandler("focus");
sourceList.find('option').eq(0).triggerHandler("drag");
sourceList.find('option').eq(0).triggerHandler("dragLeave");
I tried using the selectedIndex
sourceList.selectedIndex = 1
None of those seem to highlight or select the item. I'm out of ideas. Has anyone accomplished this?
Here is the method of the directive which I am trying to test:
// Clicks on the add button. Should take all items highlighted and move them over
$scope.add = function(){
var sourceList = $element.find('select').eq(1);
angular.forEach(sourceList.val(), function(val, index){
$scope.selected.push({
text: val
});
});
checkListDupes();
};
This code works when I do it manually in the browser but I can't seem to get my test to highlight some items in the select box before clicking the add button. So when this code executes sourceList.val() is equal to [].
A bit late to the party, but I'll leave it here for anyone else with the same problem.
I don't know how you've built the options for the select box but sourceList.val("banana").triggerHandler('change');
should work fine as long as the value of the option is banana. If you haven't specified a track by on the ng-options, the default values that angular creates will be 'string:'+<option label> so sourceList.val('string:banana').triggerHandler('change'); should do the trick.
Below is my first piece of knockoutjs code i've written. In the code below, i have two list boxes. the first is populated after the page loads and works fine. What i want to do next is that when a user selects an item from the cmbDataSets listbox, i want to make a second ajax call to populate the 'instruments' property of my view model. later when the user selects an instrument, i want to make yet another call to fetch data that i will display in a grid (using slickgrid.js).
Right now, i'd like to understand what are the ways or best practice for accomplish this. i think i can simply add normal html/javascript selection change event handler on the first list box to accomplish this...but i'm not sure if that is the recommended way (i know it's not the MVVM way anyway). I feel that since selectedDataSet is an observable, i should be able to chain that to an event handler as well..no? My question is how? Can i define an onSelectedDataSetChange method on my viewmodel and if so, how do i 'hook' it into the actually selection change of the cmbDataSets control?
<div class="container">
<label for="cmbDataSets">Select list:</label>
<select id="cmbDataSets" data-bind="options: dataSets, optionsText:'descr', value:selectedDataSet, optionsCaption:'Choose' " class="form-control"></select>
<label for="cmbInstruments">Select instrument:</label>
<select id="cmbInstruments" data-bind="options: instruments, optionsText:'intrument', value:selectedInstrument, optionsCaption:'Choose' " class="form-control"></select>
</div>
<script type="text/javascript">
$(document).ready(function () {
var viewModel = {
dataSets: ko.observableArray(),
instruments: ko.observableArray(),
selectedDataSet: ko.observable(),
selectedInstrument: ko.observable()
}
$.ajax({
url: '/ds/sets',
type: "GET",
dataType: "json",
success: function (data) {
debugger;
console.log(data);
viewModel.dataSets(data);
}
});
ko.applyBindings(viewModel);
});
</script>
You can subscribe to the first boxes selectedOption observable and make a call whenever it changes.
selectedOption = ko.observable();
selectedOption.subscribe(function (newValue) {
secondBoxSource(ajaxCallFunction(newValue));
});
Where ajaxCallFunction() is the function you use to fetch the data for the second box, and newValue is the newly selected value from the first box.
Mike.
Check this code, for setting change event in the View:
<select data-bind="event: { change: selectionChanged }"></select>
and then a proerty in the ViewModel:
selectionChanged: function(event) { }
Is this is what you were searching for? Other than this, I have a small suggestions - SelectedDataSet and selectedInstrument can be also observable arrays. The difference is that you are going to use not the 'value' biding, but the 'selectedOptions' one. This will help you when you have multiple selection, but even when it's a single one, it's a better option, I think.
I have a Select2 working fine attached to a select list but when someone enters a new item and I rebind the select list and try to programmatically select the new item, it refuses to display.
There are a number of other posts re this but their solutions don't work for me.
Use .select2("val", value). Does nothing.
Use .select2("data", value). Does nothing. Not sure how this is supposed to be different.
Use the InitSelection option to set a value. This leads to the following error message: Uncaught Error: Option 'initSelection' is not allowed for Select2 when attached to a select element.
In the Ajax success function after adding the new option to the database, I have the following Javascript:
BindDishes(RestaurantID); //rebinds the select list successfully
$('#hidDishID').val = data.DishID; //populates a hidden field
var arr = '[{id: "' + data.DishID + '", text: "' + data.DishName + '"}]';
$("#dd").select2("data", arr);
So in this latest iteration I have tried to supply an array with the id and text from the select list item as per another suggested solution but still nothing occurs. What am I doing wrong? Is this impossible when using Select2 with a select list?
Edit: I have the following line in MVC that creates the Select list:
#Html.DropDownGroupListFor(m => m.DishID, Model.GroupedSelectList, "Select a dish", new { id="dd",style="width:470px;" })
which must be causing the problem by binding to DishID from the model which was originally blank when it came from the server. DropDownGroupListFor is just an HTML helper that allows for using OptionGroups in a Select but it must be the binding that is a problem. What is the best approach here, to bind in a different way or to somehow update the model's DishID, not sure of the best practice?
var $example = $(".js-example-programmatic").select2();
$example.val("CA").trigger("change");
You need to destroy your select2 before adding/modifying items. If you do, then your select2 will respond just like its bound first time
Select2 Dropdown Dynamically Add, Remove and Refresh Items from
I have an AJAX MVC Contrib Grid implementation, that already existed and now I am in a situation where I am trying to bolt on some knockout functionality... and I want to know if this is possible without changing the whole grid implementation.
This is the refresh grid function that is setting the container html when the pagination changes.
scope.refreshGrid = function (container, url) {
if (url)
container.data(scope.selectors.actionUrlAttribute, url);
$.post((url || container.data(scope.selectors.actionUrlAttribute)), scope.getParams(),
function(html) {
container.html($(html).html());
scope.bindDeleteButtons();
}).done(function() {
container.trigger("refresh.ctb.grid");
});
}
one of the columns for the grid is custom column that uses Html.Partial like this:
column.Custom(x => Html.Partial("_CartSelection", new CartSelection(x.Id)));
The partial view has the below markup with some knockout data bindings
<input type="checkbox" value="#Model.Id" data-bind="enable: (selectionEnabled() || $element.checked), checked: selectionIds" />
This works for the first page of results, when the paging is selected to change the page and the container html() is updated the bindings no longer work but the KO viewModel still has the correct selectionIds.. which is what I was expecting to happen.
The KO view model is being applied as shown below, where the grid has a wrapper parent div with an id of "cart":
$(function() {
var viewModel = new IP.Configuration.CartSelector(new IP.Router());
ko.applyBindings(viewModel, document.getElementById("cart"));
});
I have already seen comments in other posts about how you shouldn't re-apply bindings. In my case it seems I want to apply bindings but only to some child nodes that are being dynamically loaded.
Is this possible?
UPDATE:
Almost had this working by adding a cart-selection class to each checkbox and doing the below in a rebind function on the viewModel, where self is the viewModel:
$("#cart .cart-selection").each(function(index, item) {
ko.applyBindings(self, item);
});
Then doing the below on the custom trigger for refreshing the grid, when the content is reloaded.
$("#cartGrid").on("refresh.ctb.grid", function() {
viewModel.rebind();
});
The issue I am finding with this at the moment is that the checkboxes are no longer enabled regardless of the $element.checked binding.. maybe a valueHasMutated will fix this, still looking into this.
I figured out what my remaining problem was, it was due to the ordering of the data bindings.
The enable data bind needed to be placed after the checked binding since it has a dependency on it via $element.checked which makes sense now after realising it!!
I changed my rebind function slightly to the below:
var gridResult = $("#cartGrid table");
if (gridResult.length > 0)
ko.applyBindings(this, gridResult[0]);
Each refresh brings in a new table but at least now if I add any more bindings to other elements in the results from the grid, they will work as expected.
I'm attempting to rebind the listview data after changing the template, based on a DropDownList value. I've included a JSFiddle for reference. When I rebind currently the values in the template are undefined.
Thanks!
JSFiddle link
I was thinking the best way to handle it would be in the 'select' or 'change' function:
var cboDetailsCategory = $("#detail").kendoDropDownList({
data: [
"All",
"Customer",
"Location",
"Meter",
"Other"],
select: function (e) {
var template = $("#" + e.item.text()).html();
console.log("template", template);
$("#details").html(template);
},
change: function (e) {
},
please refer to the JSFiddle link and this graphic as a visual
Here is a lengthier workflow:
User completes a name search and clicks a search button.
Name results are populated in a listview, rendered individually as button controls using a template.
User then clicks one of the name results (shown as the button text).
A dropdownlist of categories ('All' <--default , 'Location', 'Customer'...) gives the user the ability to target what subject of data they want to see. 'All' is the default, showing all details about the selected name.
So by default the 'All' template is populated.
If user wants to see the 'Location' details (template) they select it from the dropdownlist.
The template shows but the values are all blank. The only way to populate it is to click the name (button) again.
I want to remove the need for having to re-click the button (name) to populate the template ('Location', etc...).
I have put together a JSFiddle showing the structure. Though due to the data being private and served over secure network I cannot access it.
Refer to JSFiddle:
I believe the issue is that the onclick event grabs the data-uid and passes it to the initial default template (named 'All' but it's not included in code as it's lengthy). When the user changes the dropdownlist (cboDetailsCategory) and selects a new template I lose the data.
Thanks for your help. I'm really stuck on this and it's a current show stopper.
There isn't an officially supported way to change templates, without destroying the listview and rebuilding it. However, if you don't mind poking into into some private api stuff (be warned I can't guarantee that kendo won't break it without telling you) you can do this
var listview = $("#MyListview").getKendoListView();
listview.options.template = templateString;
listview.template = kendo.template(listview.options.template);
//you can change the listview.altTemplate the same way
listview.refresh(); //redraws the elements
if you want to protect against unknown API changes you can do this, which has A LOT more overhead, but no risk of uninformed change (untested!)
var listview = $("#MyListview").getKendoListView(),
options = listview.options;
options.dataSource = listview.dataSource;
listview.destroy();
$("#MyListview").kendoListView(options);
Here's the solution, thanks for everyone's help!
JSFiddle Link
The issue was where I was setting the bind:
$("#list").on("click", ".k-button", function (e) {
var uid = $(e.target).data("uid");
var item = dataSource.getByUid(uid);
var details = dropdown.value();
var template = $("#" + details).html();
$("#details").html(template);
kendo.bind($("#details"), item);
currentData = item;
});