Vue.js filtering on array - arrays

I am trying to filter an array using a computed property in vue.js. I would like to search on on multiple fields, name, state, tags etc.
My data:
events: [
{
id: 1,
name: 'Name of event',
url: '#',
datetime: '2017-05-10T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'ordinary',
'advanced'
]
},
{
id: 2,
name: 'Another event',
url: '#',
datetime: '2017-05-12T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'beginner'
]
},
{
id: 3,
name: 'Great event',
url: '#',
datetime: '2017-05-18T00:00:00Z',
description: 'The full text of the event',
state: 'NSW',
tags: [
'beginner'
]
}
]
},
The following function works as expected, however I cant work out how to have it search the items in 'tags' (commented out).
searchevents: function(){
let result = this.events
if (this.filterValue){
result = result.filter(event =>
event.name.toLowerCase().includes(this.filterValue.toLowerCase()) ||
event.state.toLowerCase().includes(this.filterValue.toLowerCase())
// event.tags.toLowerCase().values().includes(this.filterValue.toLowerCase())
)
}
return result
}
The following returns a blank array, this method works ok when i have done it in angular but not in vue.
searchevents2: function(){
var searchRegex = new RegExp(this.filterValue,'i')
this.events.filter(function(event){
return !self.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state)
})
}
Ideally I would either like to be able to list array items to filter by or just filter by the entire array.
Appreciate any help, first post here so be gentle. I have a lot more experience with Python than Javascript so i may also use incorrect terminology at times.

You weren't too far off.
For your searchEvents filter, you just needed to add the tag filter. Here's how you might do that.
searchevents: function(){
let result = this.events
if (!this.filterValue)
return result
const filterValue = this.filterValue.toLowerCase()
const filter = event =>
event.name.toLowerCase().includes(filterValue) ||
event.state.toLowerCase().includes(filterValue) ||
event.tags.some(tag => tag.toLowerCase().includes(filterValue))
return result.filter(filter)
}
Array.some() is a standard array method that returns true if any element of the array passes your test.
searchevents2: function(){
const searchRegex = new RegExp(this.filterValue,'i')
return this.events.filter(event =>
!this.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state))
}
With searchEvents2 you really only left an errant self in there. Either you needed to set self before you executed the filter, or, as I have done here, turned it into an arrow function.
Example.

