Angular Custom Pipe - Multilevel Filtering - angular10

I have a set of data which I need to filter via pipe. Now, in that data array, some objects have a nested array. Similar to this:
this.testArray = [
{
name: "Ford",
cars: [
{
name: "Figo",
year: "2015"
},
{
name: "Ecosport",
year: "2021"
},
{
name: "Endeavour",
year: "2021"
}
],
location: "USA"
},
{
name: "Suzuki",
location: "Japan"
},
{
name: "Honda",
cars: [
{
name: "Brio",
year: "2015"
},
{
name: "Amaze",
year: "2021"
},
{
name: "CR-V",
year: "2021"
}
],
location: "Japan"
},
{
name: "Hyundai",
cars: [
{
name: "Tucson",
year: "2015"
},
{
name: "Creta",
year: "2021"
},
{
name: "Venuw",
year: "2021"
}
],
location: "South Korea"
},
{
name: "Renault",
cars: [
{
name: "Duster",
year: "2015"
},
{
name: "Scala",
year: "2021"
},
{
name: "Kwid",
year: "2021"
}
],
location: "France"
}
];
Now, the requirement is that when I type a company name it should filter and when I type a car name, the same should happen with the nested array returning only the filtered car name. Eg: If I search for 'Figo', it should return me Ford and then Figo nested inside.
here is my pipe I am using for this:
#Pipe({ name: "carCompSearch" })
export class CarCompSearch implements PipeTransform {
transform(carData: [], searchString: string) {
if (!carData || !searchString) {
return carData;
}
return carData.filter((item: any) => {
return item.name.toLowerCase().includes(searchString.toLowerCase());
});
}
}
I know the pipe is incomplete. Need help in completing this.
and here is the template:
<div *ngFor="let comp of testArray | carCompSearch: searchString">
<h2>{{comp.name}}</h2>
<h5>{{comp.location}}</h5>
<ul>
<li *ngFor="let model of comp.cars">
<span>{{model.name}} - {{model.year}}</span>
</li>
</ul>
</div>
your help is appreciated.
Here is the fork link:
https://codesandbox.io/s/custompipe-forked-5urrl

This may help you out:
#Pipe({ name: "carCompSearch" })
export class CarCompSearch implements PipeTransform {
transform(carData: any[], searchString: string) {
if (!carData || !searchString) {
return carData;
}
const carDataWithNestedFilter = carData.map((item) => {
const newItem = { ...item }; // copy the item to not manipulate the original one
newItem.cars = item.cars?.filter((car) =>
car.name.toLowerCase().includes(searchString.toLowerCase())
);
return newItem;
});
return carDataWithNestedFilter.filter((item) => {
const nameIncludes = item.name
.toLowerCase()
.includes(searchString.toLowerCase());
return item.cars?.length > 0 || nameIncludes;
});
}
}

Related

how to pass i18n data $t as prop to a component

