When writing the name of the building in the searchbar the ionic returns me this error item.tolowercase is not a function in ionic
How should I proceed to correct this detail?
This is a image example
This is home.ts
export class HomePage {
searchQuery: string = '';
items: object[];
constructor() {
this.initializeItems();
}
initializeItems() {
this.items = [
{ nome:'Abaeté', rua:'Rua Andira - 396', imagem:"assets/img/4Wj6RBrjQPGiTMhV6T9W_Abaete.JPG" },
];
}
getItems(ev: any) {
// Reset items back to all of the items
this.initializeItems();
// set val to the value of the searchbar
const val = ev.target.value;
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.items = this.items.filter((item) => {
return (item.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
}
Try to convert the items to string-
return (item.toString().toLowerCase().indexOf(val.toString().toLowerCase()) > -1);
Related
I have a Prosemirror Editor text and, I created a plugin that applies some decorations for text. It's been working pretty well, however, when I try to edit the text, it looks really buggy, and the styles are not properly applied to the text which is been edited.
The text originally had an orange background, but when I start to edit the text with decorations, the decoration from the moment that start to update onwards disappear or only shows in some parts
The video down below demonstrates the issue:
https://imgur.com/RCETIoO
Basically, this is the code that generates the Decorations:
export const getDecorationsForAnnotations = (
doc: any,
data: Data[],
selectedDataId?: string
) => {
let decos: any[] = [];
let initialPos: number | undefined = undefined;
let finalPos: number | undefined = undefined;
doc.descendants((node: Node, pos: number, parent: any) => {
...
// ... my logic to filter nodes here
decos.push(
Decoration.inline(pos, pos + parent.content.size, {
style: S.DECORATED_PROSEMIRROR_ANNOTATED_SELECTED_NODE,
})
);
}
}
}
return true;
});
return { decos };
};
export const getHighlightAnnotationsPlugin = (
...
) => {
return new Plugin({
key: new PluginKey("pluginHighlightNotes"),
state: {
init(config, editorState) {
const doc = editorState.doc as any;
return DecorationSet.create(doc, []);
},
apply(transaction, oldEditorState, newEditorState) {
let data: Data[] | undefined = undefined;
let decorationSet = undefined;
let selectedDataId = undefined;
const mark = new Mark(); // being used to pass metadata to pluginView ( to add components in the DOM )
if (transaction.getMeta("isTransactionToListen")) {
data = transaction.getMeta("data") as Data[];
selectedDataId = transaction.getMeta("selectedDataId") as string;
} else {
if (!data && oldEditorState instanceof DecorationSet) {
// reuse previous state and decorations
decorationSet = oldEditorState;
}
}
if (!decorationSet && data?.length) {
const doc = transaction.doc;
const { decos } = getDecorationsForAnnotations(
doc,
data,
selectedDataId
);
decorationSet = DecorationSet.create(doc, decos);
}
return decorationSet ? decorationSet : DecorationSet.create(transaction.doc, []);
},
},
view: (editorView: any) => {
return new pluginView(...);
},
props: {
decorations(state) {
return this.getState(state);
},
},
});
};
I could solve this problem by doing this:
In the pluginView:
return new pluginView(...);
},
extract the mouse cursor position (lastState.selection.ranges[0])
Check if the cursor position is inside a decorated text range
If the cursor is inside a decorated text range, then trigger a new Prosemirror transaction to refresh the decorations on the text
I am trying to create a clothing sales site, I am at the function which calculates the total of the products present in the cart at the localStorage level, but my function returns "null"
I really need help please...
here is the code:
constructor(private productService: ProductService,
private orderService: OrderService,
private httpClient: HttpClient,
private router: Router,
private spinner: NgxSpinnerService,
private toast: ToastrService) {
this.cartTotal$.next(this.cartDataServer.total);
this.cartDataObs$.next(this.cartDataServer);
let info: CartModelPublic = JSON.parse(localStorage.getItem('cart')!);
if (info !== null && info !== undefined && info.prodData[0].incart !== 0) {
// assign the value to our data variable which corresponds to the LocalStorage data format
this.cartDataClient = info;
// Loop through each entry and put it in the cartDataServer object
this.cartDataClient.prodData.forEach(p => {
this.productService.getSingleProduct(p.id).subscribe((actualProdInfo: ProductModelServer) => {
if (this.cartDataServer.data[0].numInCart === 0) {
this.cartDataServer.data[0].numInCart = p.incart;
this.cartDataServer.data[0].product = actualProdInfo;
this.CalculateTotal();
this.cartDataClient.total = this.cartDataServer.total;
localStorage.setItem('cart', JSON.stringify(this.cartDataClient));
} else {
this.cartDataServer.data.push({
numInCart: p.incart,
product: actualProdInfo
});
this.CalculateTotal();
this.cartDataClient.total = this.cartDataServer.total;
localStorage.setItem('cart', JSON.stringify(this.cartDataClient));
}
this.cartDataObs$.next({...this.cartDataServer});
});
});
}
}
AddProductToCart(id: number, quantity?: number) {
this.productService.getSingleProduct(id).subscribe(prod => {
// If the cart is empty
if (this.cartDataServer.data[0].product === undefined) {
this.cartDataServer.data[0].product = prod;
this.cartDataServer.data[0].numInCart = quantity !== undefined ? quantity : 1;
this.CalculateTotal();
this.cartDataClient.prodData[0].incart = this.cartDataServer.data[0].numInCart;
this.cartDataClient.prodData[0].id = prod.id;
this.cartDataClient.total = this.cartDataServer.total;
localStorage.setItem('cart', JSON.stringify(this.cartDataClient));
this.cartDataObs$.next({...this.cartDataServer});
this.toast.success(`${prod.name} added to the cart.`, "Product Added", {
timeOut: 1500,
progressBar: true,
progressAnimation: 'increasing',
positionClass: 'toast-top-right'
})
} // END of IF
// Cart is not empty
else {
let index = this.cartDataServer.data.findIndex(p => p.product.id === prod.id);
// 1. If chosen product is already in cart array
if (index !== -1) {
if (quantity !== undefined && quantity <= prod.quantity) {
// #ts-ignore
this.cartDataServer.data[index].numInCart = this.cartDataServer.data[index].numInCart < prod.quantity ? quantity : prod.quantity;
} else {
// #ts-ignore
this.cartDataServer.data[index].numInCart < prod.quantity ? this.cartDataServer.data[index].numInCart++ : prod.quantity;
}
this.cartDataClient.prodData[index].incart = this.cartDataServer.data[index].numInCart;
this.toast.info(`${prod.name} quantity updated in the cart.`, "Product Updated", {
timeOut: 1500,
progressBar: true,
progressAnimation: 'increasing',
positionClass: 'toast-top-right'
})
}
// 2. If chosen product is not in cart array
else {
this.cartDataServer.data.push({
product: prod,
numInCart: 1
});
this.cartDataClient.prodData.push({
incart: 1,
id: prod.id
});
this.toast.success(`${prod.name} added to the cart.`, "Product Added", {
timeOut: 1500,
progressBar: true,
progressAnimation: 'increasing',
positionClass: 'toast-top-right'
})
}
this.CalculateTotal();
this.cartDataClient.total = this.cartDataServer.total;
localStorage.setItem('cart', JSON.stringify(this.cartDataClient));
this.cartDataObs$.next({...this.cartDataServer});
} // END of ELSE
});
}
here is my CalculateTotal() function:
private CalculateTotal() {
let Total = 0;
this.cartDataServer.data.forEach(p => {
const {numInCart} = p;
const {price} = p.product;
// #ts-ignore
Total += numInCart * price;
});
this.cartDataServer.total = Total;
this.cartTotal$.next(this.cartDataServer.total);
}
and here is the result in the console:
I think it is because your const {numInCart} = p; in private CalculateTotal()
has a an object value
try changing const {numInCart} = p; to const {numInCart} = p.numInCart;
i've got an array:
dataSet: [
{ name: "Имя1", image: "img.jpeg", author: "Александр Полтавченко", date: "21.02.2020", id: 1 },
{ name: "Имя2", image: "img.png", author: "Александр Полтавченко", date: "21.02.2020", id: 2 },
],
addedToCart: []
and here is the function which put value from dataSet to addedToCart according ID from props:
added = (id) => {
this.setState (( { addedToCart, dataList } )=>{
const newItem = dataList.filter(el=>el.id===id);
const testArr = [...addedToCart ];
const filteredATC = testArr.filter((item, el)=>{
if(addedToCart.indexOf(item)===el){
item.count++
return item, el
}
else {
return item
}
it is works well (only one element with count ++) but if click add to another element it is just change element in array (with correct count surprisingly).
How to put another element into addedToCart, just like
[
{el1},
{el2}
]
filter returns an array instead of the desired element, you should use find instead.
I believe you would desire an approach like this:
added = (id) => {
this.setState (( { addedToCart, dataList } ) => {
const newItem = dataList.find(el=> el.id === id);
const testArr = [...addedToCart ];
const filteredATCIndex = testArr.findIndex((_item, id) => newItem.id === id)
// if there is an added item
if (filteredATCIndex !== -1) {
const count = testArr[filteredATCIndex].count + 1
testArr[filteredATCIndex] = { ...testArr[filteredATCIndex], count }
return { addedToCart: testArr }
}
// for new item
const newItemAdded = { ...newItem, count: 1 }
testArr.push(newItemAdded)
return { addedToCart: testArr }
})
}
though this approach duplicates data, which is not desirable. I suggest you consider to change addedToCart to an object where key value pairs are the id and count respectively from added items. This way you would avoid duplicating data.
then your update state would look like:
added = (id) => {
this.setState (( { addedToCart } ) => {
const count = typeof addedToCart[id] === 'undefined' ? 1 : ++addedToCart[id]
return { addedToCart: { ...addedToCart, [id]: count } }
})
}
I would like to use a BehaviorSubject to store an Array of objects and have a way to easily update (next?) a single item of that array without having to update the whole array.
I would also like for an easy way to subscribe to changes to an specific item of that array. I know it could be done with filter, but an easier way would be nice...
Is that possible?
I am currently using this version I created (which I don't know if it is the best way or not) that also persists its contents to localstorage:
export class LocalStorageBehaviorSubject<T, Y = T> {
private _data: BehaviorSubject<T>;
public asObservable() {
return this._data.asObservable();
}
public next(data: T) {
if(this.expirationFn !== null) {
data = this.expirationFn(data);
}
localStorage.setItem(this.key, JSON.stringify(data));
this._data.next(data);
}
public nextItem(item: Y) {
if (!Array.isArray(this._data.getValue())) {
throw "Type is not an Array";
}
let dados: any = (<any>this._data.getValue()).slice();
if (dados.some(r => r[this.id] === item[this.id])) {
dados = dados.map(r => r[this.id] === item[this.id] ? item : r);
} else {
dados.push(item);
}
if(this.expirationFn !== null) {
dados = this.expirationFn(dados);
}
localStorage.setItem(this.key, JSON.stringify(dados));
this._data.next(<any>dados);
}
public removeItem(id) {
if (!Array.isArray(this._data.getValue())) {
throw "Type is not an Array";
}
let dados: any = (<any>this._data.getValue()).slice();
dados = dados.filter(r => r[this.id] !== id);
localStorage.setItem(this.key, JSON.stringify(dados));
this._data.next(<any>dados);
}
public removeExpiredData(){
let data = this.loadFromStorage();
if (data) {
if(this.expirationFn !== null) {
data = this.expirationFn(data);
}
this._data.next(data);
}
}
public getValue() {
this.removeExpiredData();
return this._data.getValue();
}
public getItem(id): Y {
if (!Array.isArray(this._data.getValue())) {
throw "Type is not an Array";
}
this.removeExpiredData();
return (<any>this._data.getValue()).slice().find(t => t[this.id] == id);
}
constructor(private key: string, private id: string, defaultValue: any = null, private expirationFn: (dados: T) => T = null) {
this._data = new BehaviorSubject<T>(defaultValue);
this.removeExpiredData();
}
private loadFromStorage(): T {
let dadosStr = localStorage.getItem(this.key);
if (dadosStr) {
return JSON.parse(dadosStr);
}
return null;
}
}
I hoped that would be an simpler way...
Thanks
I would also like for an easy way to subscribe to changes to an
specific item of that array. I know it could be done with filter, but
an easier way would be nice...
You can use map operator and inside lambda array.find
Example
const mockStorage = {
values: {},
setItem(key, value) {
this.values[key] = value;
},
getItem(key) {
return this.values[key]
},
clearItem(key) {
this.values[key] = undefined;
}
}
class LocalStorageBehaviorSubject {
constructor(key, defaultValue) {
this.key = key;
this._data = new rxjs.BehaviorSubject(defaultValue);
}
nextItem(item) {
const list = this._data.value;
const itemIndex = list.findIndex(pr => pr.id === item.id);
this._data.next([
...list.slice(0, itemIndex),
{
...(list[itemIndex] || {}),
...item
},
...list.slice(itemIndex + 1)
]);
}
removeItem(id) {
this._data.next(this._data.value.filter(pr => pr.id !== id));
}
getItem(id) {
return this.asObservable()
.pipe(
rxjs.operators.map(values => values.find(pr => pr.id === id) || null),
rxjs.operators.distinctUntilChanged());
}
asObservable() {
return this._data.asObservable().pipe(
rxjs.operators.tap(values => {
if (values && values.length) {
mockStorage.setItem(this.key, JSON.stringify(values));
}
else {
mockStorage.clearItem(this.key);
}
}))
}
}
const localStorageBehaviorSubject = new LocalStorageBehaviorSubject('items', []);
localStorageBehaviorSubject
.getItem(1)
.subscribe(item => {
console.log(item);
})
localStorageBehaviorSubject.nextItem({id: 1, value: 'test'})
localStorageBehaviorSubject.nextItem({id: 1, value: 'test1'})
localStorageBehaviorSubject.nextItem({id: 2, value: 'test2'})
localStorageBehaviorSubject.nextItem({id: 3, value: 'test3'})
localStorageBehaviorSubject.removeItem(2);
localStorageBehaviorSubject.removeItem(1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
I have this component DynamicSelect (child compoenent) and I'm using it in an another component (parent) but when I try to validate my childe component, it deliver always the value as null so the validation is always false
DynamicSelect Component:
<template>
<a-select
:showSearch="true"
:placeholder=placeholder
:value="selectedValue"
#search="searchRegex($event)"
#change="$emit('changed-item', setChangedItem($event))"
#select="$emit('selected-item', setSelectedItem($event))"
:filterOption="filterOption"
>
<a-select-option
v-for="(item,idx) in dropdownData"
:value="idx"
:key="idx"
>{{item.text}}</a-select-option>
</a-select>
</template>
<script>
export default {
name: "DynamicSelect",
data(){
return{
dropdownData: [],
copyDropdownData:[],
selectedValue: undefined
}
},
props:{
//Input data collection
dataSrc:Array,
//Placeholder for input field
placeholder: String,
//if true the dropdown will be automatically cleared after element selected
resetAfterSelect: false,
// List of id to filter the dropdown list
lookFor:Array,
// Data to display in the dropdown if not set, lookFor list will be displayed
displayedValues: Array,
//Default Value
defaultValues:String,
},
beforeMount(){
this.checkDefaultVariable();
},
watch:{
dataSrc:function(newVar,oldVar) { // watch it
this.checkDefaultVariable()
}
},
methods:{
//Search for search term in the data collection 'lookFor' elements to set the dropdown list
async searchRegex(term){
if(term.length>2) {
let searchTerm = new RegExp(term.toUpperCase());
this.dropdownData = await this.filterData(searchTerm);
this.copyDropdownData = JSON.parse(JSON.stringify(this.dropdownData));
}
else
{
this.dropdownData = [];
this.copyDropdownData = [];
}
},
filterData(searchTerm){
return this.dataSrc.filter(x => {
let filtered= [];
for (let i=0; i<this.lookFor.length;i++){
if(x[this.lookFor[i]])
{
if(searchTerm.test(x[this.lookFor[i]].toUpperCase()))
{
let text = '';
if(this.displayedValues !== undefined)
{
for (let k=0; k<this.displayedValues.length;k++)
{
text += x[this.displayedValues[k]];
if(k < this.displayedValues.length-1)
text += ', '
}
}
else {
for (let k=0; k<this.lookFor.length;k++)
{
text += x[this.lookFor[k]];
if(k < this.lookFor.length-1)
text += ', '
}
}
x.text = text;
filtered.push(x);
}
}
}
return filtered.length>0
});
},
// just a logger
logger(event){
console.log(event);
},
async checkDefaultVariable(){
if (this.defaultValues !== '' && this.defaultValues !== undefined && this.dataSrc.length>0 ){
// console.log('DATA',this.dataSrc);
await this.searchRegex(this.defaultValues);
let selected = await this.setSelectedItem(0);
this.$emit('selected-item', selected)
}
},
// return the selected Item as an Object
setSelectedItem(id){
// console.log('ON SELECT');
let temp = JSON.parse(JSON.stringify(this.dropdownData[id]));
delete temp.text;
if(this.resetAfterSelect)
{
this.dropdownData = [];
this.selectedValue = undefined;
}
else {
this.selectedValue = id;
}
return temp
},
setChangedItem(id){
let temp = JSON.parse(JSON.stringify(this.copyDropdownData[id]));
delete temp.text;
if(this.resetAfterSelect)
{
this.copyDropdownData = [];
this.selectedValue = undefined;
}
else {
this.selectedValue = id;
}
return temp
},
// search in the dropdown list
filterOption(input, option) {
let searchTerm = new RegExp(input.toUpperCase());
if(searchTerm.test(this.dropdownData[option.key].text.toUpperCase()))
return true;
else {
for(let i=0;i<this.lookFor.length;i++){
if(searchTerm.test(this.dropdownData[option.key][this.lookFor[i]].toUpperCase()))
return true;
else if(i >= this.lookFor.length)
return false;
}
}
}
}
}
</script>
parent component:
<template>
<dynamic-select
:dataSrc="users"
placeholder="Lastname, Firstname"
#selected-item="onSelectUser($event)"
#changed-item="onSelectUser($event)"
:lookFor="['lastname','firstname']"
v-decorator="['contact', {valuePropName:'selectedValue',
rules: [{ required: true,
validator: userExists,
message: 'Error'}]}]"
>
</dynamic-select>
</template>
<script>
.
.
.
methods: {
userExists(rule, value, callback) {
console.log('VALUE', value); //always undefined
console.log('RULES',rule);
console.log('CALLBACK',callback)
return value !== null && value !== undefined && value.length > 2;
},
onSelectUser(user) {
console.log("user: " , user); // set with the selected value
}
},
.
.
.
</script>
I expect that the child component returns the selected value like when emitting an event, I also tried with models but it hasn't helped
thanks :)
You can easily communicate between components
Vue.config.debug = true;
// Parent
let App = new Vue({
el: "#the-parent",
data(){
return{ msg: "Nothing.." };
},
methods:{
receivedFromChild(request){
this.msg = request;
}
},
// Children
components: {
'children': {
template: `
<div><button #click="request">Send to parent!</button>` + `<input type="text" v-model="text"></div>`,
props: [ 'childrenRequest' ],
data() {
return {
text: 'this is value'
}
},
methods: {
request(){
console.log('work!');
this.$emit('received', this.text);
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="the-parent">
<h3>The children want me to say: {{ msg }}</h3>
<children #received="receivedFromChild"></children>
</div>