const app = new Vue ({
el: '#app',
data: {
search: '',
userList: [
{
id: 1,
name: "Prem"
},
{
id: 1,
name: "Chandu"
},
{
id: 1,
name: "Shravya"
}
]
},
computed: {
filteredAndSorted(){
// function to compare names
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
return this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in filteredAndSorted">{{user.name}}</li>
</ul>
</div>

Related

Categories not listed on Interaction Studio UI

I'm implementing on a website a tool from Sales Force that is called Interaction Studio.
We need to develop into a sitemap file, where those interaction written into the file will catch the information that we need.
Here we have an example of a sitemap
Basically, the function that collects the categories is a custom function that collects the information from the breadcrumb. The function is:
const getCategoriesIdAsArray = () => {
return Evergage.resolvers.fromSelectorAttributeMultiple(
"a.c-breadcrumb__link",
"href",
(link) => {
let catId = "";
if (link.length) {
link.shift();
link.map((url, index) => {
if (index < link.length - 1) {
catId += url.split("/").reverse()[0].split("-")[0] + "|";
} else {
catId += url.split("/").reverse()[0].split("-")[0];
}
});
return [catId];
} else {
return;
}
}
);
};
To collect the information, we need to write some code in the pageTypes array. This following code is what I have in my sitemap:
pageTypes: [
{
name: "product_detail_tecnico",
action: "product_detail_tecnico",
//isMatch: () => document.body.classList.contains("is-tech-product"),
isMatch: () => {
return Evergage.DisplayUtils.pageElementLoaded(
"body#productcore.is-tech-product",
"html"
).then(() => true);
},
locale: () => {
return buildLocale();
},
catalog: {
Product: {
_id: productId,
name: Evergage.resolvers.fromJsonLd("name"),
url: Evergage.resolvers.fromJsonLd("url"),
imageUrl: Evergage.resolvers.fromJsonLd("image"),
description: Evergage.cashDom("meta[name='description']")
.first()
.attr("content")
.substring(0, 250),
decoTecnico: () => {
return bodyClasses.includes("is-deco-product");
},
inventoryCount: 1,
idc: productId && productId.split("_")[1],
idp: getProductIdFromUrl(),
price: Evergage.resolvers.fromJsonLd("offers.0.price"),
precioAnterior: Evergage.util.getFloatValue(
Evergage.cashDom(".c-product-price__before .js-old-price")
.first()
.text()
.split(" ")[0]
.replace(",", ".")
),
video: Evergage.cashDom("iframe#player").attr("src"),
categories: () => {
return getCategoriesIdAsArray();
},
},
},
]
As we can see, the last attribute, categories returns the value from the custom function that is an array with one string due to the data that I must pass in.
The data that I get on the visual editor:
In the sitemap into the pageTypes I also have this code:
{
name: "Category",
action: "Viewed Category",
isMatch: () => {
return Evergage.DisplayUtils.pageElementLoaded(
"body.categorycore",
"html"
).then(() => true);
},
catalog: {
Category: {
_id: getCategoriesId(),
//parentId: categoryParentId,
name: categoryName,
department: () => isDepartment(),
url: Evergage.resolvers.fromHref(),
description: Evergage.cashDom("meta[name='description']")
.first()
.attr("content"),
},
},
listeners: [
Evergage.listener("click", "section.c-distributive-filters", () => {
Evergage.sendEvent({
action: "Filter Results",
});
}),
],
},
This code is required because to assign a category to a product, we must have that category recorded in the UI.
As we can see in the next picture, the categories are recorded.
The event stream is recording the following:
This is the code from item:
{
description:
"Tira LED 12V DC 30LED/m 5m IP20 Ancho 10mm es una opción muy extendida si se desea disponer de una luz decorativa que consuma muy poco. Su diseño flexi...",
idc: "122262",
inventoryCount: 1,
type: "Product",
url: "https://dev61.efectoled.com/es/comprar-tiras-led-monocolor/62294-tira-led-12v-dc-smd5050-30ledm-5m-ip20.html",
idp: "62294",
price: 7.95,
imageUrl:
"https://4438d3e301b6821c9a36cfd759bc58f8/442295-thickbox_default/tira-led-12v-dc-smd5050-30ledm-5m-ip20.jpg",
name: "Tira LED 12V DC 30LED/m 5m IP20 Ancho 10mm",
decoTecnico: false,
attributes: {
idp: { value: "62294" },
decoTecnico: { value: false },
idc: { value: "122262" },
},
id: "62294_122262",
categories: [{ _id: "10|63|24", type: "c" }],
}
But when I go into the details of a product, the categories are not.
I have read all the documentation that Interaction Studio provides, but I can not understand what happens. Because in the visual editor the data is recorded and in the event stream also is recorded.
Does anybody know why this is happening? Thanks in advance

TypeError: (node.text || []).map is not a function

This is the final result which i obtained in mapping state i want to map the text array inside node state please Ignore the nodes const it is the output which i got in the console.log(this.state.nodes);
`const nodes = [{
id: 1,
style: 'dummy data',
className: 'dummy data',
text: [
{ id: 1, text: 'text 1'},
{ id: 2, text: 'text 2'}
]
}]`
Here i map the node state i have text array in node State i want to access text 1 inside text how can i achieve this by mapping
`{this.state.nodes.map((node, index) => {
const showbutton = node.className === 'square';
const decisionbutton = node.className === 'diamond';
return(
<div
key={index}
className={'node ' + node.className}
id={node.id}
ref={nodes => this.refs.nodes[index] = nodes}
style={node.style}
onClick={this.activeElem}
>
{(node.text|| []).map((child,key)=>{
return (
<div key={key}>
{child.text}
</div>
);
})}`
I also had the same issue with map inside the rxjs html
Here's my work around it...
return(
<div
key={index}
className={'node ' + node.className}
id={node.id}
ref={nodes => this.refs.nodes[index] = nodes}
style={node.style}
onClick={this.activeElem}
>
{Array.from(Array(node.text.length), (e, i) => {
return (
<div key={key}>
{node.text[i].text}
</div>
)
}
</div>
)
see if it helps.
I'm just creating a temp empty array and using it's index to iterate over the node.text array
The problem is that the first variable in condition (node.text || []), isn't really an Array.
When you tried to console it, it showed you [object][object].
which is a JS Objectdata type - And map(), is a function that exists in Array.prototype.
When you did console.log(JSON.stringify(node.text)) it did show you the text as inteded.
.map() won't work on undefined also.
That's the reason it's failing. node.text isn't an array.
If you return a object, consider using Object.keys (info) to loop through it, or a for...of loop,
or just reformat node.text to be of type array.
const nodes = [{
id: 1,
style: 'dummy data',
className: 'dummy data',
text: [
{ id: 1, text: 'text 1'},
{ id: 2, text: 'text 2'}
]
}]
node.map((node) => { return node.text.map((value) => (value.id)); })
running this, it returns the valid values... just make sure that the node.text has object as values rather then string as shown in the screenshot

Using a pipe to filter an array of objects by an array of strings in Angular2

I am attempting to filter an array of objects which have a similar structure to this:
posts = [
{
title: 'post 1 title',
location: 'Location 1'
},
{
title: 'post 2 title',
location: 'Location 2'
},
{
title: 'post 3 title',
location: 'Location 3'
}
]
I am filtering this array of objects (above) with an array of strings (below)
locations = [
'Location 1',
'Location 2',
'Location 3'
]
How it should be working:I enter into a 'tag' style input box in the browser as many locations as I want and it should display the posts with location properties matching those location 'tags' in the locations array.
All the posts are initially displayed (which is good), then when I enter tags I am able to get the filtered objects in the log which shows that they seem to be getting filtered based on my inputs. but the html in which the posts are looped through does not show the relevant posts.
I am using firebase to store the posts, and calling my service which returns all the posts on ngOnInit()
ngOnInit() {
this._tags = [];
this._postService.getAllPosts().subscribe(posts => {
this._posts = posts;
});
}
HTML:
<!--input *edited some stuff out like the add tag function. 'term' model gets pushed into the _tags array on enter or click etc-->
<input type="text" placeholder="Search" [(ngModel)]="term [ngModelOptions]="{standalone: true}">
<!--tag list, shows tags that have been searched-->
<div class="tag" *ngFor="let t of _tags">{{t}}</div>
<!filtered post list-->
<div *ngFor="let post of _posts | tagsearchfilter:_tags">
<h4>{{post.title}}</h4>
</div>
...
Pipe filter:
import { Pipe, PipeTransform } from '#angular/core';
import { PostService } from '../../services/post/post.service';
#Pipe({
name: 'tagsearchfilter'
})
export class TagSearchFilterPipe implements PipeTransform {
transform(posts: any, tags: any): any {
let rposts = [];
if (tags === undefined || tags.length === 0 ) {
return posts;
}
posts = posts.filter(function(post){
tags.forEach(tag => {
if (
post.location.toLowerCase().includes(tag.toLowerCase())
) {
rposts.push(post);
console.log('Post location: ' + post.location + ' Tag: ' + tag);
}
});
posts = rposts;
console.log(posts);
return posts;
});
}
}

NaN showing even with number (angularjs)

In the controller:
$scope.startCount = 0;
$scope.vs = function (number) {
$scope.startCount = number;
}
$scope.startTimeout = function () {
$scope.startCount = $scope.startCount + 1;
mytimeout = $timeout($scope.startTimeout, 1000);
}
$scope.startTimeout();
$scope.stopTimeout = function () {
$timeout.cancel(mytimeout);
alert("Timer Stopped");
}
$scope.meals = [
{ title: 'Abs', url:"#/app/mworkouts",id: 100, img: "img/female.jpg", vid:"vid/1.mp4",},
{ title: 'Arms', url:"#/app/browse",id: 2 , img: "img/male.jpg", vid:"vid/2.mp4"},
{ title: 'Biceps', url:"#/app/search",id: 3, img: "img/Spotify_2.jpg", vid:"vid/1.mp4" },
{ title: 'Legs', url:"#/app/search",id: 4, img: "img/Spotify_4.jpg", vid:"vid/2.mp4" },
{ title: 'Core', url:"#/app/mworkouts",id: 5, img: "img/female.jpg", vid:"vid/1.mp4" },
{ title: 'Back', url:"#/app/mworkouts",id: 6, img: "img/male.jpg", vid:"vid/2.mp4" }
];
In the html:
<div ng-repeat="m in meals">
<button ng-click='vs({{m.id}})'>Setter</button>
</div>
So how it is supposed to work is pass in a number from the array and then count up using $timeout. It works perfectly fine if I enter in a number manually but I want the number to come from the array. Also, {{m.id}} is definelty a number because I have tested it {{m.id-60}} and it works. I am have no idea what is wrong.
Sorry about the weird names...it's just an example :)
Just get rid of the {{}}
ng-click='vs(m.id)'
It's seems to work for me here! I added couple of log for you to make sure
<div ng-repeat="m in meals">
<button ng-click='vs(m.id)'>Setter</button>
</div>
http://plnkr.co/edit/tpl:rfqcl9AHEoJZEEJxyNn2?p=preview

unable to update the ngmodel of select list

Case :
I am trying to update the value of ng-model inside the controller but it is not working and i wonder why , any idea ?
HTML :
<select ng-model="dumm" ng-options="item.name as item.type for (name, item) in availableFilters" ng-change="selectFilter()"></select>
JS :
$scope.selectFilter = function () {
$scope.availableFilters[$scope.dumm].visible = true;
$scope.dumm = "";
};
$scope.availableFilters = {
name: {
type: 'Name',
name: 'name'
},
producttype: {
type: 'Product type',
name: 'producttype',
data: $scope.xxx
},
status: {
type: 'Status',
name: 'status',
data: $scope.unitStatusTypes
}
};
$scope.dumm should be an object rather than a string, otherwise the binding won't work - this fact is quite obscure in Angular's docs. The binding should look like this:
<select ng-model="dumm.value"...
and the variable's definition:
$scope.dumm = {value: ""};

Resources