in a normal way with out translation but i want to translate the two object array and bind into a component
<InfoNews
v-for="infonew in infonews"
:id="infonew.id"
:title="infonew.title"
:content="infonew.content"
/>
data() {
return {
infonews: [
{
id: "01",
title: "what we do",
content:"industke aecimen book. ",
},
{
id: "02",
title: "our mission",
content:"ggdddg",
},
],
};
Make infonews a computed property. The title and content of each should be the translation keys.
export default {
computed: {
infonews() {
return [
{
id: "01",
title: this.$t("what we do"),
content: this.$t("industke aecimen book"),
},
{
id: "02",
title: this.$t("our mission"),
content: this.$t("ggdddg"),
},
]
};
}
}

Vue-multiselect update field using AJAX

Here is my Vue multiselect component:
<multiselect v-model="selectedcategoryitem"
:options="options"
:multiple="true"
:internal-search="false"
group-values="libs"
group-label="category"
:group-select="true"
placeholder="Type to search"
track-by="value"
label="name"
v-on:select="toggleSelected">
<span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
</multiselect>
And data and method:
data: { selectGoalID: 0
, selectedcategoryitem: []
, queryData: []
, options: [
{
value: 1,
category: 'item1',
libs: [
{ value: "1_1", name: 'name1(E)' },
{ value: "1_2", name: 'name2(P)' },
{ value: "1_3", name: 'name3(T)' },
{ value: "1_4", name: 'name4(F)' },
{ value: "1_5", name: 'name5' },
]
},
{
value: 2,
category: 'item2',
libs: [
{ value: "2_1", name: 'name1' },
{ value: "2_2", name: 'name2' }
]
},
{
value: 3,
category: 'item3',
libs: [
{ value: "3_1", name: 'name1' },
{ value: "3_2", name: 'name2' },
{ value: "3_3", name: 'name3' },
{ value: "3_4", name: 'name4' },
{ value: "3_5", name: 'name5' },
]
},
}
, methods: {
UpdateType: function (goal_id, selectedTypes) {
return $.ajax({
method: "POST"
, url: "#Url.Action("UpdateType", "Predict")"
, data: {
_goal_id: goal_id,
_selectedTypes: selectedTypes
}
, success: function (result) {
if (result.code == "S") {
}
else {
alertResultModel(result);
}
},
error: function (xhr, ajaxOptions, thrownError) {
alertInfo(xhr.statusText);
}
});
}
, toggleSelected: function (value) {
if (value.length > 0) {
this.queryData = JSON.parse(JSON.stringify(value));
}
console.log(this.queryData);
this.UpdateType(this.selectGoalID, this.queryData).then(
function (result) {
if (result.code == "S") {
}
else {
alertResultModel(result);
}
}
, function () {
alertError("Error!!");
}
);
}
}
And when I selected single item, console log return: null,
When i selected multiple items console log return:
(2) [{…}, {…}, __ob__: Observer]
0: {__ob__: Observer}
1: {__ob__: Observer}
length: 2
__ob__: Observer {value: Array(2), dep: Dep, vmCount: 0}
[[Prototype]]: Array
Question is:
Why first selected item is null, but v-model="selectedcategoryitem" selectedcategoryitem.length is 1.
How to convert value to JSON format send to Backend.
Step 1: Create an HTML template
<div id="app">
<multiselect
v-model="selectedcategoryitem"
:options="options"
:multiple="true"
:internal-search="false"
group-values="libs"
group-label="category"
:group-select="true"
placeholder="Type to search"
track-by="value"
label="name"
#input="onChange"
>
</multiselect>
<p>Selected Item: {{ selectedcategoryitem }}</p>
</div>
Step 2: Model data like,
data() {
return {
selectGoalID: 0,
selectedcategoryitem: [],
options: [
{
value: 1,
category: "item1",
libs: [
{ value: "1_1", name: "name1(E)" },
{ value: "1_2", name: "name2(P)" },
{ value: "1_3", name: "name3(T)" },
{ value: "1_4", name: "name4(F)" },
{ value: "1_5", name: "name5" },
],
},
{
value: 2,
category: "item2",
libs: [
{ value: "2_1", name: "name1" },
{ value: "2_2", name: "name2" },
],
},
{
value: 3,
category: "item3",
libs: [
{ value: "3_1", name: "name1" },
{ value: "3_2", name: "name2" },
{ value: "3_3", name: "name3" },
{ value: "3_4", name: "name4" },
{ value: "3_5", name: "name5" },
],
},
],
};
},
Step 3: Create an methods and call REST API call
methods: {
UpdateType: function (goal_id, selectedTypes) {
console.log("selectedTypes", selectedTypes);
return $.ajax({
method: "POST",
url: "#Url.Action('UpdateType', 'Predict')",
data: {
_goal_id: goal_id,
_selectedTypes: JSON.stringify(selectedTypes),
},
success: function (result) {
alert(result);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
},
});
},
onChange() {
console.log("this.selectedcategoryitem", this.selectedcategoryitem);
this.UpdateType(this.selectGoalID, this.selectedcategoryitem).then(
function (result) {
alert(result);
},
function () {
alert("Error!!");
}
);
},
},
You can see the logs for selected items and AJAX call form data
DEMO Link

How to track changes in a property stored in Vuex(store) and perform some method based on the value?

I'm trying to change the links based on the variable user_role which is stored in Vuex(store). I'm not able to find an appropriate way to track the change and based on its value I want to perform some method. Any suggestions on how to do it?
------------------------------store.js-------------------------------
export default new Vuex.Store({
state: {
user_role: "User"
},
mutations: {},
actions: {},
modules: {}
});
-----------------------------------component.vue---------------------------
export default {
name: "Navbar",
data() {
return {
links: [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "", route: "" },
{ text: "Resources", route: "/resources" }
],
pers_actions: ["Profile", "LogOut"],
};
},
watch: {
user_role: {
if (user_role === "PM") {
this.links[2] = {
text: "Allocations",
route: "/allocations"
};
} else if (user_role === "PMO") {
this.links[2] = {
text: "Reports",
route: "/reports"
};
} else if (user_role === "User") {
this.links = [
{
text: "Allocations",
route: "/allocations"
}
];
}
}
},
Rather than explicitly mutating your local data in response to some state change, it is better to compute your links within a computed property because it will automatically update whenever some dependent data has changed. It'll "just work".
computed: {
links() {
switch (this.$store.state.user_role) {
case: "PM": return [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "Allocations", route: "/allocations" },
{ text: "Resources", route: "/resources" },
];
case: "PMO": return [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "Reports", route: "/reports" },
{ text: "Resources", route: "/resources" },
];
// For any other role
default: return [
{ text: "Allocations", route: "/allocations" },
];
}
}
}

Vue - Deep watch change of array of objects, either if a object is added of modified

