Bind dropdown to knockout value from external modal - javascript

I have a view model containing 2 arrays. One array is an array of users, and the other is an array of user levels.
{
"Users": [
{
"UserLevel": {
"Permissions": [],
"Id": 2,
"Name": "Developer",
"SortOrder": 1,
"IsHidden": false
},
"Id": 1,
"Username": "Björn Jakobsson",
"Password": null,
"Fullname": null,
"Email": "bjiorn#bjinteractive.se",
"Phone": null
}
],
"UserLevels": ko.observableArray([
{
"Permissions": [],
"Id": 1,
"Name": "Admin",
"SortOrder": 2,
"IsHidden": false
},
{
"Permissions": [],
"Id": 2,
"Name": "Developer",
"SortOrder": 1,
"IsHidden": false
}
])
}
and the drop down
<select data-bind="options: $parent.UserLevels(), optionsText:'Name', value: UserLevel" class="form-control"></select>
While editing a user from the user array i have a dropdown for choosing user level of this user wich is populated from the UserLevels array. If I choose a user level (in this case Developer) and saves the value in my database is saved and everything, and a reload of the page shows the correct value, but as soon as I choose to edit the user (using a bootstrap modal and with-data binding, the drop down automatically selects Admin (first in the array) and not Developer from my user model, and then the user model is updated since the user level of the user is bound to the drop down.

I believe your binding is incorrect. You are not binding to the observable array, but instead to what the observable array resolves to (i.e. the array)
Use
options: $parent.UserLevels
instead of
options: $parent.UserLevels()

You probably need to add an optionsValue binding.
From doc:
Typically you’d only want to use optionsValue as a way of ensuring
that KO can correctly retain selection when you update the set of
available options. For example, if you’re repeatedly getting a list of
“car” objects via Ajax calls and want to ensure that the selected car
is preserved, you might need to set optionsValue to "carId" or
whatever unique identifier each “car” object has, otherwise KO won’t
necessarily know which of the previous “car” objects corresponds to
which of the new ones.

I found a way to get around the problem with my own biding instead for value. Below is the code I use. Any drawbacks or mistace to edit? I'm pretty new to KO and still have problems sometimes when to use unwrap, unwrapobservable etc, but below code works. Ideas for improvement?
ko.bindingHandlers.valueBasedOnObject = {
init: function (element, valueAccessor, allBindings) {
var prop = allBindings().optionsValue;
var value = valueAccessor();
$(element).change(function () {
var value = $(element).val();
var selectedModel = ko.utils.arrayFirst(ko.unwrap(allBindings().options()), function (item) {
return ko.utils.unwrapObservable(item[prop]) == value;
});
valueAccessor()(selectedModel);
});
var valueUnwrapped = ko.unwrap(value);
$(element).val(valueUnwrapped[prop]);
},
update: function (element, valueAccessor, allBindings) {
/* */
}
};

Related

Update single object in localStorage

I want to update a single Object in my localStorage. I made a detail page, where I can submit new values (progress and value)
When I want to update the value, it changes the value in both objects. How can I change just one object.
Here is my deployment link.(its work in progress)
https://mastery-app.herokuapp.com/
This is my localStorage array:
skills[
{
"title": "Sewing",
"imageSrc": "images.unsplash.com",
"description": "Make your own clothes",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
},
{
"title": "Crocheting",
"imageSrc": "images.unsplash.com",
"description": "Interlock loops of yarn",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
}
]
This is how I update the localStorage:
const update = skills.map((skills) => {
skills.title === skills.title;
const updateProgress = skills.progress - value;
const rankNumber = parseInt(ranking);
const updateRank = skills.rank + rankNumber;
console.log(updateRank);
const updateValue = skills.value + value;
return {
title: skills.title,
rank: updateRank,
description: skills.description,
progress: updateProgress.toFixed(1),
imageSrc: skills.imageSrc,
category: skills.category,
isDone: false,
value: updateValue,
};
});
localStorage.setItem('skills', JSON.stringify(update));
You may consider using the find method to find the object you want to update. map is not the right function to be used for your use case.
Also skills.title === skills.title; has no effect at all (Maybe you wanted to use an if statement to do some kind of filtering by using title but that always would return true). Please remove that.
Now, I don't exactly know which field are you going to use to search for the object you want to update, but it has to be unique. If none of the fields in the objects are unique you should consider adding an unique id field in the skills objects. But if title is unique you can use the title to search. Then you can do something like the pseudo code below:
const toUpdate = skills.find(skill => skill.title === your_title_here)
toUpdate.field_to_update_1 = some_value_1
toUpdate.field_to_update_2 = some_value_2
localStorage.setItem('skills', JSON.stringify(skills))
Please also check the MDN docs to see how map, find and other array methods work and some of their use cases.

Youtrack, get list value some fields

I want to get a list of all the values that are now available in the field State, for example, that is, approximately so ['Open', 'Closed', 'Hold']
I try
var arrSprint = issue.fields['State'];
console.log(arrSprint);
But this command outputs to me characteristics this fileds
"foregroundColor": "#444",
"presentation": "Open",
"colorIndex": "0",
"description": "null",
"name": "Open",
Possible values can be retrieved from the respective ProjectCustomField. To get a ProjectCustomField, either call issue.project.findFieldByName('State'), or better add a requirement like
State: {
type: entities.State.fieldType
}
which will allow you to access the ProjectCustomField via ctx.State.

Populating kendo-tree using from Json

I am in Angular environment using Kendo. All I want to do is following:
Take Json
Produce Kendo tree using it
I have tried it with simple data and it seems to work fine. But this time I have somewhat complex data and it seems like it does not work well with complex Json. I have been trying to have it render Json but it seems like it keeps on thinking and never comes back. I have created a sample Dojo for reference:
http://dojo.telerik.com/EdOqE
I am not sure what am I doing wrong but it just does not seem to work. Can anyone help me with this please?
I presume you have controll over the resultant json, because you'll have to change it a little to fit the TreeView's expected format. Check this out:
{
"items": [{ // Projects
"Id": 0,
"Name": "Your Example Project",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Analyses
"Id": 0,
"Name": "1.0 - Your Example Run",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Samples
"Id": 0,
"Name": "Sample 1",
"hasChildren": false,
"Description": "ample frample sample"
}, {
"Id": 0,
"Name": "Sample 2",
"hasChildren": false,
"Description": null
}]
}]
}]
};
The above json is what I did to work in the widget. First of all, the collection properties were renamed to items. All of them, in all levels. With that, kendo will know how property it should deal with. A hasChildren property was added to let it know when it has to show the expand icon. Otherwise it will show the expand option even if the item doesn't haves any children. So user clicks it and get an empty result.
This is the widget initialization options:
{
dataSource: new kendo.data.HierarchicalDataSource({
data: things,
schema: {
data: "items"
}
}),
dataTextField: "Name"
};
With schema.data I tell which property kendo will deal as the collection item. The dataSource expects an array, but if you give him an object, you have to set this property. If it was an array, then kendo would look for item property of each child for default. dataTextField is the name of the property it will use as the label.
Demo
Here is another demo with the data as an array. No need to set schema.data.
Update:
I was afraid you would say that. Yes, there is a way to deal with the data if you can't change it in the server-side. You have to intercept the data at the schema.parse() method and change the resultant data object property to items, so then the widget will understand:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
Every node when opened will call parse with items collection as data parameter. You have to return a new object with the property name as items instead of Projects, Analysis or Samples.
I forgot you can't touch the data, so can't add hasChildren property as well. Then you have to add a tiny logic into parse to set those properties in each level, otherwise the expand icon would not appear:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
data.Projects.forEach(p => {
p.hasChildren = false;
if (p.hasOwnProperty("Analyses")) {
p.hasChildren = true;
}
});
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
data.Analyses.forEach(a => {
a.hasChildren = false;
if (a.hasOwnProperty("Samples")) {
a.hasChildren = true;
}
});
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
It is ugly, I know. But get used to Kendo, it is the it goes with it.

