How do i update observable array in knockout binding handler? - javascript

I want to update the observable array in knockout binding handler. But it is not updating. The following code i tried but nothing worked out.
this.DropdownValues = ko.observableArray([
{ id: 0, type: "Arc",Checked:false },
{ id: 1, type: "Eve",Checked:false },
{ id: 2, type: "Ca",Checked:false },
{ id: 3, type: "test",Checked:false },
]);
Code I have written inside binding handler.
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
console.log("true");
valueUnwrapped.map(function(item){
item[Checked]= true; return item;
});
ko.utils.unwrapObservable(value(valueUnwrapped));
But my view still not detecting the values. foreach not refeshing in view.

You were missing quotes around the word Checked. It should be item['Checked'] or item.Checked rather than item[Checked].
ko.bindingHandlers.updateArray = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
$("#before").text(JSON.stringify(valueUnwrapped));
console.log("true");
valueUnwrapped.map(function(item){
item['Checked']= true; return item;
});
ko.utils.unwrapObservable(value(valueUnwrapped));
}
}
var viewModel = function(){
var self = this;
self.DropdownValues = ko.observableArray([
{ id: 0, type: "Arc",Checked:false },
{ id: 1, type: "Eve",Checked:false },
{ id: 2, type: "Ca",Checked:false },
{ id: 3, type: "test",Checked:false },
]);
};
ko.applyBindings(new viewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Before:<br>
<span id="before"></span>
<div data-bind="updateArray: DropdownValues">
</div>
<br><br>
After:<br>
<span data-bind="text: ko.toJSON(DropdownValues)"></span>

Related

Knockout ko.mappings.fromJS not working

I am trying to use knockout mapping, but it isn't working as I expected.
Here I created simpliest fiddle i can and it's not working.
Am I missing something?
https://jsfiddle.net/p48d11j5/1/
function Model(){
var self = this;
self.Id = ko.observable(0);
self.Name = ko.observable("Default");
self.Visible = ko.observable(false);
self.Items = ko.observableArray([]);
}
function ModelItem(){
var self = this;
self.Id = ko.observable(0);
self.Name = ko.observable("Default item name")
}
var m = new Model();
ko.mapping.fromJS({
Id:1,
Name: "Test",
Visible: true,
Items: [
{
Id:1,
Name:"First"
},
{
Id:2,
Name:"Second"
}
]
}, m);
ko.applyBindings(m);
edit: I am working with nested arrays, so I added array
edit2: I want have models "typed" to use functions or ko.computed properties of them
If you call ko.mapping.fromJS with two arguments : ko.mapping.fromJS(data, mappedObject) the second argument is a mappedObject which is already created.Then the second argument will be taken as a viewModel not options.
All you have to do is: ko.mapping.fromJS(data, {}, viewModel) - this one puts your data in your model.
ko.mapping.fromJS({
Id:1,
Name: "Test",
Visible: true,
Items: [{Id: 1, Name: "First"}, {Id: 2, Name: "Second"}]
}, {} ,m); // pass the second argument as an empty object.
Try this
var m = ko.mapping.fromJS({
Id:1,
Name: "Test",
Visible: true,
Items: [
{
Id:1,
Name:"First"
},
{
Id:2,
Name:"Second"
}
]
}, new Model());
ko.applyBindings(m);
Working Example: https://jsfiddle.net/p48d11j5/2/
You can give something like this a try, using the mapping plugin to set up your default state as well as to apply updates:
// Set up the initial model.
var model = ko.mapping.fromJS({
Id: 0,
Name: "Default",
Visible: false,
Items: []
});
ko.applyBindings(model);
// Map new data from the "server"...
var model = ko.mapping.fromJS({
Id:1,
Name: "Test",
Visible: true,
Items: [
{
Id:1,
Name:"First"
},
{
Id:2,
Name:"Second"
}
]
}, model);
// ...or directly manipulate the model.
model.Id(2);
model.Items.push({Id: 3, Name: "Third"});
https://jsfiddle.net/3evtx022/

My viewmodel does not reflect my selections

I'm using cascading dowpdowns in a list.
The user interface is working fine, but the underlying viewmodel is not up to date with the user selections.
I have the following html :
<ul data-bind="foreach: selectedExams">
<li>
<select data-bind="options: $parent.availableExams, optionsCaption: 'Choisir un type...', optionsText: 'examTypeName', value: examtype"></select>
<select data-bind="options: exams, optionsCaption: 'Choisir un examen...' , optionsText: 'examName',value: exam, enable:exams().length"></select>
Remove
</li>
</ul>
<button data-bind="click: add">Add</button>
<pre data-bind="text: ko.toJSON($root.selectedExams, null, 2)"></pre>
js file:
function AppViewModel() {
var self = this;
self.availableExams = [
{
examTypeId: "SCAN", examTypeName: "Scanner", exams: [
{ examId: "SCOEUR", examName: "SCOEUR" },
{ examId: "SANGIO", examName: "SANGIO abdominopelvien" },
{ examId: "SSINUS", examName: "SSINUS sans inj" }
]
},
{
examTypeId: "RX", examTypeName: "Radio", exams: [
{ examId: "RBRAS", examName: "RBRAS" },
{ examId: "RAVBRAS", examName: "RAVBRAS" },
{ examId: "RBASSIN", examName: "RBASSIN 1 inc + rx bilat COXO FEMO 1/2 inc" }
]
},
{
examTypeId: "IRM", examTypeName: "IRM", exams: [
{ examId: "ITETE", examTypeId: "IRM", examName: "ITETE angio IRM enceph" },
{ examId: "IRACHIS", examTypeId: "IRM", examName: "IRACHIS 1/2 segt avec INJ" },
{ examId: "ITHORAX", examTypeId: "IRM", examName: "ITHORAX sans inj" }
]
}
];
self.selectedExams = ko.observableArray([new selectedExam()]);
self.add = function () {
self.selectedExams.push(new selectedExam());
};
self.remove = function (exam) { self.selectedExams.remove(exam) }
}
var selectedExam = function () {
self.examtype = ko.observable(undefined);
self.exam = ko.observable(undefined);
self.exams = ko.computed(function () {
if (self.examtype() == undefined || self.examtype().exams == undefined)
return [];
return self.examtype().exams;
});
}
ko.applyBindings(new AppViewModel());
The result, for 3 lines of various selections is the following :
[
{},
{},
{}
]
I'm expecting to see this for instance :
[
{"SCAN","SCOEUR"},
{"RX", "RBRAS"},
{"IRM", "ITETE"}
]
This is probably a data binding issue, but I don't know where to start to debug this kind of problem.
Please note that I'm using this code in a bootstrap grid.
Any Help appreciated.
Thanks in advance.
The problem is that you're missing the definition of self in your selectedExam constructor function. Currently, your self is actually referencing window (the global context) hence you're ending-up with empty objects being returned.
Try this:
var selectedExam = function () {
var self = this; // <-- add this
self.examtype = ko.observable(undefined);
self.exam = ko.observable(undefined);
self.exams = ko.computed(function () {
if (self.examtype() == undefined || self.examtype().exams == undefined)
return [];
return self.examtype().exams;
});
}

VueJs How to duplicate object from v-repeat?

Functionality allows you to add/delete description, title and time for the event.
I can not deal with the duplication(cloning) of the object which is created through v-model = (event.name, event.description and event.date)
All works fine with the removing selected object, it works like this:
deleteEvent: function(index){
if(confirm('Are you sure?')) {
this.events.$remove(index);
}
}
Here's an example of my code for a application to adding and changing events.
var vm = new Vue({
el: '#events',
data:{
event: { name:'', description:'', date:'' },
events: []
},
ready: function(){
this.fetchEvents();
},
methods: {
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
this.$set('events', events);
},
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent($index)"
deleteEvent: function(index){
if(confirm('Вы точно хотите удалить данную запись?')) {
this.events.$%remove(index);
}
},
cloneItem: function(index) {
}
}
});
there full code
http://codepen.io/Monocle/pen/ojLYGx
I found undocumented built it extend function Vue.util.extend that is equivalent to jQuery's extend.
In this case, you can avoid the enumerating the object properties
cloneItem: function(index) {
this.events.push(Vue.util.extend({},this.events[index]));
}
Access the cloned object via this.events[index], and then use it's properties to create new object and push it into events array:
cloneItem: function(index) {
this.events.push({ name: this.events[index].name,
description: this.events[index].description,
date: this.events[index].date });
}
Demo: http://codepen.io/anon/pen/MajKZO

