KnockoutJS filtering an array - javascript

I have an observable array that contains a list of object that I want to filter through based on a user input. If the user searches a word that appears in the array in two different places then the filter function should return the title of both objects and delete or hide all other objects in the array that did not match the input from the user. I must use knockout js to preform this feature which is still new to me. Currently my filter function checks to see if the user input is included in a title of one of the objects within the array and if it is not then it removes the object. However, this not providing me what I need as it does not accurately filter the list.
My ViewMode
var viewModel = function() {
var self = this;
self.filter = ko.observable('');
self.locationList = ko.observableArray(model);
self.filterList = function(){
return ko.utils.arrayFilter(self.locationList(), function(location) {
if(location.title == self.filter()){
return location.title
}
else if( location.title.includes(self.filter()) ){
return location.title
}
else{
return self.locationList.remove(location)
}
});
};
}
The View
<section class="col-lg-2 sidenav">
<div class="row">
<div class="col-lg-12">
<div class="input-group">
<input data-bind="textInput: filter"
type="text" class="form-control" placeholder="Filter Places"
aria-describedby="basic-addon2" id="test">
<button data-bind="click: filterList id="basic-addon2">
<i class="glyphicon glyphicon-filter"></i>
Filter
</button>
</div>
</div>
<div class="col-lg-12">
<hr>
<div data-bind="foreach: locationList">
<p data-bind="text: $data.title"></p>
</div>
</div>
</div>
</section>

The answer to the question can be found here answered by Viraj Bhosale
ViewModel
var viewModel = function() {
var self = this;
self.filter = ko.observable('');
self.locationList = ko.observableArray(model);
self.filterList = ko.computed(function(){
return self.locationList().filter(
function(location){
return (self.filter().length == 0 || location.title.toLowerCase().includes(self.filter().toLowerCase()));
}
);
});
}
View
<main class="container-fluid">
<div class="row">
<section class="col-lg-2 sidenav">
<div class="row">
<div class="col-lg-12">
<div class="input-group">
<input data-bind="textInput: filter, valueUpdate: 'keyup'"
type="text" class="form-control" placeholder="Filter Places"
aria-describedby="basic-addon2" id="test">
</div>
</div>
<div class="col-lg-12">
<hr>
<div data-bind="foreach: filterList">
<p data-bind="text: $data.title"></p>
</div>
</div>
</div>
</section>
<section class="col-lg-10" id="map"></section>
</div>

Related

How to resolve an error in To Do List Web App

