Knockout : Unusual Mapping Pattern - javascript

Scenario: I make a request to the server for a part. It gives me this back (it's pseudo, but represents what I'm looking at):
{
PartNumber : "XYZ",
Description: "ABCFOO",
ProductClass: "Widget",
FieldList:[
{Name: "PROGRAM TYPE", Value: "Program3"},
{Name: "SHIP", Value: false},
{Name: "NOTES", Value: "SomeValue1"}
],
MoreStuff : [{
...
}]
}
Note the FieldList list of elements, that's the focus here.
The server also gave me a list of certain fields, their types and default values. It looks like this:
[
{FieldName : "PROGRAM TYPE", FieldType: "List", Defaults: [{Name:"Program 1", Value: "Program1"},{Name:"Program 2", Value: "Program2"},{Name:"Program 3", Value: "Program3"}]},
{FieldName : "SHIP", FieldType : "Boolean", Defaults: []},
{FieldName : "NOTES", FieldType: "TextArea", Defaults: []}
]
That comes in a seperate REST call, and the prior to loading my Part. I use it to create part of the HTML page for the Part. You can see they're similarly related to the FieldList section from when I ask for Part.
From that "list of fields" and defaults -- I generate the appropriate HTML elements on the page. If it's a Boolean field type, I create a checkbox - if it's a list, I create a SELECT (with options given in Defaults), TextArea is a text-area, etc. That all works fine. It ends up looking like:
<input data-bind="textInput: PartNumber"/>
<textarea data-bind="textInput: Description"></textarea>
<!-- generating fieldlist - i create a pseudo attr because the field name can have spaces-->
<select field_label="PROGRAM TYPE"> <!-- how the heck do i bind to this??-->
<option value="Program1">Program 1</option>
<option value="Program2">Program 2</option>
<option value="Program3">Program 3</option>
</select >
<input type="checkbox" field_label="SHIP" value="true"/> <!-- or this, how to bind to it?!-->
<!-- end of field list generation -->
Now I take the object (the part I'm given) and put that into my ViewModel - that is all working just swimmingly. I make it easy and just use ko.mapping.fromJS(rest_data); Works just fine.
Data binding is ducky -- for what I am able to bind it to. My issue comes from -- how the heck do I map my FieldList to the HTML I generated for the fields the server gave me?. My data / my viewmodel object has FieldList in it, with a buncha stuff I want to map to that generated stuff. The only real "key" I have is the self-created field_label I have, because the server's FieldName can have spaces.
So I guess what I'm asking is, I have that array of FieldList from my part. I have the whole Part object in my view model and it's all fine. How do I take that FieldList and map it into my self-generated set of fields from the other object (ie., take the FieldList name and tie it to the element with field_label of the same value?)
Spelled out - it'd be like: How to map FieldList with Name of "PROGRAM TYPE" to HTML element having field_label of "PROGRAM TYPE".
I begin to think something like this might be the direction I should be going:
http://jsfiddle.net/MhdZp/128/
but it goes over my head.

One way to approach this:
function Option(definition) {
this.definition = definition;
this.value = ko.observable();
this.templateName = 'input-template-' + definition.FieldType;
}
function ViewModel() {
var self = this;
// from REST call
var fieldDefinition = [{
FieldName: "PROGRAM TYPE",
FieldType: "List",
Defaults: [
{ Name: "Program 1", Value: "Program1" },
{ Name: "Program 2", Value: "Program2" },
{ Name: "Program 3", Value: "Program3" }
]
}];
self.options = ko.observableArray();
// for the sake of the example
self.options.push(new Option(fieldDefinition[0]));
// methods
self.optionByName = function (name) {
return ko.utils.arrayFirst(self.options(), function (option) {
return option.Name = name;
});
};
// poor man's init, imagine 2nd rest call instead
self.optionByName("PROGRAM TYPE").value("Program3");
}
and
<script type="text/html" id="input-template-List">
<label data-bind="text: definition.FieldName"></label>
<select data-bind="
value: value,
options: definition.Defaults,
optionsText: 'Name',
optionsValue: 'Value',
optionsCaption: 'Please select...'
"></select>
</script>
and
<div data-bind="foreach: options">
<div data-bind="template: templateName"></div>
</div>
Add more templates as needed, this should be very easy to extend.
jsFiddle: http://jsfiddle.net/0nxt2zte/

Related

Knockup.js dropdown not working after adding KO binding

I am trying to bind the dropdown . following code works
<select data-bind="options :MyArray"/>
However it doesn't works but when i add the Knockout bindings (as below) .the dropdown doesn't shows up
<select data-bind="options: MyArray, event:{change:DropdownChnaged.bind($data,'Task')} "/>
It looks as though you have a typo in your change event (DropdownChnaged -> DropdownChanged). That maybe the root cause of the issue.
But I would also avoid using the change event and instead use knockouts subscriber functionality.
When the dropdown value is being set, you can subscribe to the value and execute a function when the observable value is updated. I have provided a snippet below which should demonstrate how you can use this.
var VM = function() {
this.MyArray = [
{
text: "Item A",
data: "This is the description for Item A",
otherData: { myData: 'MyOtherDataA'}
},
{
text: "Item B",
data: "Description of Item B",
otherData: { myData: 'MyOtherDataB'}
},
{
text: "Item C",
data: "This is Item C's description",
otherData: { myData: 'MyOtherDataC'}
}
];
this.selectedItem = ko.observable();
this.selectedItem.subscribe(function(latest)
{
console.log("Change event triggered");
console.log(latest);
}, this);
};
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="value: selectedItem, options: MyArray, optionsText: 'text'">
</select>
<!-- ko with: selectedItem -->
<p>
Item Description: <span data-bind="text: data"></span>
</p>
<!-- /ko -->

How can we use the "name" or "id" attribute values as the data property in Vue JS?

I am working on an SPA application where I have a list of data variables that are used as the <option> tags in the dropdown. I want to navigate to another page on the #change event of the dropdown therefore I want to use either the id or name of the select command as the name of the data property. Here is what I mean:
Here is what I have in the data function:
data(){
return {
participants: [
{ value: 0, linkText: '', linkTerm: 'Select' },
{ value: 1, linkText: '/meetings/participants/create', linkTerm: 'Add New' },
{ value: 2, linkText: '/meetings/participants', linkTerm: 'All Participants' },
],
positions: [
{ value: 0, linkText: '', linkTerm: 'Select' },
{ value: 1, linkText: '/meetings/positions/create', linkTerm: 'Add New' },
{ value: 2, linkText: '/meetings/positions', linkTerm: 'All Positions' },
],
}
}
Here is the select tag where I use the above data variables as the <option> tag:
<select name="participants" id="participants" class="select-field" #change="changeRoute($event)">
<option v-for="p in participants" :value="p.value">{{ p.linkTerm }}</option>
</select>
Now I want to have one function changeRoute($event) from which I will navigate to different pages, therefore I want to use the id or name value as the data property, here is the function:
methods:{
changeRoute($event){
var name = $event.target.name;
var value = document.getElementById($event.target.id).value;
}
}
Here in the above function I want to use the name as the data property as below:
I want to write this:
this.name[value].linkText;
And because name here is the name of the tag which is actually participants so the above line of code should mean something like this:
this.participants[value].linkText
And that should return the linkText of the participants object of the data function.
Any help is appreciated in advance.
Change the below line
this.name[value].linkText;
to
this[name][value].linkText;

AngularJS and Select

I'm having trouble understanding why my Angular <select> doesn't add the default value to the category I want.
I have an object that is returned from our database in Parse; the object is like this ( this is the "selected" option ):
$scope.item.category = { id: 'uosDHIF', className: 'Category', name: 'Projects' }
And I have a list of categories also:
$scope.categories = [{ id: 'uosDHIF', className: 'Category', name: 'Games' }, { id: 'uosDHIF', className: 'Category', name: 'Random' }, { id: 'uosDHIF', className: 'Category', name: 'Projects' }]
In my HTML what I have it's a select like this:
<select name="category" class="form-control mb-10" ng-model="item.category" ng-options="category.name for category in categories"></select>
I can select a new category and it's assigned to the item, and then I can save the item with item.save() but what I have trouble doing is that the selected (ng-model) doesn't seem to work, when I enter the page the first time I always get the default blank option even if I have the category when I check in the console.
Also, I was able to make it work with a simple object that I have created myself, but this object is returned from our database in Parse, so I'm not sure if this is the problem or I'm just missing something in there.
Thank you for your help.
Note: I have read other StackOverflow problems similar to mine but they doesn't help with mine.
In fact, on ng-options, you have to specify the value for the model (category as category.name => you choose category for ng-model but display it by his name).
The matching occurs by reference so you need the same object (category : $scope.categories[0]).
You can try:
JS
$scope.categories = [
{ id: 'uosDHIF', className: 'Category', name: 'Games' },
{ id: 'uosDHIF', className: 'Category', name: 'Random' },
{ id: 'uosDHIF', className: 'Category', name: 'Projects' }
];
$scope.item = {
category : $scope.categories[0]
};
HTML
<select name="category" class="form-control mb-10" ng-model="item.category" ng-options="category as category.name for category in categories"></select>
Please change the html as below.. Hope this is helpful.
ng-model="item.category.id" ng-options="category.id as category.name for category in categories">

knockout.js not setting select option properly

Maybe I am misunderstand how this works. I want to use knockout.js to populate a select element options. I am using the following markup to achieve this:
<select data-bind="options: type_options, optionsText: function(item) {
return item.text;
}, optionsValue: function(item) {
return item.value;
}, optionsCaption:'Select a type...',
value: type">
Here is the relevant model code:
var myModel = {
type: ko.observable(),
type_options: ko.observableArray([
{text: "String 1", value:1},
{text: "String 2", value:2},
{text: "String 3", value: 3},
{text: "String 4", value: 4},
{text: "String 5", value: 5}
]),
}
Now the drop down renders correctly, with all the correct text and values, but when I select the an option from the drop down it doesn't set the value of 'type' correctly.
For instance if I selected the option labeled "String 4", and run the following command in the browser:
myModel.type()
I would expect it to return the value "4". Instead i get the object entire object:
Object
text: "String 4"
value: 4
__proto__: Object
My question is how do i get knockout to set the value of type based on the option's value attribute, instead of the entire object?
Well, you should be able to pass the text for the variable in you options array instead of a function. I don't know if that's what's causing the issue but your markup would look better like
<select data-bind="options: type_options, optionsText: 'text', optionsValue: 'value', optionsCaption:'Select a type...', value: type"></select>
That should get you what you want, see fiddle for full example.