Couchbase view custom reduce

i'm not sure if the title of the questions fits, if you know a better one, let me know ;)
I just named it like this, because i'm thinking if i could solve my problem with a custom reduce function.
I have two types of objects:
Vehicles:
{
"id": "1G1JC5444R7252367",
"type": "Vehicle"
}
Users:
{
"company": "companyname",
"type": "User",
"parts": [
{
"company": "companyname",
"id": "1G1JC5444R7252367",
"active": true
},
{
"company": "companyname",
"id": "1G1135644R7252367",
"active": false
}
]
}
What i want is a View which returns me all vehicles of a certain company. But the company is only stored in the User object.
This is how far I got in the mapfunction:
function (doc, meta) {
if(doc.type == 'User'){
if(doc.parts){
Array.prototype.contains = function ( needle ) {
for (var i in this) {
if (this[i] == needle) return true;
}
return false;
};
var ids = new Array(doc.parts.length);
for(var k in doc.parts){
if(doc.parts[k].active) {
if(!vins.contains(doc.parts[k].id)) {
if (doc.parts[k].company && doc.parts[k].id ) {
ids.push(doc.parts[k].id);
emit(doc.parts[k].company, doc.parts[k].id);
}
}
}
}
}
}
}
But this only returns me the company as key and the id of the vehicle as value. So i get a User document. Can I somehow loop through the documents again in the map function and get all vehicles according to the ids in my ids array?
Saving the company in the vehicle itself also is not desired, because the company is not the vehicles company itself but the company of the parts.
Thanks for any help in forward.
A Couchbase view can only operate on the document presented to it. As you discovered, it can only partially do what you want.
The real problem isn't the view though but is your data model. You appear to have designed your data model as if you were using a relational database. The calculation you are attempting is a kind of join.
A fundamental concept with document databases is that a document should represent all of the information pertinent to some kind of event. This concept is what allows document databases to horizontally scale. You should not worry about data duplication. Locality of access is the key to an appropriate map-reduce data model.
I would redesign your data model.

