push object from an array to observableArray - javascript

so I'm trying to push the object stored in an array when a checkbox is called with knockout to an observable array.
HTML:
<input type="checkbox" data-bind="checked: checked, click: $root.saveSelected"/>
JS:
var definition = [
{title: 'some text', checked: ko.observable(false), definition: '<p>Some HTML</p>'}
],
var viewModel = {
selectedItems: ko.observableArray([]),
saveSelected: function() {
for (var i = 0; i < definition.length; ++i) {
if (viewModel.definition[i].checked().value === true) {
viewModel.selectedItems.push(definition[i]);
}
}
}
So I'm pretty sure that my if statement is what's causing the issue here, but I'm not sure what I did wrong. But the outcome should be that for every checkbox that is selected, that object (now with a value of true for 'checked') should get pushed to the selectedItems array so that (with this example) the blank selectedItems array should have the object
{title: 'some text', checked: ko.observable(false), definition: '<p>Some HTML</p>'}
in it after the saveSelection function runs.
--EDIT--
The fiddle for this code: http://jsfiddle.net/imagitron/mMc6k/6/

Remove the ".value" when accessing the checked observable:
if (viewModel.definition[i].checked() === true) {
...
Knockout manages setting the value of the HTML <input> control. All you need to know is that it's an observable with either true or false value.

First off, click: $root.saveSelected doesn't match saveSelection: function() {
Second issue
for (var i = 0; i < definition.length; ++i) {
if (viewModel.definition[i].checked() === true) {
definition and viewModel.definition are two seperate things here.
viewModel.definition is actually with the first save an empty array.
so, viewModel.definition[i] won't work ...

Related

angularjs filters with an OR operation

I have data array like this :
$scope.data = [{
name: 'joseph',
statarray: [{
status: 'Online',
status: 'Offline',
}],
active: 'yes'
},
{
name: 'arnold',
statarray: [{
status: 'Offline'
}],
active: 'no'
},
{
name: 'john',
statarray: [{
status: 'Online'
}],
active: 'yes'
}
];
$scope.findObjectByKey = function(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
};
$scope.Online = function(array){
var obj = $scope.findObjectByKey(array, 'status', 'Online');
return obj;
}
$scope.Offline = function(array){
var obj = $scope.findObjectByKey(array, 'status', 'Offline');
return obj;
}
The functions $scope.Online and $scope.Offline sorts the data according to the status Online and Offline.
Here's my view :
I have these two checkboxes as filters :
<input ng-true-value='Online' ng-false-value='' type="checkbox" ng-model="online" type="checkbox">Online
<input ng-true-value='Offline' ng-false-value='' type="checkbox" ng-model="offline" type="checkbox">Offline
<div ng-repeat="user in data|filter:online|filter:offline">
<p>{{user.name}}</p>
</div>
Currently when I click the checkbox corresponding to Online it displays the user joseph and john whose status is Online and when I click the checkbox corresponding to Offline it displays the users joseph and arnold whose status are Offline. This much is working perfectly. But when I click both the filter buttons it only displays joseph as joseph has both Online and Offline status. So an AND operation is being applied here. But I want an OR operation here. So when I click both the filter buttons I should get the output as joseph,arnold and john in the view. Any clue on how can I do it?
First, your statarray seems wrong, considering you declared one object with two properties with the same name, first we should move it to something like an array only containing the status strings ex. ['Online', 'Offline'].
You are executing the filter function only using the latest filter selected.
You need to think in a different approach to aggregate your selected filters,
something like create an filter obj.
filter = {
online: true,
offline: false
}
and iterate over then to display your data
$scope.filterArray = function(array, key, value) {
var filtered = [];
for (var i = 0; i < array.length; i++) {
var shouldInclude = false;
shouldInclude |= ($scope.filter.online && array[i].statarray.indexOf('Online') >= 0);
shouldInclude |= ($scope.filter.offline && array[i].statarray.indexOf('Offline') >= 0);
if (shouldInclude) {
filtered.push(array[i]);
}
}
return filtered;
};
This is just one possible approach, if you are able to use ES6 functions this become even simpler.
# pravin navle-
Are you sure its working same as you described below code? Because when I tried to replicated same functionality it works only for Offline and not for Online as well as Both Checked.

Find in JSON array by manually entered ID

in my angularjs app I get JSON object from API.
I have an input field, where I can input ID manually, and if an item with this ID exists, I need to change his property.
I also store this ID to sessionStorage, so when user refresh page, he get value like before refresh.
Here is json from API where I need to change ON THE FLY property SHOW to true if exist
$scope.tableParams.data = [
{id: 1015, type: "ssss", show: false},
{id: 1016, type: "ssss", show: false},
{id: 1017, type: "ssss", show: false},
{id: 1018, type: "ssss", show: false}
]
Function for getting input value and store to session storage, and also change SHOW property
$scope.getIndex = function (indexOfRow) { //indexOfRow is passed data from input field
//here I want to change show to true for passed id
sessionStorage.setItem("indexOfOpenedRow", JSON.stringify(indexOfRow));
}
I try answer from here but not working for me
I also try this, but allways get undefined
function findId(array, id) {
var i, found, obj;
for (i = 0; i < array.length; ++i) {
obj = array[i];
if (obj.id == id) {
console.log(obj);
return obj;
}
}
return undefined; // <= You might consider null or undefined here
}
$scope.getIndex = function (indexOfRow) { //indexOfRow is passed data from input field
//here I want to change show to true for passed id
angular.forEach($scope.tableParams.data, function(key, value){
var result = findId(key.id, indexOfRow);
console.log(result);
});
sessionStorage.setItem("indexOfOpenedRow", JSON.stringify(indexOfRow));
}
Filter is the function you are searching for. As i understood your question by reading your code you want to compare an seperate ID with the ID in the JSON? If I am wrong commend here and I will edit the answer:
function findIDAndSetShowingTrue(array, id) {
return array.filter(curr=> curr.id === id).map(curr => ({...curr, show: true}));
}
The filter function iterates over every child inside the array and gives you the specific value (array.filter).
Then you can use this child and compare it like an if statement with some other values (current).
In the end you compare current.id and your input id (current.id === id) and return the current object if it's true.

Understanding the role of a variable accessing an array item/s using [ ]. (WatchAndCode related)

I am currently working my way through a WatchAndCode course.
We are building a reminders/todo-style JavaScript app.
I am struggling to understand how the [i] in the this.todos[i].todoText section of the code works. The code does not function properly without it, so it is needed.
Please can somebody explain its role? I understand you can target a specific item in an array using brackets, but I am still confused how i works exactly.
Any help would be so appreciated. If possible, can there be no alterations to the code. I am a beginner so I'm taking it step-by-step, I understand there are probably more efficient ways to go about writing the code.
Here is the full code below:
var todoList = {
todos: [],
displayTodos: function() {
if (this.todos.length === 0) {
console.log('Your todo list is empty!');
} else {
console.log('My todos:');
for (var i = 0; i < this.todos.length; i++) {
console.log(this.todos[i].todoText);
}
}
},
addTodo: function(todoText) {
this.todos.push({
todoText: todoText,
completed: false
});
this.displayTodos();
},
changeTodo: function(position, todoText) {
this.todos[position].todoText = todoText;
this.displayTodos();
},
deleteTodo: function(position) {
this.todos.splice(position, 1);
this.displayTodos();
},
toggleCompleted: function(position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
this.displayTodos();
}
};
Thanks again
Basically the array consists of multiple objects, to access those objects you need to access their position in the array. The "i" in the for loop is just a variable that is going through numbers 0 to the length of the array. So the "i" will have the value 0 at start and todos[i] will actually access todos[0] (the first element of the array) and add the text from todoText to that element in the array, then the "i" becomes 1 because the for loop adds i++, it could have been written e.g i+2 if you need every 2nd element, etc.
Loops are used so you don't have to do that manually, it might be easier for you to understand if it was written manually at first :
this.todos[0].todoText
this.todos[1].todoText
this.todos[2].todoText
this.todos[3].todoText
this.todos[4].todoText
this.todos[5].todoText
this.todos[6].todoText
That's the code that happens in the loop basically so you don't have to write it all by yourself, accessing every object of the array and doing something with it. (in my example the array has 7 elements)
Hope this clarify's it a bit for you.
You can access a property's value via:
object.an_indentifier_that_matches_the_name
or
object["an_expression_that_evaluates_as_a_string_that_matches_the_name"]
Arrays are just a type of object where property names that are integers (like "1") are given special meaning.
You can use any expression (a string literal, a variable, a function call, etc) between the square brackets, so long as you get something which can be converted to a string at the end of it.
var foo = ["a", "b", "c"];
var i = 2;
console.log(foo[2]);
console.log(foo["2"]);
console.log(foo[4 / 2]);
console.log(foo[i]);
You have i in the loop:
for (var i = 0; i < this.todos.length; i++) {
console.log(this.todos[i].todoText);
}
So you have to know how does loops works. First, you declare a variable i at the first part of the loop: var i = 0;. Then, in the middle, you have a condition, it will be checked in every loop iteration. If it evaluates to true, current iteration will be executed (this code will be executed: console.log(this.todos[i].todoText);). And finally, after one iteration, variable i will be increased by 1 (it happens here: i++).
So i is just a counter, it starts from 0 and increasing by 1 after every iteration. And in first iteration the actual console.log in the body of loop will be console.log(this.todos[0].todoText);, in the second it will be console.log(this.todos[1].todoText);, and so on, while this condition: i < this.todos.length; evaluates to true.
When you're doing todos[0], it gets the first element from todos array.
For more info about for loops: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for
Firstly you should read up on arrays here: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Arrays
Lets assume this.todos equals this array:
[
{todoText: 'Foo', completed: false},
{todoText: 'Bar', completed: false},
]
If you view this like:
[
0: {todoText: 'Foo', completed: false},
1: {todoText: 'Bar', completed: false},
]
The number is the index for that entry in the array. Run snippet below to see output:
let todos = [
{todoText: 'Foo', completed: false},
{todoText: 'Bar', completed: false},
];
console.log('if `i` equals 0 then the output of `todos[i]` is:')
var i = 0;
console.log(todos[i]);
console.log('if you want to get the `todoText` value for this entry then the value of `todos[i].todoText` is:')
console.log(todos[i].todoText);
Let's suppose we have this array:
this.todos = [
/* Index "0" */ { todoText: 'my-text', otherProperty: 'blabla', },
/* Index "1" */ { todoText: 'another-text', otherProperty: 'blabla', },
/* Index "2" */ { todoText: 'last-text', otherProperty: 'blabla', },
];
// The line "this.todos[0].todoText" will show "my-text"
// The line "this.todos[2].todoText" will show "last-text"
In order to display the content of you todo-list, you have to pass through all of your todo elements, so you will make a loop through all of them with the for statement:
// Pass through your todo list.
// We began to the first element (with index "0"), the current step is stocked into "i" (var i = 0)
// Until we reach the last one (i < this.todos.length)
// When a step is over, we increment it of one (i++, or i += 1)
for (var i = 0 ; i < this.todos.length ; i++)
For each one, you write its content:
console.log(this.todos[i].todoText);
You precised that your first element in the index "0", so it's like you remplaced "i" by "0":
console.log(this.todos[0].todoText); // Remember, "this.todos[0].todoText" show "my-text".
And so on, until you loop through your last element:
console.log(this.todos[1].todoText); // Remember, "this.todos[1].todoText" show "another-text".
console.log(this.todos[2].todoText); // Remember, "this.todos[2].todoText" show "last-text".
Your loop is executed as long as i < this.todos.length is true. It ends after that because this.todos[3] does not exist and return false.

Check Checkboxes from an array of objects on load

i have a model like this
function ViewModel(){
var self = this
self.Choices = ko.observableArray([])
self.AcceptedChoices = ko.observableArray([])
self.LoadData = function(){
self.ViewAnswered()
}
self.ViewAnswered = function(){
var url = 'QuestionsApi/ViewAnswered'
var type = 'GET'
ajax(url , null , self.OnViewAnsweredComplete, type )
}
self.OnViewAnsweredComplete = function(data){
var currentAnswer = data.Answer
self.Choices(currentAnswer.Choices)
self.AcceptedChoices(currentAnswer.AcceptedChoices)
}
self.LoadData()
}
Here is my object. I have removed extra things
{
"AcceptedChoices": [94, 95],
"Choices": [{
"ChoiceId": 93,
"ChoiceText": "Never"
}, {
"ChoiceId": 94,
"ChoiceText": "Sometimes"
}, {
"ChoiceId": 95,
"ChoiceText": "Always"
}]
}
And here is binding
<u data-bind="foreach:Choices">
<li>
<input type="checkbox" name="choice[]" data-bind="value:ChoiceId,checked:$root.AcceptedChoices">
<span data-bind="text:ChoiceText">Never</span>
</li>
</u>
Now the problem is that checkboxes are not being checked due to the choices being array of objects. How can i resolve this issue? Although the same thing works for radio where there is only one selection.
Never mind i have found a solution here
checked binding does not properly compare primatives
Also it tells two ways for this. The Solution provided in fiddle is creepy so i will use the one using knockout version 3.0.0.
All i need to do is attach knockout-3.0.0.js instead of any other and then use checkedValue instead of value.
<input type="checkbox" name="choice[]"
data-bind="
checkedValue:ChoiceId,
checked:$root.AcceptedChoices"
>
And that's done. Hope it helps someone.
EDITS :
I noticed it is not working on the Chrome. So i found an alternative. I created these two functions.
self.ConvertToString = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(item.toString())
})
return AcceptedChoices
}
self.ConvertToInteger = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(parseInt(item))
})
return AcceptedChoices
}
And use them
self.AcceptedChoices(self.ConvertToString(currentAnswer.AcceptedChoices))
To get the value
AcceptedChoices: self.ConvertToInteger(self.AcceptedChoices()),
You need to be checking to see if the Id of a choice is in the AcceptedChoices array. Use the ko.utils array function to help do that:
checked: function() { return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == ChoiceId();
} !== null }
You could put this into a function on your root object:
self.isChoiceAccepted = function(choiceId){
return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == choiceId;
} !== null
};
then call it in your data-bind as:
checked: function() { return $root.isChoiceAccepted(ChoiceId()); }
This isn't tested, I'm not 100% sure that the arrayFirst method returns null if it doesn't find a matching item in the array, so chack that.

