Reputation: 1052
I wrote my simple first example in knockout but I need a little help with two things. I searching information and examples of smiliar works but I don't found so I asking you. I need to add simple search on my page where I can filter results by name and surname. My first question is how I can do that correct because my current solution show me all results; (extra question how I can filter my json by name and surname in one query)? My second problem is with tab structure, is not working correct. Now when I click on li element I toggle results for this element to container and not hiding all current open results (simple example what I want to do); by the way I want to add style for active tab (example). Probably my problem comming from external json but I'm not sure. Please chceck my code I'm open on better solution than main.
external JSON (example.json)
[
{
"index": 0,
"name": [{
"first": "Barlow",
"last": "Moore"
}]
},
{
"index": 1,
"name": [{
"first": "Valeria",
"last": "Meadows"
}]
},
{
"index": 2,
"name": [{
"first": "Constance",
"last": "Singleton"
}]
},
{
"index": 3,
"name": [{
"first": "Wilder",
"last": "Steele"
}]
}
]
JS
$(document).ready(function() {
ko.applyBindings(viewModel);
});
ko.bindingHandlers.slideVisible = {
update: function(element, valueAccessor, allBindings) {
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
var duration = allBindings.get('fadeDuration') || 400;
if (valueUnwrapped == true)
setTimeout(function(){ $(element).fadeIn(duration); }, duration);
else
$(element).fadeOut(duration);
}
};
/* show all data from json */
function PersonInfo(data) {
this.firstName = ko.observable(data.name[0].first);
this.lastName = ko.observable(data.name[0].last);
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
this.showMoreInfo = ko.observable(false);
this.toggleshowMoreInfo = function () {
this.showMoreInfo(!this.showMoreInfo())
}
}
function PersonInfoViewModel() {
var self = this;
self.personData = ko.observableArray([]);
$.getJSON('example.json', function(allData) {
var mappedPersonalData = $.map(allData, function(item) {
this.query = ko.observable('');
var search = this.query().toLowerCase();
if(item.name[0].first.toLowerCase().indexOf(search) >= 0) {
return new PersonInfo(item)
}
});
self.personData(mappedPersonalData);
});
}
var viewModel = new PersonInfoViewModel();
<!-- template for presonal information -->
<script type="text/html" id="person-template">
<div class="col-xs-12 col-md-6">
<p><span data-bind="text: fullName"></span></p>
</div>
</script>
HTML
<form action="#">
<input placeholder="Search…" type="search" name="q" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off">
</form>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
<ul class="name-list" data-bind="foreach: personData">
<li data-bind="attr:{class: $index == 0 ? 'active' : ''}, click: toggleshowMoreInfo" role="button">
<span class="full-name" data-bind="text: fullName"></span>
</li>
<div class="hidden-sm hidden-md hidden-lg" data-bind="slideVisible: showMoreInfo, fadeDuration:600,template: {name: 'person-template'}"></div>
</ul>
</div>
<div class="col-xs-12 col-sm-6 col-md-8 col-lg-8 hidden-xs" data-bind="foreach: personData">
<div class="row" data-bind="slideVisible: showMoreInfo, fadeDuration:600,template: {name: 'person-template'}""></div>
</div>
</div>
</div>
Upvotes: 2
Views: 263
Reputation: 1529
There are many ways to do this. I used a computed observable to keep filtering the array of people to remove the ones that do not match.
var data = [
{
"index": 0,
"name": [{
"first": "Barlow",
"last": "Moore"
}]
},
{
"index": 1,
"name": [{
"first": "Valeria",
"last": "Meadows"
}]
},
{
"index": 2,
"name": [{
"first": "Constance",
"last": "Singleton"
}]
},
{
"index": 3,
"name": [{
"first": "Wilder",
"last": "Steele"
}]
}
];
var stringStartsWith = function (startsWith, string) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
ko.bindingHandlers.slideVisible = {
update: function(element, valueAccessor, allBindings) {
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
var duration = allBindings.get('fadeDuration') || 400;
if (valueUnwrapped == true)
setTimeout(function(){ $(element).fadeIn(duration); }, duration);
else
$(element).fadeOut(duration);
}
};
/* show all data from json */
function PersonInfo(data) {
this.firstName = ko.observable(data.name[0].first);
this.lastName = ko.observable(data.name[0].last);
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
this.showMoreInfo = ko.observable(false);
this.toggleshowMoreInfo = function () {
this.showMoreInfo(!this.showMoreInfo())
}
}
function PersonInfoViewModel() {
var self = this;
self.query = ko.observable('');
self.mappedPersonalData = $.map(data, function(item) {
return new PersonInfo(item)
});
self.filteredPeople = ko.computed(function () {
return self.mappedPersonalData.filter(function (value) {
if(self.query() === '' || self.query() === null){
return true; //no query
}
if (stringStartsWith(self.query().toLowerCase(), value.firstName().toLowerCase()) || stringStartsWith(self.query().toLowerCase(), value.lastName().toLowerCase())){
return true;
}
return false;
});
});
}
var viewModel = new PersonInfoViewModel();
$(document).ready(function() {
ko.applyBindings(viewModel);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<form action="#">
<input placeholder="Search…" type="search" name="q" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off">
</form>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
<ul class="name-list" data-bind="foreach: filteredPeople">
<li data-bind="attr:{class: $index == 0 ? 'active' : ''}, click: toggleshowMoreInfo" role="button">
<span class="full-name" data-bind="text: fullName"></span>
</li>
<div class="hidden-sm hidden-md hidden-lg" data-bind="slideVisible: showMoreInfo, fadeDuration:600,template: {name: 'person-template'}"></div>
</ul>
</div>
<div class="col-xs-12 col-sm-6 col-md-8 col-lg-8 hidden-xs" data-bind="foreach: filteredPeople">
<div class="row" data-bind="slideVisible: showMoreInfo, fadeDuration:600,template: {name: 'person-template'}"></div>
</div>
</div>
</div>
<!-- template for presonal information -->
<script type="text/html" id="person-template">
<div class="col-xs-12 col-md-6">
<p><span data-bind="text: fullName"></span></p>
</div>
</script>
Upvotes: 1