AngularJS: How to create a model which holds an array for a dynamic list of input fields?

I have quite an interesting question (I hope) for all you AngularJS gurus out there. I am looking to create a dynamic list of form input fields based on a SELECT dropdown. As an example, we have a number of categories with each category having a set of specifications which are unique to that category. To help with the explanation we have the following:
Firstly, in the controller we start by initializing the models.
$scope.category = {};
$scope.category.specs = [];
Next we ready the data to be used in the form (actually retrieved from the server via $http). We also initialize a variable to the first element in the categories array.
$scope.categories = [
{ "id": "1", "name": "mobile", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Camera type" } ] },
{ "id": "2", "name": "laptop", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Graphics Card" } ] }
};
$scope.selectedCategory = $scope.categories[0];
In the form, we have a dropdown which when selected loads the appropriate input fields specific to that category. We use the ngRepeat directive to accomplish this. This is a dynamic list of fields based on $scope.categories.specs. (please note the ???)
<select ng-model="selectedCategory" ng-options="category.name for category in categories"></select>
<div ng-repeat="spec in selectedCategory.specs">
<label>{{spec.label}}</label>
<input type="text" ng-model="???">
</div>
Ultimately, when the user clicks the submit button, we would like to extract the category he/she has selected and then package it together with the specifications they have filled in. The post request should contain something like the following for instance (of course, I only included one spec item, but in reality there would be many):
{ "id": "1", specs [ { "id": "2", "details": "RADEON HD 8970M" } ] }
Unfortunately I am not really sure how to accomplish this. I need to somehow create an array for the spec model, and then ensure that both the ID and user entered data are appropriately extracted... what goes in the ??? and what do we do after? Any help would be much appreciated.
this is how I do it. I make a form, validate it with angular, and then when its valid I submit it with a function.
<form name="signup_form" novalidate ng-submit="signupForm()"></form>
$scope.signupForm = function() {
var data = $scope.signup;
$http({
method : 'POST',
url : 'http://yoursite.com/mail.php',
data : $.param(data), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
});
}
also if you want to look at another form validation system for angular check out http://nimbly.github.io/angular-formly/#!/ It may help you solve your current form system.
In the controller, initialize $scope.specDetails as follows:
$scope.specDetails = {};
angular.forEach($scope.categories, function (category, index1) {
$scope.specDetails[category.id] = {};
angular.forEach(category.specs, function (spec, index2) {
$scope.specDetails[category.id][spec.id] = '';
});
});
In the html, replace "???" with specDetails[selectedCategory.id][spec.id]

Categories