ExtJs 5 Show data in grid from two stores

Is it possible to show data in table from two stores (merge them) without creating third store?
Example:
var store1 = {
data: [{
name: 'Joe'
}, {
name: 'Jane'
}, {
name: 'Kate'
}]
};
var store2 = {
data: [{
name: 'John'
}, {
name: 'Richard Roe'
}]
};
var grid = {
store: [store1, store2]
}
If both stores' models are same, why don't you merge stores' data ? then load merged data into store1. Like that: https://fiddle.sencha.com/#fiddle/tjh
var mergedData = Ext.Array.union(store1.getRange(),(store2.getRange());
store1.loadData(mergedData);
grid.setStore(store1);
// to provide unique
store1.on('datachanged', function(store) {
var checkArray = [];
Ext.each(store.getRange(), function(record) {
var userName = record.get('name');
if (checkArray.indexOf(userName) > -1) {
store.remove(record);
}
checkArray.push(userName);
});
});

Dynamically update and check an array of Objects

So I have a callback function that returns a data object from the dom (there is a list of items and every time you select an item it returns it as an object). Here is the call back function:
$scope.fClick = function( data ) {
$scope.x = data;
}
The object returned from fClick looks like this when you select an item from the dropdown : { name: "English", ticked: true }
When you deselect it from the dropdown it would return something like this:
{ name: "English", ticked: false }
Now I keep an array something like $scope.output to maintain a list of the returned objects. However, what I want to do is add an object returned from scope.fClick to $scope.output only if there isn't an object in output already with the same property "name" value. So right now in my implementation both { name: "English", ticked: false } and { name: "English", ticked: true } get added to the array. Instead I want it to update the ticked property. So, for instance if if $scope.output = { name: "English", ticked: false } and then scope.fClick returns { name: "English", ticked: true}. When I push this value to $scope.output I want it to the update the existing object's tick property so $scope.output = { name: "English", ticked: false } becomes $scope.output = { name: "English", ticked: true }
This is what I have tried:
$scope.fClick = function( data ) {
$scope.x = data;
}
$scope.$watch(function () {
return $scope.y = $scope.x;
},function (newValue, oldValue) {
var id = $scope.output.indexOf(newValue);
if(id === -1){
$scope.output.push(newValue);
}
else {
$scope.output[id].tick = newValue.tick;
}
console.log($scope.output);
},true);
How do I make this work?
Set & Get selected values, name and text of Angularjs isteven-multi-select
<div isteven-multi-select
input-model="marks"
output-model="filters.marks"
button-label="name"
item-label="name"
tick-property="ticked"
selection-mode="multiple"
helper-elements="all none filter"
on-item-click="fClick( data )"
default-label="Select marks"
max-labels="1"
max-height="250px">
</div>
Add items
$scope.marks= [
{ name: 'Mark I', value: 'Mark i', text: 'This is Mark 1', ticked: true },
{ name: 'Mark II', value: 'Mark ii', text: 'This is Mark 2' },
{ name: 'Mark III', value: 'Mark iii', text: 'This is Mark 3' }
];
Get selected item (on change)
$scope.fClick = function (data) {
console.log(data.name);
console.log(data.value);
console.log(data.text);
return;
}
Select item (Dynamically)
$scope.abc = function (data) {
console.log(data.element1, data.element2);
angular.forEach($scope.marks, function (item) {
if (item.value == data.element1) {
item.ticked = true;
}
else {
item.ticked = false;
}
});
}
Deselect item
$scope.marks.map(function (item) {
if (item.value == "")
item.ticked = true;
else
item.ticked = false
});
You can do the following by "simulate" a key/value map.
Controller
(function(){
function Controller($scope) {
$scope.data = [
{name: 'English', ticked: true},
{name: 'French', ticked: false}
];
//Represent a map key - value
$scope.output = {};
$scope.update = function(index){
//Change the ticked value by opposite
$scope.data[index].ticked = !$scope.data[index].ticked;
//Set the value to our map
$scope.output[index] = $scope.data[index].ticked;
}
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Here, when you will update $scope.output, if the key exist, it will erase it by the new value, instead of it will create a new key/value pair.
HTML
<body ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in data">{{item.name}} {{item.ticked}} <button type="button" ng-click="update($index)">update</button></li>
</ul>
</body>

Categories

Resources