I have data stored in my localStorage and in my component OnInit I check if that data exist filter the table results if not, show all results.
The issue is that this functionality sometimes works and sometimes not! I'm not sure what cause the issue but it's most highly to fail when I hard refresh my page Ctrl+F5
Code commented
searchbar_values: boolean = false;
tableSearch_column: boolean = false;
ngOnInit(): void {
// Get all data from server
this.getList(null);
// Condition 1 (first storage values)
const getstoredata = store.get('searchbar_values')
if (getstoredata) {
this.searchbar_values = true;
this.tableSearch_column = false;
}
// Condition 2 (second storage values)
const getstoredata2 = store.get('tableSearch_column')
if (getstoredata2) {
this.tableSearch_column = true;
this.searchbar_values = false;
}
// if first condition happened do this (sometimes fail)
if (this.searchbar_values) {
const storeData = store.get('searchbar_values')
this.globalSearchService.searchTerm.next(storeData.value);
this.globalSearchService.searchTerm.subscribe((newValue: string) => {
this.searchTerm = newValue;
if (newValue != null) {
this.visible = false;
this.globalSearchService.getSearchBar(newValue).subscribe((data: any) => {
this.listOfData = data.data;
this.limit = data.limit
this.totalPages = data.total
this.page = data.page
this.pages = data.pages
this.isSpinning = false;
store.set('searchbar_values', { value: newValue })
this.searchbar_values = true;
if (this.tableSearch_column) {
store.remove('tableSearch_column');
this.tableSearch_column = false;
}
});
}
});
}
// if second condition happened do this (sometimes fail)
if (this.tableSearch_column) {
const storeData = store.get('tableSearch_column')
this.isSpinning = true;
this.globalSearchService.getTableFilter(storeData.field, storeData.op, 'none', storeData.value).subscribe((data: any) => {
this.listOfData = [];
this.listOfData = data.data;
this.limit = data.limit
this.totalPages = data.total
this.page = data.page
this.pages = data.pages
this.isSpinning = false;
});
}
}
PS: tableSearch_column and searchbar_values will never be stored at the same time in localStorage because I always remove one before storing other one.
So the logic is either one of them is exist or none of them.
Any idea?
Solved
I've moved my storage check into constructor like this:
constructor(
//....
) {
//...
const getstoredata = store.get('searchbar_values')
if (getstoredata) {
this.searchbar_values = true;
this.tableSearch_column = false;
}
const getstoredata2 = store.get('tableSearch_column')
if (getstoredata2) {
this.tableSearch_column = true;
this.searchbar_values = false;
}
}
And then called my conditions in ngOnInit as you see in my question above. now everytime it calls localStorage and return correct values.
Related
I am making a list where you can edit or delete items. I am listening for 2 events on the same table. One for edit and one for delete, I am listening on the table and not the actual buttons as they are created dinamycally. Edit and Delete both have the same id, the id of the product, which I am using later for the http requests.
,
Now when I press edit the console.logs from the delete functions fire up,but nothing happens, if I am trying to save the item the http request doesnt work, it will not take the id (but it logs it to the console)
If I press the delete button once nothing happnes, if I press it a second time the page refreshes and the item is deleted.
Is there a way to listen for those events separately or for them to not interfere with one another? All I want is if I press the edit button the respective item's values to go to the input field and when I press Add Item update the product in the JSON file as well, and on delete press, to delete the item from the JSON file and remove from the html document.
Update: Managed to resolve edit function
Here is my code:
import { http } from "./http.js";
import { ui } from "./ui.js";
const productsURL = "https://61363d1a8700c50017ef54c1.mockapi.io/product";
// const addProductBtn = document.querySelector('.new-product-btn');
const adminContainer = document.querySelector('.admin-container');
const addItem = document.querySelector('.admin-add-item-btn');
const imgInput = document.getElementById('image');
const nameInput = document.getElementById('name');
const priceInput = document.getElementById('price');
const stockInput = document.getElementById('stock');
const categoryInput = document.getElementById('category');
const typeInput = document.getElementById('type');
const descriptionInput = document.getElementById('description');
const validSvg = document.querySelectorAll('.valid_input_svg');
// const adminForm = document.getElementById('admin-form');
const adminTable = document.getElementById('admin-tbody');
const editBtn = document.querySelectorAll('.edit-btn');
const adminBtn = document.querySelectorAll('.admin-delete-btn');
const cancel = document.getElementById('cancel');
let productToEdit;
let edit = false;
let id;
document.addEventListener('DOMContentLoaded', listAdminProducts);
// adminForm.addEventListener('submit', validateInput);
addItem.addEventListener('click', addOrEditProducts);
// adminTable.addEventListener('click', editOrDeleteItem);
adminTable.addEventListener('click', deleteProduct);
adminTable.addEventListener('click', editProduct);
cancel.addEventListener('click', cancelEdit);
function listAdminProducts() {
http.get(productsURL).then(products => {
ui.showAllAdminProducts(products);
});
};
function addOrEditProducts() {
if (edit === true && validateInput() === true) {
productToEdit = {
image: imgInput.value,
name: nameInput.value,
price: priceInput.value,
stock: stockInput.value,
category: categoryInput.value,
type: typeInput.value,
description: descriptionInput.value,
};
http
.put(`${productsURL}/${id}`, productToEdit)
.then(() => listAdminProducts());
console.log(`${productsURL}/${id}`)
ui.clearFields();
id = '';
edit = false;
return;
} else if (edit === false && validateInput() === true) {
const product = {
image: imgInput.value,
name: nameInput.value,
price: priceInput.value,
stock: stockInput.value,
category: categoryInput.value,
type: typeInput.value,
description: descriptionInput.value
};
http.post(productsURL, product).then(() => listAdminProducts());
ui.clearFields();
};
};
function editProduct(e) {
console.log('works');
if (e.target.classList.contains('edit-btn')) {
edit = true;
id = e.target.getAttribute('id');
console.log(id);
console.log(e.target)
http.get(`${productsURL}/${id}`).then((data) => {
imgInput.value = data.image;
nameInput.value = data.name;
priceInput.value = data.price;
stockInput.value = data.stock;
categoryInput.value = data.category;
typeInput.value = data.type;
descriptionInput.value = data.description;
});
console.log(`${productsURL}/${id}`)
};
// id = '';
}
function deleteProduct(e) {
console.log(e.target);
if (e.target.className === 'admin-delete-btn') {
console.log(e.target);
id = e.target.getAttribute('id');
console.log(id);
http
.delete(`${productsURL}/${id}`)
.then(() => listAdminProducts())
.catch("Error on delete");
id = '';
}
ui.showSuccessMessage('Product deleted', adminContainer);
}
function cancelEdit() {
ui.clearFields;
imgInput.className = '';
nameInput.className = '';
priceInput.className = '';
stockInput.className = '';
categoryInput.className = '';
edit = false;
}
function validateInput() {
let valid = true;
if (imgInput.value == '') {
if (imgInput.classList.contains('input-invalid')) {
imgInput.classList.remove('input-invalid');
};
ui.showAdminMessage('Must contain a link to an image', 0);
imgInput.classList.add('input-invalid');
valid = false;
} else {
imgInput.classList.add('input-valid');
validSvg[0].style.display = "block";
removeClass(imgInput, 0);
};
if (nameInput.value === '') {
if (nameInput.classList.contains('input-invalid')) {
nameInput.classList.remove('input-invalid');
};
ui.showAdminMessage('Name is requierd', 1);
nameInput.classList.add('input-invalid');
valid = false;
} else {
// stockInput.classList.remove('input-invalid');
nameInput.classList.add('input-valid');
validSvg[1].style.display = "block";
removeClass(nameInput, 1);
};
if (priceInput.value == "" || isNaN(priceInput.value) || priceInput.value < 0) {
if (priceInput.classList.contains('input-invalid')) {
priceInput.classList.remove('input-invalid');
};
ui.showAdminMessage('Price must be a number greater then 0', 2);
priceInput.classList.add('input-invalid');
valid = false;
} else {
// stockInput.classList.remove('input-invalid');
priceInput.classList.add('input-valid');
validSvg[2].style.display = "block";
removeClass(priceInput, 2);
};
if (stockInput.value == "" || isNaN(stockInput.value) || stockInput.value < 0) {
if (stockInput.classList.contains('input-invalid')) {
stockInput.classList.remove('input-invalid');
};
ui.showAdminMessage('Stock must be a number greater then 0', 3);
stockInput.classList.add('input-invalid');
valid = false;
} else {
// stockInput.classList.remove('input-invalid');
stockInput.classList.add('input-valid');
validSvg[3].style.display = "block";
removeClass(stockInput, 3);
};
if (categoryInput.value === 'barware' || categoryInput.value === 'spirits') {
// categoryInput.classList.remove('input-invalid');
categoryInput.classList.add('input-valid');
validSvg[4].style.display = "block";
removeClass(categoryInput, 4);
} else {
ui.showAdminMessage('Category must be barware or spirits', 4);
categoryInput.classList.add('input-invalid');
valid = false;
};
return valid;
};
function removeClass(element, index) {
// console.log(element, index);
setTimeout(() => {
element.className = '';
validSvg[index].style.display = "none";
}, 3000)
}
Finally managed to resolve it, I had to put e.preventDefault() in both functions, so the page will not reload first
I have a variable called
let idStatus = '';
I need my getValues function to return true and am using this variable to determine whether it returns true or false.
function getValues(){
let idStatus = '';
let getValuesUrl = 'https://something.something.com/v1.0/something/1?apiKey=1234abcdefghijklmnop';
const getValuesRequest = new XMLHttpRequest();
getValuesRequest.responseType = 'json';
getValuesRequest.open('GET', getOptionValuesUrl);
getValuesRequest.send();
getValuesRequest.onreadystatechange = function () {
const response = getValuesRequest.response;
if (response) {
if (getValuesRequest.status == 200) {
console.log('Success');
if(validateIds(response)){
console.log('ID is Valid');
idStatus = true;
}
else {
console.log('ID is NOT Valid');
idStatus = false;
}
}
}
console.log(idStatus);
return idStatus;
}
function validateIds(obj) {
const data = obj['data'];
console.log(data);
let validId = '';
for (let i = 0; i < data.length; i++) {
if (data[i].id == 1) {
validId = true;
}
else {
validId = false;
}
}
console.log(validId);
return validId;
}
Valid IDs runs the way it should and getValues console.logs the appropriate responses when it is true or false, yet idStatus always returns null.
Here I've made a simpler version of your code. I used axios instead of XMLHttpRequest as it's simpler to use.
const axios = require("axios");
async function getValues(link) {
const response = await axios.get(link);
if (!response.statusCode == 200) return false;
if (response.data.some(elm => elm.id != 1)) return false;
return true;
}
// if the status isn't 200, return false;
if (!response.statusCode == 200) return false;
// if one id isn't 1, return false;
if (response.data.some(elm => elm.id != 1)) return false;
If you want more details on Array.some(), here's a link Array.Some()
the parent component makes a call to the api and pushes data to the sharedData service and subscribes to it. So everytime a change has been made to the date set its automatically updated in the parent component.
ngOnInit() {
this.initialise();
}
initialise() {
this.dataShareService.setDataFilter(null);
this.loading = true;
this.assetService.table_data().subscribe(res => {
this.dataShareService.setEnviromentData(res);
this.dataShareService.enviromeData.subscribe(r => {
this.loading = false;
this.data = r;
let i = 0;
console.log('just a ocunt', i++)
this.searchValue = r['results'].map((val: any) => {
return val.name;
});
this.totalItems = this.data['results'].length;
this.loading = false;
}, err => {
console.log(err);
});
}
this data is then passed down to the child component - which is the data table
<data-table
*ngIf="!loading"
class="flex_display_centre"
[showTable]="showTable"
[columns]="columns"
[limit]="limit"
[data]="data">
</data-table>
I am the onScroll function to to get the why coordinates.
<ngx-datatable
[scrollbarV]="true"
[loadingIndicator]="isLoading"
[limit]="limit"
(scroll)='onScroll($event.offsetY)'>
</ngx-datatable>
onScroll(offsetY: number) {
this.isLoading = true;
let numberViewRows = 12;
const viewHeight = this.el.nativeElement.getBoundingClientRect().height - this.headerHeight;
if (!this.isLoading && offsetY >= this.rowHeight * numberViewRows ) {
this.loadPageData.emit(this.data['next']);
this.isLoading = false;
}
}
Once the condition is true an emitter is sent to the parent component to get the next page/data
nextPage(pageNum: any) {
this.isLoading = true;
this.assetService.reloadDataTable(pageNum).subscribe(results => {
this.dataShareService.setEnviromentData(results);
this.isLoading = false;
}, err => {
console.log('err', err)
});
}
data share service:
this first time the service get data it assigns the to previousEnviromentData variable. everytime the api is called the next/prev and count is updated and the results array is appended to the previous data
private enviromeInfoData = new BehaviorSubject([{}]);
enviromeData = this.enviromeInfoData.asObservable();
private previousEnviromentData;
setEnviromentData(data: any) {
if (data['results'] !== this.previousEnviromentData && this.previousEnviromentData !== undefined) {
this.previousEnviromentData["count"] = data.count,
this.previousEnviromentData["next"] = data.next,
this.previousEnviromentData["page_count"] = data.page_count,
this.previousEnviromentData["previous"] = data.previous,
this.previousEnviromentData['results'].push(...data['results']);
this.enviromeInfoData.next(this.previousEnviromentData);
} else {
this.previousEnviromentData = data;
this.enviromeInfoData.next(data);
}
}
However when I scroll to the bottom of the table the data is updated in the parent components and data share servuce but never updates in the table.
What am I doing wrong/missing?
I don't even know where to start....
Here is my code...
const request = require("request");
const bodyParser = require('body-parser');
let posts = [];
function sort(postsCollection, type, direction){
let target = postsCollection[type];
let swapp = false,
n = postsCollection.length - 1,
x = postsCollection;
do {
swapp = false;
for(let i = 0; i < n; i++){
if(x[i][type] < x[i+1][type]){
let temp = x[i];
x[i+1] = temp;
swapp = true;
}
}
n--;
} while (swapp);
return x;
}
function postExists(posts, post){
if(posts.indexOf(post) == -1){
return false;
}else{
return true;
}
}
module.exports = {
buildPostsCollection(options, url){
//posts = [];
if(!options || !url){
return "Error: No options or URL given";
}
options.tags.forEach((tag) => {
const newUrl = `${url}?tag=${tag}`;
request.get(newUrl, (err, res, body) => {
let target = JSON.parse(body)['posts'];
Object.keys(target).forEach((key) => {
if(postExists(posts, target[key]) == false){
posts.push(target[key]);
}
/*
if(!(posts.includes(target[key]))){
posts.push(target[key]);
}
*/
})
});
});
return posts;
},
orderPostsCollection(postsCollection, sortType, direction){
if(!sortType && !direction){
return postsCollection;
}
if(sortType) {
return sort(postsCollection, sortType, direction);
}else if(direction){
return sort(postsCollection, false, direction);
}
}
};
I don't know if its me or what.. But when I call this code through the api server I just made it duplicates itself over and over.... IE.. The posts array does not disappear, it just adds to itself everytime the page is refreshed..
Also I should add that, I had issues even keeping the posts array in existence across the functions.. I can't array.push to it unless its a global variable. And for some reason my duplicate checking functions do not work on it..
I am trying to bind a variable to a scope before it moves to the view but my view shows before the variable is bounded. Here is my code.
$scope.getListing = function() {
var deferred = $q.defer();
$scope.$applyAsync(function() {
$rootScope.listingDetails =[];
referralCasesGroupByCaseStatus.getListing($rootScope.userDetails.rows.item(2).value).then(function(data){
$rootScope.listingDetails = data
deferred.resolve($rootScope.listingDetails)
if($rootScope.fromDashboard === false) {
$scope.showCaseStatus(1);
$state.go('app.case_status')
}
else {
$scope.showCaseStatus($rootScope.statusNumber)
$state.go('app.case_status')
$ionicLoading.hide();
}
});
})
return deferred.promise;
};
var changedNumber = 0;
$scope.showCaseStatus = function(number) {
var finishedPushingListings = false;
$rootScope.listingByCaseStatus = [];
$rootScope.caseStatusListings = [];
if(changedNumber !== 0 && changedNumber !== number) {
changedNumber = number;
}
else {
if(changedNumber > 0) {
$scope.$applyAsync($rootScope.detailsPresent = true);
}
}
$scope.$applyAsync(function() {
angular.forEach($rootScope.listingDetails, function(value, key) {
if(value.real_estate_agent_assignment_status_id == number) {
$rootScope.listingByCaseStatus.push(value);
}
});
})
$scope.$applyAsync(function() {
if($rootScope.listingByCaseStatus == 0 || $rootScope.listingByCaseStatus == undefined || $rootScope.listingByCaseStatus == null) {
$rootScope.detailsPresent = true;
$rootScope.changeNumber = true;
finishedPushingListings = true;
}
else {
$rootScope.detailsPresent = false;
$scope.noMoreItemsAvailable = false;
$rootScope.changeNumber = true;
finishedPushingListings = true;
}
})
};
The main problem here is that the function $scope.showCaseStatus($rootScope.statusNumber) doesnt finish executing before it executes the $state.go('app.case_status') and i would like for it to wait and finish executing before it jumps to the $state.go('app.case_status').
Any help is appreciated.
Since you are using $applyAsync(), the function effects are asynchronous. One way to achieve what you want is to make showCaseStatus() return a promise - and take into account that there are 2 asynchronous blocks:
$scope.showCaseStatus = function(number) {
var ..., d1, d2;
...
d1 = $q.defer();
$scope.$applyAsync(function() {
angular.forEach($rootScope.listingDetails, function(value, key) {
...
});
d1.resolve();
})
d2 = $q.defer();
$scope.$applyAsync(function() {
...
d2.resolve();
})
// both promises must be resolved to continue
return $q.all([d1.promise, d2.promise]);
};
Then the caller becomes:
$scope.showCaseStatus($rootScope.statusNumber).then(function() {
$state.go('app.case_status')
$ionicLoading.hide();
});
Some notes:
If you do not need the async blocks, you can remove them and simplify the code
If the second async block relies on the result of the first, they too should be synchronized