Binding text value to another property of view model when using a dropdown

I am trying to display an observable array in a dropdown, and when the user selects the option, I want to present a property of that view model. It seems simple. I have provided code and a link to Fiddle.
HTML:
<select data-bind="options: oCountries,
optionsText: 'name',
optionsValue: 'isocode',
value: selectedCountry">
</select>
Controlled by <span data-bind="value: selectedCountry.isocode"></span>
JS:
var countries = [{
"name": "Afghanistan",
"isocode": "AF",
"language": "English",
"crmdatacontroller": "CRM India"
}, {
"name": "Aland Islands",
"isocode": "AX",
"language": "Finnish",
"crmdatacontroller": "CRM Finland"
}]
var viewModel = {
oCountries: ko.observableArray(countries),
selectedCountry: ko.observableArray(['AX'])
};
ko.applyBindings(viewModel);
See also this fiddle
The optionsValue parameter controls what is written by the value binding. So, in your case, it will write the isocode to selectedCountry.
So, the simplest changes to your code would be to set selectedCountry to a normal observable and the appropriate isocode. Then, have your span use the text binding against selectedCountry.
Like this: http://jsfiddle.net/wxNrt/21/
Now, a couple other options available to you as well:
-if you leave off optionsValue in the binding, then it will set/read the object directly. In that case, you need to change how you are intially setting it.
http://jsfiddle.net/wxNrt/23/
-if you want to send/receive a key (like the isocode), then you can use optionsValue, but set up a dependentObservable to represent the actual object like:
http://jsfiddle.net/wxNrt/22/
-if you are dealing with a multi-select situation, then you want to use the selectedOptions binding instead of value, as described here: http://knockoutjs.com/documentation/selectedOptions-binding.html
I changed your markup to this:
<select data-bind="options: oCountries, optionsText: 'name', optionsValue: 'isocode', value: selectedCountry">
</select>
Controlled by <span data-bind="text: selectedCountry"></span>
And the script:
var countries = [
{
"name": "Afghanistan",
"isocode": "AF",
"language": "English",
"crmdatacontroller": "CRM India"},
{
"name": "Aland Islands",
"isocode": "AX",
"language": "Finnish",
"crmdatacontroller": "CRM Finland"}
]
var viewModel = {
oCountries: ko.observableArray(countries),
selectedCountry: ko.observable('AX')
};
ko.applyBindings(viewModel);
And the jsfiddle

Categories