I am trying to refactor a ViewModel
class because it has two methods that are really similar called updateRegions
and updateTowns
and even another one may end up being made called updateCountries
. I basically copy-paste the method and change the variable names. How can this be refactored with maybe an abstract class that has an abstract updateArea
function?
class SearchFilterViewModel {
_regionsList = [];
_townsList = [];
_regions = ko.observableArray();
_towns = ko.observableArray();
selectedRegion: KnockoutObservable<string> = ko.observable('');
selectedTown: KnockoutObservable<string> = ko.observable('');
selectedCategories: KnockoutObservableArray<string> = ko.observableArray([]);
constructor() {
this.updateRegions("2186224");
this.selectedRegion.subscribe(this.updateTowns.bind(this));
}
updateRegions(geonameId) {
var self = this;
self._regionsList = [];
$.ajax({
url: `http://api.geonames.org/children?geonameId=${geonameId}&username=elion`
}).then(function(allRegionsXML) {
var allRegionsJSON = xml2json(allRegionsXML);
var allRegions = JSON.parse(allRegionsJSON);
if(allRegions.geonames.length) {
for (var index = 1; index < allRegions.geonames.length - 1; index++) {
self._regionsList.push(allRegions.geonames[index].geoname);
}
} else {
if(allRegions.geonames) {
self._regionsList.push(allRegions.geonames.geoname);
}
}
self._regions(self._regionsList);
});
}
updateTowns(geonameId) {
var self = this;
self._townsList = [];
$.ajax({
url: `http://api.geonames.org/children?geonameId=${geonameId}&username=elion`
}).then(function(allTownsXML) {
var allTownsJSON = xml2json(allTownsXML);
var allTowns = JSON.parse(allTownsJSON);
if(allTowns.geonames.length) {
for (var index = 1; index < allTowns.geonames.length - 1; index++) {
self._townsList.push(allTowns.geonames[index].geoname);
}
} else {
if(allTowns.geonames) {
self._townsList.push(allTowns.geonames.geoname);
}
}
self._towns(self._townsList);
});
}
}
1 Answer 1
I have managed to get it down to this. It had been a while since I had encountered this scenario so I had to rack my brain for a bit and do some research. I basically followed this principle. Let me know if you have any suggestions for further improvements. Cheers.
interface iUpdateArea {
_areasList: Array<string>;
_areas: KnockoutObservableArray<string>;
_selectedArea: KnockoutObservable<string>;
updateSubArea(geonameId: string);
}
class Area implements iUpdateArea {
_areasList = [];
_areas = ko.observableArray([]);
_selectedArea = ko.observable('');
updateSubArea(geonameId){
var self = this;
self._areasList = [];
$.ajax({
url: `http://api.geonames.org/children?geonameId=${geonameId}&username=elion`
}).then(function(allRegionsXML) {
var allRegionsJSON = xml2json(allRegionsXML);
var allRegions = JSON.parse(allRegionsJSON);
if(allRegions.geonames.length) {
for (var index = 1; index < allRegions.geonames.length - 1; index++) {
self._areasList.push(allRegions.geonames[index].geoname);
}
} else {
if(allRegions.geonames) {
self._areasList.push(allRegions.geonames.geoname);
}
}
self._areas(self._areasList);
});
}
}
class SearchFilterViewModel {
_region: Area;
_town: Area;
constructor() {
this._region = new Area();
this._town = new Area();
this._region.updateSubArea("2186224");
this._region._selectedArea.subscribe(this._town.updateSubArea.bind(this._region));
}
}
$(document).ready(function() {
var _searchFilterViewModel: SearchFilterViewModel = new SearchFilterViewModel();
var _searchFilterForm = $("#find-vegan-products-page").find("form")[0];
ko.applyBindings(_searchFilterViewModel, _searchFilterForm);
$('.select-multiple').each(function(i, obj) {
//obj[i] gets each element inside the div
$(this).multiselect({
noneSelectedText: "CATEGORIES"
});
})
});
Explore related questions
See similar questions with these tags.