I have a scenario that I'm attempting to apply knockout to with some problems. Basically I have this sort of ui
Add (create a new Select Box duo with delete button)
Select Box (options = Json from ajax request)
Select Box (options = Json from ajax request with param from 1st select)
Delete
Select Box
Select Box
Delete
etc
Each row I regard as another Widget in the array so my knockout for simplicity
var ViewModel = function (widgets) {
var self = this;
this.widgets= ko.observableArray(widgets);
this.subWidgets= ko.observableArray();
this.mySelections = ko.observableArray();
this.selectedWidget.subscribe(function (name) {
if (name != null) {
$.ajax({
url: '#Url.Action("AddSubWidgetsByName")',
data: { name: name },
type: 'GET',
async: false,
contentType: 'application/json',
success: function (result) {
self.subWidgets(result);
}
});
}
} .bind(this));
self.addWidget = function (widget) {
self.mySelections.push({
??? profit
});
};
}
var viewiewModel = new ViewModel();
ko.applyBindings(viewiewModel);
$.ajax({
url: '#Url.Action("AddFund")',
type: 'GET',
async: false,
contentType: 'application/json',
success: function (result) {
viewModel.widgets(result);
}
});
<select id="widgets"
data-bind='
options: widgets,
optionsValue : "Name",
optionsText: "Name",
optionsCaption: "[Please select a widgets]"'
value: selectedWidget,
>
Can I dynamically create a select for each widget and relate the subwidget selection to an item in mySelections array? I can't use the value binding for selectedWidget in quite this way as all dropdowns are bound together in this manner. I need to make them independant - any ideas on how to go about that?
Cheers!
One way of doing this is to make each widget its own viewmodel (note, this is from jsFiddle, so the ajax is done to work with their echo API, which requires POST):
var Widget = function(){
var self = this;
self.selectedWidget = ko.observable('');
self.subWidgets = ko.observableArray([]);
self.selectedSubWidget = ko.observable('');
this.selectedWidget.subscribe(function (name) {
if (name != null) {
$.ajax({
url:"/echo/json/",
data: {
json: $.toJSON(
[Math.floor(Math.random()*11),
Math.floor(Math.random()*11),
Math.floor(Math.random()*11)]
),
delay: 1
},
type:"POST",
success:function(response)
{
self.subWidgets(response);
}
});
}
});
};
You could then easily track sub-selections and additions with a simple viewmodel. Here is the complete fiddle.
Related
I am new to ExtJS and I am trying to implement a combo box and 'Save' button that will save the "boxStatus" for all of the records selected in my grid (there is a little checkbox column for selecting records, and a combo box for the status). I have tried with the following ajax call:
saveBulkBoxComments : function(){
var formObj = this.getPositionPanel().getForm();
var fieldValues = formObj.getFieldValues();
Ext.Ajax.request({
url: SAVE_BULK_BOX_COMMENTS_URL,
method: 'POST',
params: {
positionDate: this.parentRecordData.data.positionDate,
groupIds: this.parentRecordData.data.groupId,
boxStatus: fieldValues.boxStatus,
csrComment: fieldValues.csrComment
},
success : function(response) {
//how do I update box status for all records selected?
this.loadPositions();
},
scope: this
});
}
Here is the Java:
return getReturnValue(new Runnable() {
public void run() {
String groupIdList[] = groupIds.split(",");
String user = mercuryUserScreenNameGetter.getValue(request);
Date date = Utils.parseDate(positionDate, DATE_FORMAT);
Stream.of(groupIdList)
.forEach(groupId ->
positionsDataMediator.addBoxAnnotation(date,user, groupId, csrComment, boxStatus));
}
});
I am not really sure how to post all of the boxStatus for all of the records selected. Would I have to write a method that iterates over all of the records when I hit Save? That seems wrong...Thanks for the help.
After some fiddling, I got it working. The trick was to iterate over each groupID, for all of the selected records...this way I was able to update the boxStatus for each of those records at once:
saveBulkBoxComments : function(){
grid = this.getPositionPanel();
var store = grid.getStore();
formObj = this.getBoxCommentsFormPanel().getForm();
var fieldValues = formObj.getFieldValues();
var value='';
selectedRecords = grid.getSelectionModel().getSelection();
Ext.each(selectedRecords, function(item) {
value +=item.get('groupId') +',';
}
);
Ext.Ajax.request({
url: SAVE_BULK_BOX_COMMENTS_URL,
method: 'POST',
params: {
groupIds :value,
positionDate: this.parentRecordData.data.positionDate,
boxStatus: fieldValues.boxStatus,
csrComment: fieldValues.csrComment
},
success: function(result, action, response) {
var jsonData = Ext.decode(result.responseText);
var jsonRecords = jsonData.records;
Ext.getCmp('boxCommentsWindow').close();
this.loadPositions();
},
scope: this
});
}
});
I have two select lists, one select list is populated by the selection of the other select list. I have subscribed to the value of the first select box to update the second list every time the value changes. What I am failing to discover is how to reset the second list if a user were to select the default "select ..." option in the first list.
To be clear, the process works, except when a user selects the default options. The Second list keeps the previous selection at that point.
Here's my code (this is an MVC 4 application).
ViewModel:
var DashboardReportViewModel = function (config, originalData) {
var self = this;
self.breweryCode = ko.observable();
self.lineCode = ko.observable();
self.GetBreweries = ko.observableArray([]);
self.GetLines = ko.observableArray([]);
var loadBreweries = function () {
$.ajax({
url: config.GetBreweries,
type: "GET",
error: function (xhr, status, error) {
alert('loadBreweries error');
},
success: function (data) {
self.GetBreweries(data);
},
cache: false
});
};
var loadLines = function () {
$.ajax({
url: config.GetLines,
data: { breweryCode: self.breweryCode() },
cache: false,
type: "GET",
error: function (xhr, status, error) {
alert('loadLines error');
},
success: function (data) {
self.GetLines(data);
}
});
}
loadBreweries();
self.breweryCode.subscribe(function () {
if (self.breweryCode() != undefined) {
loadLines();
}
});
};
View:
<select class="ui-select" id="BrewerySelect" name="BrewerySelect" data-bind="options: GetBreweries,
optionsText: 'Text',
optionsValue: 'Value',
value: breweryCode,
optionsCaption: 'Select a Brewery'"></select>
<select class="ui-select" id="LineSelect" name="LineSelect" data-bind="options: GetLines,
optionsText: 'Text',
optionsValue: 'Value',
value: lineCode,
optionsCaption: 'Select a Line'"></select>
I would go this way:
self.breweryCode.subscribe(function () {
if (!!self.breweryCode()) {
loadLines();
} else {
self.GetLines([]);
}
});
If self.breweryCode() is truthy an Ajax call will be placed to load the lines, otherwise that list is just emptied completely.
PS. If you like you could also use observableArray utility methods to do self.GetLines.removeAll().
You missing else case here:
self.breweryCode.subscribe(function () {
if (self.breweryCode() != undefined) {
loadLines();
}
else {
self.GetLines([]);
}
});
I have a page with three form fields (2 textbox, 1 dropdown), a submit button and a 'refresh' link. I want to be able to click the link and pass two form textbox values to a controller action, and get a list of values to populate the dropdown box. I do not want to submit the form at this stage.
At the moment, I have managed to call the controller action from the link click, but I cannot pass the two form field values in for some reason. Also, the return JSON just takes me to a new page instead of populating my dropdown list. Any pointers would be great as I am new to javascript and MVC. My code is below;
Controller
public ActionResult Find(AddressFormViewModel model)
{
...
var temp = new List<OptionModel>();
temp.Add(new OptionModel {Id = item.Id, Value = item.desc});
return Json(temp, JsonRequestBehavior.AllowGet);
}
HTML
#Html.TextBoxFor(x => Model.HouseNameInput, new { id = "HouseNameInput" })
#Html.TextBoxFor(x => Model.PostCodeInput, new { id = "PostCodeInput" })
#Html.ActionLink("Find","Find", "Address", new { houseInput = Model.HouseNameInput, postcodeInput = Model.PostCodeInput }, new { htmlAttributes = new { #class = "Find" } })
#Html.DropDownListFor(x => Model.AddressOption, Enumerable.Empty<System.Web.Mvc.SelectListItem>(), "-- Loading Values --", new {id = "AddressOptions"})
And lastly, my Javascript method which is retrieving the data from the controller action but not populating the dropdown list (it displays the results in a new page). It is also not successfully sending the form values to the controller action.
$(function () {
$('.Find').click(function (evt) {
$.ajax({
type: 'POST',
url: '#Url.Action("Find","AddressFormSurface")',
cache: false,
async: true,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: {
houseNameInput: $("#HouseNameInput").value,
postCodeInput: $("#PostCodeInput").value
},
success: function (data) {
if (data.exists) {
var ddl = $('#AddressOptions');
ddl.empty();
data.each(function () {
$(document.createElement('option'))
.attr('value', this.Id)
.text(this.Value)
.appendTo(ddl);
});
}
},
error: function (req) {
}
});
// we make sure to cancel the default action of the link
// because we will be sending an AJAX call
return false;
});
});
You have a number of errors in your script which will cause it to fail.
You specify contentType: "application/json; charset=utf-8", but do
not stringify the data (the option should be removed)
You need to use .val() (not .value) to get the values of the
inputs
The data you receiving does not contain a property named exists so
the if block where you append the options will never be hit
In addition it is unnecessary to generate your link using #Html.ActionLink() (your adding route values based on the initial values of the model). Instead just create it manually
Find
and change the script to
var ddl = $('#AddressOptions'); // cache it
$('#find').click(function () { // change selector
$.ajax({
type: 'GET', // its a GET, not a POST
url: '#Url.Action("Find","AddressFormSurface")', // see side note below
cache: false,
async: true,
dataType: "json",
data: {
houseNameInput: $("#HouseNameInput").val(),
postCodeInput: $("#PostCodeInput").val()
},
success: function (data) {
if (!data) {
// oops
return;
}
ddl.empty();
$.each(data, function(index, item) {
$(document.createElement('option'))
.attr('value', item.Id)
.text(item.Value)
.appendTo(ddl);
// or ddl.append($('<option></option>').text(item.Value).val(item.Id));
});
},
error: function (req) {
....
}
}
});
Side note: Also check the name of the controller. Your Html.ActionLink() suggests its AddressController but your script is calling AddressFormSurfaceController
Hi I have a observable array return from a web API.
1) How to I bind the return jSon as follows to the view model and how do I access it in view?
2) Since there is no information about which option is selected from the returned jSon, how do I make the view initially display the selected option based on the self.selectedAnimal (which is the selected text)?
function NewViewModel() {
var self = this;
self.selectedAnimal = "Cat";
self.GetAnimal() {
$.ajax({
url:"http:/abc.com/api/GetAnimalList",
dataType: "json",
type: "GET",
data: {}
success: function() {
// What to assign here
}
});
}
}
ko.applyBindings(new NewViewModel());
// example of json return
"animals": [
{
"animalid": "1",
"animalname": "cat" },
{
"animalid": "two",
"animalname": "dog" },
{
"animalid": "three",
"animalname": "horse"}
]
Use observableArrays. Like this:
function NewViewModel() {
var self = this;
self.selectedAnimal = ko.observable('Cat');
self.animals = ko.observableArray();
self.getAnimals = function() {
$.ajax({
url:"http:/abc.com/api/GetAnimalList",
dataType: "json",
type: "GET",
data: { }
success: function(animals) {
self.animals(animals);
}
});
};
//reload the animals
//load the view
self.getAnimal();
}
In your view:
<div data-bind="foreach: animals">
<label data-bind="text:animalname"></label>
</div>
Fiddle with example https://jsfiddle.net/vnoqrgxj/
If you have:
<select data-bind="options: animalOptions,
optionsText: 'animalname',
optionsValue: 'animalid',
value: selectedAnimal,
optionsCaption: 'Select animal'">
</select>
As select markup in HTML, then in your view model add the animalOptions array and fill that when the ajax request returns success.
function NewViewModel() {
var self = this;
self.selectedAnimal = "two"; // initial selection as 'dog'
self.animalOptions = ko.observableArray([]);
function GetAnimal() {
$.ajax({
url:"http:/abc.com/api/GetAnimalList",
dataType: "json",
type: "GET",
data: {},
success: function(data) {
// What to assign here
$.each(data.animals, function(i,option){
self.animalOptions.push(option);
});
}
});
}
GetAnimal();
}
ko.applyBindings(new NewViewModel());
For the initially selected option, set self.selectedAnimal = "two" i.e. the animalid value of desired selection.
Read this for more information about options binding.
Please help. I'm using MVC, razor 3, jquery.
I dynamically create a multi select box when a dropdown selection changes. I bind the multiple selection to a List in my model. And it works, except it passes me the list of selected indice, instead of a list of the selected text. I want selected text, not index of the list. I set the value as text, but I have no luck.
if I manually create the list, everything works. How do I pass a list of selected options back to the controller?
I have this div in my view:
<div class="row-fluid" id="divAvailableAssemblies" hidden ="hidden">
<label class="span4">Available Assemblies:</label>
<select multiple="multiple" class="span8 ui-corner-all" id="Request_SelectingAssemblies" name="Request.SelectingAssemblies">
#*<option value="test">test</option>
<option value="test2">test2</option>*#
</select>
</div>
Here my jquery:
<script type="text/javascript">
$(function () {
$('#ddPartsToCreate').live('change',function () {
var selectedPart = this.value;
if (selectedPart < 6 || $("#txtOrderNumber").val()=="")
{
$("#divAvailableAssemblies").attr("hidden", "hidden");
return;
}
$("#divAvailableAssemblies").removeAttr("hidden");
$.ajax({
type: 'POST',
url: '#Url.Action("GetSelectingAssembliesFromOrder", "Home")',
data: JSON.stringify({ orderNumber: $("#txtOrderNumber").val() }),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
cache: false,
async: false,
success: function (response) {
var returnedData = JSON.parse(response);
var selectingAssemblies = $("#Request_SelectingAssemblies");
selectingAssemblies.empty();
for (var assembly in returnedData)
{
//selectingAssemblies.append($('<option >', { value: assembly }).text(returnedData[assembly].Text)).hide().show();
//selectingAssemblies.append($('<option value=' + assembly + '>' + returnedData[assembly].Text + '</option>'));
//selectingAssemblies.append($('<option >', { value: assembly, text: returnedData[assembly].Text }));
//selectingAssemblies.append($('<option></option>').val(assembly).html(returnedData[assembly].Text));
//$("#Request_SelectingAssemblies").append($('<option>', { value: assembly }).text(returnedData[assembly].Text));
//$("#Request_SelectingAssemblies").append($('<option>', { value: assembly }).text(returnedData[assembly].Text));
//$('<option />', { value: assembly, text: returnedData[assembly].Text }).appendTo(selectingAssemblies);
selectingAssemblies.append($('<option></option>').val(assembly).html(returnedData[assembly].Text));
}
},
error: function (jqXHR, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
});
in the backend, I generate JSON:
foreach (var assembly in pr.ShipParts)
{
sb.Append(String.Format(",{{\"Text\":\"{0}\", \"Value\":\"{1}\"}}", assembly.Mark.ToString(), assembly.Mark.ToString()));
availableAssemblies.Add(assembly.Mark.ToString());
}
I bind the multiple selection(Request_SelectingAssemblies) with this property in my model:
public List<String> SelectingAssemblies
{
get
{
return _SelectingAssemblies;
}
set
{
_SelectingAssemblies = value;
}
}
private List<String> _SelectingAssemblies = new List<string>();
When it gets to my action in the controller, SelectingAssemblies has index instead of the actual text. But I set the value of each option as text. If I set the option manually, they will show in source page and return the text. But since I dynamically create the options, they don't show in source page. I don't know how I can make MVC understand dynamic data.
In the picture, the list of CX001, RBX001, RBX002 is dynamically created. if I hit F12 in IE, I will see them created correctly in the DOM. If I choose CX001 and RBX002, SelectionAssembies will have 0 and 2.
Thanks
This is the latest and working code, thanks to #StephenMuecke:
<script type="text/javascript">
$(function () {
$('#ddPartsToCreate').live('change',function () {
var selectedPart = this.value;
if (selectedPart < 6 || $("#txtOrderNumber").val()=="")
{
$("#divAvailableAssemblies").attr("hidden", "hidden");
return;
}
$("#divAvailableAssemblies").removeAttr("hidden");
$.ajax({
type: 'POST',
url: '#Url.Action("GetSelectingAssembliesFromOrder", "Home")',
data: JSON.stringify({ orderNumber: $("#txtOrderNumber").val() }),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
cache: false,
async: false,
success: function (response) {
var returnedData = JSON.parse(response);
var selectingAssemblies = $("#Request_SelectingAssemblies");
$.each(returnedData, function (index, item) {
selectingAssemblies.append($('<option></option>').val(item.Value).html(item.Text));
});
},
error: function (jqXHR, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
});
public ActionResult GetSelectingAssembliesFromOrder(String orderNumber)
{
return Json(model.GetSelectingAssembliesFromOrder(orderNumber), JsonRequestBehavior.AllowGet);
}
public String GetSelectingAssembliesFromOrder(String orderNumber)
{
//...
StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (var assembly in pr.ShipParts)
{
string assemblyName = assembly.Mark.Trim();
sb.Append(String.Format(",{{\"Text\":\"{0}\", \"Value\":\"{1}\"}}", assemblyName, assemblyName));//JSON to build the list
//...
}
sb.Append("]");
sb.Remove(1, 1);//remove extra comma
_db.SaveChanges();
return sb.ToString();
}