Currently experimenting with JavaScript and building a To-Do List Web App. Having some issues with it. When I refresh the page, it gives the error:
TypeError: tasks is null
And when I press the submit button, it gives the error:
TypeError: cyclic object value
I was trying it out after reading a tutorial online on JavaScript and then I got the idea on implementing it with some basic project like this one.
How can I resolve the errors? I can't seem to figure out the solution.
JS Code:
document.getElementById('form-Task').addEventListener('submit', saveTask);
// Save new To-Do Item
function saveTask(e){
let title = document.getElementById('title').value;
let description = document.getElementById('description').value;
let task = {
title,
description
};
if(localStorage.getItem('tasks') === null) {
let = tasks = [];
tasks.push(tasks);
localStorage.setItem('tasks', JSON.stringify(tasks));
} else {
let tasks = JSON.parse(localStorage.getItem('tasks'));
tasks.push(task);
localStorage.setItem('tasks', JSON.stringify(tasks));
}
getTasks();
// Reset form-Task
document.getElementById('form-Task').reset();
e.preventDefault();
}
// Delete To-Do Item
function deleteTask(title) {
let tasks = JSON.parse(localStorage.getItem('tasks'));
for (let i = 0; i < tasks.length; i++){
if(tasks[i].title == title) {
tasks.splice(i, 1);
}
}
localStorage.setItem('tasks', JSON.stringify(tasks));
getTasks();
}
// Show To-Do List
function getTasks(){
let tasks = JSON.parse(localStorage.getItem('tasks'));
let tasksView = document.getElementById('tasks');
tasksView.innerHTML = '';
for(let i = 0; i < tasks.length; i++){
let title = tasks[i].title;
let description = tasks[i].description;
tasksView.innerHTML +=
`<div class="card mb-3">
<div class="card-body">
<div class="row">
<div class="col-sm-3 text-left">
<p>${title}</p>
</div>
<div class="col-sm-7 text-left">
<p>${description}</p>
</div>
<div class="col-sm-2 text-right">
X
</div>
</div>
</div>
</div>`;
}
}
getTasks();
HTML code:
<nav class="navbar navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">My To-Do List</a>
</div>
</nav>
<div class="container">
<div class="row my-5">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<form id="form-Task">
<div class="form-group">
<input type="text" id="title" class="form-control" maxlength="50" autocomplete="off" placeholder="Title" required>
</div>
<div class="form-group">
<textarea type="text" id="description" cols="30" rows="10" class="form-control" maxlength="500" autocomplete="off" placeholder="Description" required></textarea>
</div>
<button type="submit" class="btn btn-success btn-block">Save</button>
</form>
</div>
</div>
</div>
<div class="col-md-8">
<div class="row">
<div class="col-sm-3 text-left">
<p class="font-weight-bold">Title</p>
</div>
<div class="col-sm-6 text-left">
<p class="font-weight-bold">Description</p>
</div>
<div class="col-sm-3 text-right">
<p class="font-weight-bold">Delete</p>
</div>
</div>
<hr>
<div id="tasks"></div>
</div>
</div>
</div>
<script src="js/app.js"></script>
I am still learning javascript so I might have missed something, but in your first if statement, you have an extra "=":
if(localStorage.getItem('tasks') === null) {
let = tasks = []; // let tasks = []
tasks.push(tasks);
and I am not sure about that first definition of the task variable. With curly braces, you make key-value pairs, but you didn't add the value. For example:
var person = { firstName: "John", lastName: "Doe" };
Hope I helped.

Arrays & JSON: Jquery $.each() only getting the last loops

Here's what I need to do.
I need to create an array of array of objects like the format below. I am getting name and values of input boxes and put then in a json then push it in an array and then I push the generated array into another array.
[
[
{"key1":"value1","key2":"value2"},
{"key1":"value3","key2":"value4"},
],
[
{"key1":"value1","key2":"value2"},
{"key1":"value1","key2":"value2"},
]
]`
below is my code
$scope.saveImages = function(){
var arrayOfArrays =[];
var arrayOfPhotos = [];
angular.element('.photo-group').each(function(){
var object = {};
$(this).find('.photo').each(function(){
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
});
arrayOfPhotos.push(object)
arrayOfArrays.push(arrayOfPhotos)
console.log(arrayOfArrays)
});
}
and this is my markup
<div class="photo-group">
<div class="photo-group-body">
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="photo-group">
<div class="photo-group-body">
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
</div>
</div>
In my markup I have two .photo-group classes.
on the first .photo-group class I have four .photo class
and on the second .photo-group class I have two .photo class
so my array should look like this
[
[
{"photo_label":"value1", "image_data":"value2"},
{"photo_label":"value3", "image_data":"value4"},
],
[
{"photo_label":"value4", "image_data":"value5"},
]
]`
but instead, what I get is only the last objects in each array
[
[
{"photo_label":"value3", "image_data":"value4"},
],
[
{"photo_label":"value4", "image_data":"value5"},
]
]`
creat the pushing object inside foreach
angular.element('.photo-group').each(function(){
$(this).find('.photo').each(function(){
var object = {};
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
arrayOfPhotos.push(object);
});
arrayOfArrays.push(arrayOfPhotos);
console.log(arrayOfArrays);
});
So after hours of trial and error testing and tracing the flow of the code.. I was able to get what I want.
Here's what I did
$scope.saveImages = function(){
var arrayOfArrays =[];
angular.element('.photo-group').each(function(){
var arrayOfPhotos = [];
$(this).find('.initial-photo').each(function(){
var object = {};
$(this).find('.photo').each(function(){
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
});
arrayOfPhotos.push(object)
});
arrayOfArrays.push(arrayOfPhotos);
});
}
I added another loop to go deep into the child elements and place the var arrayOfPhotos = []; in the first instance of the loop

Get info from dynamic/growing form

Have a form to create a contract, where that contract can have one or more users associated.
The area to input the users info, starts with only one field of one user, and one button to add more fields if needed.
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
</div>
This is the starting div
Clicking on Add User button it adds a new div under the "utilizador1"
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
<div id="utilizador2" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador2" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador2" placeholder="Função">
</div>
</div>
</div>
My question is, how can I get the number of users created, and insert them into a list using Javascript. The list will be a attribute of a Object (Contract).
What i have til now:
function test_saveItem() {
var contract = new Object();
contract.Dono = <% =uID %>;
contract.BoostMes = $("#boostMes").val();
contract.BoostAno = $("#boostAno").val();
var ListaUtilizadores = [];
var divs = document.getElementsByName("utilizador");
for (var i = 0; i < divs.length; i++){
var user = new Object();
user.Nome = $('#nomeUtilizador' + i).val();
ListaUtilizadores.push(user);
}
var test = JSON.stringify({ "contract": contract });
}
Any help appreciated
Edit: Got to a solution thanks to Shilly
List = [];
Array.prototype.slice.call(document.querySelectorAll('.user')).forEach(function (node, index) {
List.push({
"name" : document.getElementById('nameUser' + (index + 1)).value,
"job" : document.getElementById('jobUser' + (index + 1)).value
});
});
Something like this? But adding it into the addUser function as Super Hirnet says, will be more performant.
var divs = document.querySelector('#utilizadores').childNodes,
users = [];
Array.slice.call(divs).forEach(function (node, index) {
users.push({
"name" : divs[index].getElementById('nomeUtilizador' + (index + 1)).value
});
});
You can have an empty array and on every click of addUser put a new object into the array. The object can have information related to the added user.

Javascript Remove a Template Element

I have a template that I will simplify as follows:
<script type="text/template" id="contactTemplate">
<div id="contactentry" class="row contact-info">
</div>
<div class="form-group">
<label for="name1">First Name</label>
<input type="text" class="form-control" id="FirstName" name="Contacts[{{ID}}].FirstName" required>
</div>
<div class="form-group minus">
<label for="contactMinus{{ID}}"> </label>
<a id="depminus{{ID}}"><i class="fa fa-minus-circle"></i>Remove</a>
</div>
</script>
I allow insertion of the template, multiple times via the following code, this part works:
<script type="text/javascript">
var clone = (function () {
var cloneIndex = -1;
var template = $('#contactTemplate').text();
return function () {
//Replace all instances of {{ID}} in our template with the cloneIndex.
return template.replace(/{{ID}}/g, ++cloneIndex);
}
})();//self executing function.
var contacts = $('#contacts')
$("#contactadd").on("click", function () {
contacts.append(clone());
window.controlManager.FindControls(contacts);
});
</script>
<div id='contacts'>
<div class="col col-lg-1">
<a id="contactadd"><i class="fa fa-plus-circle"></i>Add</a>
</div>
</div>
Now I am trying to figure out how to make javascript code to remove each element when/if the minus button is clicked (in the template).
I'm not sure what kindof code I could then use to delete the contacts, via the depminus{{ID}} control. Any tips to get me started?

How to add HTML contents Dynamically on Button Click Event with AngularJS

I need to add HTML content on Button Click event using AngularJS. Is it possible??
My index.html
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
</div>
</div>
I want to add Nos. of HTML divs as per quantity added dynamically..
myApp.js
angular.module("myApp", []).controller("AddQuestionsController",
function($scope) {
$scope.myData = {};
$scope.myData.questionNos = "";
$scope.myData.doClick = function() {
//Do Something...????
};
});
It should be possible. I would data-bind your Divs to viewModel elements, and in your doClick function create the viewModels.
I would avoid directly creating Html in your viewModel.
For example:
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
<div ng-repeat="q in myData.questions">
<!-- BIND TO Q HERE -->
</div>
</div>
</div>
And in doClick:
$scope.myData.doClick = function() {
var newQuestions = getNewQuestionViewModels($scope.myData.questionNos);
for (var i = 0; i < newQuestions.length; i++) {
$scope.myData.questions.push(newQuestions[i]);
}
};
You have to store questions in collection and do repeat.
DEMO
HTML:
<div>
<input type="text" ng-model="data.qcount">
<button type="button" ng-click="data.add()">Add</button>
</div>
<div>
<div ng-repeat="q in data.questions track by $index">
<pre>{{ q | json }}</pre>
</div>
</div>
JS:
$scope.data = {
questions: [],
qcount: 0,
add: function() {
var dummy = {
'title': 'Q title',
'body': 'Q body'
},
newQ = [];
for (var i = 0; i < $scope.data.qcount; ++i) {
newQ.push(dummy);
}
$scope.data.questions = $scope.data.questions.concat(newQ);
$scope.data.qcount = 0;
}
};

Categories