I'm trying to create an element in vue.js, so that when I update my cart it will show a warning with the item added/updated to cart. So if I add a new car, it would show that last car added.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3},
{ name: 'Mustang', quantity: 1}
]
will show
<div>
You have 1 x Mustang in Cart
</div>
But if I update the quantity of a car that was already in the cart, it will show that last car updated.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 4}
]
will show
<div>
You have 4 x Toyota in Cart
</div>
So far I made it work based in this answer
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
html
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<body>
<div id="app">
<p>Added to Cart:</p>
<car-component :car="car" v-for="car in cars"></car-component>
</div>
</body>
The point is that now it just detects when a object is already in the cart and changes quantity, but not when there is a new car added. I tried to play with another watcher, but it didn't work. Thanks in advance!
hmm how would I do this?
seems to me we have an array of objects and we are tracking the most recently added or modified object. Sure.
So, I think I'd want to only track the recently modified object and render that.
first the html:
<div id="app">
<p>Added to Cart:</p>
<car-component :car="latestCar"></car-component>
</div>
and the vue instance:
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
],
latestCar: {}
},
methods: {
updateLatestCar(car) {
this.latestCar = car;
//call this method from any other method where updates take place
//so if would be called from your addCar method and your updateCar method
//(which I assume exist even though they are not shown in your code)
}
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
If you are modifying your array of objects via some method that is external to the Vue instance then that will require some additional thought.
But it seems like for this you'd have some methods in the Vue instance methods block like this:
addCar(car) {
this.cars.push(car);
this.updateLatestCar(car);
},
updateCar(index, car) {
this.cars[index] = car;
this.updateLatestCar(car);
}
You could pass the entire cars[] array to <car-component>, and allow the component to determine which element of cars[] to display a message about:
In car-component, add a prop (typed for safety) to hold the passed-in cars[]:
Vue.component('car-component', {
// ...
props: {
cars: Array
},
}
Add two data properties:
* `car` - the current car.
* `copyOfCars` - the last known copy of `cars[]`, used to determine which array element has changed. *Note: While watchers are provided both the old and new values of the watched property, the old value does not actually indicate the previous value for arrays of objects.*
Vue.component('car-component', {
//...
data() {
return {
car: {},
copyOfCars: undefined, // `undefined` because we don't need it to be reactive
};
},
}
Define a method (e.g., named findActiveCar) that determines which element in a given cars[] is most recently "active" (newly added or modified).
Vue.component('car-component', {
// ...
methods: {
/**
* Gets the newest/modified car from the given cars
*/
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
// Assume the last item of `newCars` is the most recently active
let car = newCars[newCars.length - 1];
// Search `newCars` for a car that doesn't match its last copy in `oldCars`
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
}
Define a watcher on the cars property that sets car to the new/modified item from findActiveCar().
Vue.component('car-component', {
// ...
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true, // watch subproperties of array elements
immediate: true, // run watcher immediately on this.cars[]
}
},
}
Vue.component('car-component', {
props: {
cars: Array,
},
data() {
return {
car: {},
copyOfCars: undefined,
}
},
template: `<div>You have {{car.quantity}} x {{car.name}} in Cart</div>`,
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true,
immediate: true,
}
},
methods: {
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
let car = newCars[newCars.length - 1];
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
});
new Vue({
el: '#app',
data: () => ({
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}),
methods: {
addCar() {
this.cars.push({
name: 'Mustang', quantity: 1
})
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<h1>Added to Cart</h1>
<button #click="addCar">Add car</button>
<ul>
<li v-for="(car, index) in cars" :key="car.name + index">
<span>{{car.name}} ({{car.quantity}})</span>
<button #click="car.quantity++">+</button>
</li>
</ul>
<car-component :cars="cars" />
</div>

Apollo-Client | No result from query when using certain fields

I'm trying to use apollo-client in my react-native app but for some reason I can only get results from queries when I use certain fields.
Here's my first query :
`query RootQueryType($page: Int!) {
events(page: $page) {
title
}
}`
Working perfectly in RN and GraphiQL but as soon as I add or use an other field than title I don't get any result from the query in RN. It's working perfectly in GraphiQL and there's no error at all.
For example :
`query RootQueryType($page: Int!) {
events(page: $page) {
description
}
}`
Here's my event type :
const EventType = new GraphQLObjectType({
name: 'EventType',
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
category: { type: GraphQLString },
description: { type: GraphQLString },
terminated: { type: GraphQLBoolean },
coverUrl: { type: GraphQLString },
startDate: { type: GraphQLString },
endDate: { type: GraphQLString },
price: { type: GraphQLFloat },
website: { type: GraphQLString },
ticketsUrl: { type: GraphQLString },
geometry: { type: GraphQLString },
participantsCount: { type: GraphQLInt },
participants: {
type: new GraphQLList(UserType),
resolve(parentValue) {
return Event.findParticipants(parentValue.id);
}
}
})
});