extjs vtype: check value using another value in the same row

I have an Ext.grid.Panel and i want to validate user input: there is a field named delete and a field named string. Valid input for delete is a number, which can't be greater than the length of a string field in the same row. I already know how to use vtype so now i have
delete : function(val, field){
var expr = new RegExp("^[-]?[0-9]*[\.]?[0-9]*$");
var num = expr.test(val);
if (!num) return false; //can't be not a number
else{
//have no idea...
}
}
I have no idea how to access string value for the same row.
Hope it's quite clear. Thanks!
It's actually much simpler and cleaner if you define your editor as a variable, you can just refer to it in the fields validator method and get the active record, for example:
// create editor as a variable
var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
autoCancel: false
});
// referring to the edited record in the validator function
{
xtype: 'datecolumn',
header: 'Comp Date',
dataIndex: 'comp_date',
width: 100,
format: 'j-M-Y',
editor: {
xtype: 'datefield',
format: 'j-M-Y',
validator: function(value) {
var record = cellEditing.getActiveRecord(); // get active record
if (Ext.Date.parse(value, 'j-M-Y') < record.get('start_date')) {
return 'Cannot complete before start date';
} else return true;
}
}
}
You should handle validateedit( Ext.grid.plugin.Editing editor, Object e, Object eOpts ) event.
The second argument, e, contains reference to the record that is edited (e.record). You could use this record to get both fields(string and delete) and perform validation accordingly.
Assigning false to e.cancel will cancel the editing.
well, there's a workaround, but it's ugly:
rowEditing.on({
scope : this,
afteredit : function(roweditor, changes, record, rowIndex) {
var can_save = true;
var records = store.getRange();
for(var i = 0; i < records.length; i++) {
if(records[i].data['delete'] >= records[i].data['string '].toString().length) {
Ext.MessageBox.alert('Input error', "blahblah");
}
I accessed the plugin the following way:
this.ownerCt.ownerCmp.plugins
That gives you access to the cellEditing plugin and from there you could do a:
var record = plugin.getActiveRecord();

Categories