Binding issue in repeat.for in aurelia - javascript

I have a list of cards that i populate, when i click on each card i want to get and display the correct items displayed for that card.
The problem i am facing is that when i return an array its not binding the correct item to the specific card.
This is my HTML
<div repeat.for="Grouping of categoryItems">
<div class="row">
<div class="col s12 m3 l3">
<div class="card blue-grey darken-1">
<div class="card-content" style="padding:10px">
<span class="card-title white-text truncate">${Grouping.name}</span>
<a if.bind="Grouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="Activate(list, Grouping)"><i class="material-icons">add</i></a>
</div>
</div>
</div>
</div>
<div repeat.for="categoryGroupingTypes of categoryItemTypes">
<div class="row" if.bind="!Grouping.hideDetails">
<div class="col" style="position:absolute;padding:5%">
<div class="rotate-text-90-negative">
<span class="blue-grey-text"><b>${categoryGroupingTypes.name}</b></span>
</div>
</div>
<div repeat.for="item of categoryItemsTypes.items" class="col s12 m3 l3 ">
<div class="card-content">
<span class="card-title white-text truncate">${items.Name} ${items.Quantity}</span>
</div>
</div>
</div>
</div>
</div>
</div>
my ts
async Activate(list: ListModel[], grouping: any) {
this.categoryItemsTypes = await this.getTypes(list);
let result = this.categoryItem.filter(x => x.name == grouping.name)
if (result) {
grouping.hideDetails = false;
}
}
so this.categoryItemsTypes has the following array
0: {name: "Clothes", items: Array(3)}
1: {name: "Shoes", items: Array(2)}
so when the page loads it loads the cards as follows
then when i click on "Clothes"
i only want it to load the array associated with clothes and if "shoes" is clicked then only load that array as follows
but what is currently happening with my above code is the following
this line is where i bind the items
repeat.for="item of categoryItemsTypes.items"
how can i bind the items to the correct ${Grouping.name} as shown in picture 2?

You are in the right direction, but not complete yet.
Array assignment are not observed by aurelia.
In general, when populating arrays with a new set of elements, this is a good way:
destarray.splice(0, destarray.length, ...sourcearray);
over
destarray = sourcearray;
because the former is observed by aurelia by default and the latter is not.

Related

Javascript array with multiple objects - send to GTM - Google data layer

I have multiple products on a page each with several infos and i need to send them to google data layer on page load within an array of objects.
My script the way it is is sending one array with a object with all fields on it, what i need is to have that same array with an object per product. Here's my code and hope someone can help me. Thanks
$(function () {
var Container = (".product");
var itemName = $($(Container)).find(".item-name").text();
var itemId = $($(Container)).find(".item-id").text();
var itemPrice = $($(Container)).find(".item-price").text();
var itemBrand = $($(Container)).find(".item-brand").text();
var itemCategory = $($(Container)).find(".item-category").text();
window.dataLayer.push({
event: 'Ecommerce - Item List Views',
event_name: 'view_item_list',
view_item_list: {
items: [{
item_name: itemName,
item_id: itemId,
price: itemPrice,
item_brand: itemBrand,
item_category: itemCategory,
}]
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="product">
<div class="item-name">
<h1>Nike blue shoes</h1>
</div>
<div class="item-id">
SKU-3456
</div>
<div class="item-price">
€22.00
</div>
<div class="item-brand">
Nike
</div>
<div class="item-category">
Shoes
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Adidas red shoes</h1>
</div>
<div class="item-id">
SKU-4335
</div>
<div class="item-price">
€55.00
</div>
<div class="item-brand">
Adidas
</div>
<div class="item-category">
Shoes
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Nike Yellow Sandals</h1>
</div>
<div class="item-id">
SKU-9654
</div>
<div class="item-price">
€11.00
</div>
<div class="item-brand">
Nike
</div>
<div class="item-category">
Sandals
</div>
</div>
<div class="product">
<div class="item-name">
<h1>Vans Black Sneakers</h1>
</div>
<div class="item-id">
SKU-362364
</div>
<div class="item-price">
€99.00
</div>
<div class="item-brand">
Vans
</div>
<div class="item-category">
Sneakers
</div>
</div>
</div>
Instead of pushing right away, try putting the object into a variable first:
const dlObject = {
event: 'Ecommerce - Item List Views',
event_name: 'view_item_list',
view_item_list: {
items: [{
item_name: itemName,
item_id: itemId,
price: itemPrice,
item_brand: itemBrand,
item_category: itemCategory,
}]
}
}
now you can enrich it by adding more stuff into it:
dlObject.view_item_list.items.push({
item_name:'second item',
item_id:'qweqwe',price:"15",
item_brand:"foo",
item_category:"bar"
});
finally, once you're done pushing products to the items array, you push the whole thing in:
window.dataLayer.push(dlObject);

How to pass an object from ngFor to the component in angular?

I'm new to Angular and I can't really understand what is the problem I'm facing. I'm trying to pass object from an *ngFor to his component.
Here is a simple *ngFor that iterate an Array
<div class="card text-center" *ngFor="let alarm of alarms">
<div class="card-header">
....
</div>
<div class="card-body p-2">
<span class="text-secondary font-weight-bold">
Floor
<span>{{ alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].floor }}</span>
</span>
</div>
</div>
I want to avoid this long interpolation so I want pass the current let alarm to the component and assign three attributes to an object and display it,
customMethod(alarm) {
this.location.floor = alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].floor;
this.location.room = alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].room;
this.location.bed = alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].bed;
}
Here is what I expect:
<div class="card text-center" *ngFor="let alarm of alarms">
<div class="card-header">
....
</div>
<div class="card-body p-2">
<span class="text-secondary font-weight-bold">
Floor
<span>{{ location.floor }}</span>
</span>
</div>
</div>
And show the attribute's object location with string interpolation for each object alarm.
What is the best way to do this?
You want to avoid having any logic in your template (.html) file so I'll advise doing the logic in your component file.
You can prepare the alarms array before it's sent into the template.
#Component(..)
export class SomeComponent {
private _alarms;
#Input()
set alarms(alarms) {
this._alarms = alarms.map(alarm => customMethod(alarm))
}
get alarms() {
return this._alarms
}
}
function customMethod(alarm) {
return {
...alarm, // <<< remove this if you just want locations
locations: {
floor: alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].floor,
room: ....,
bed: ...
}
}
Then in your template
<div class="card text-center" *ngFor="let alarm of alarms">
<div class="card-header">
....
</div>
<div class="card-body p-2">
<span class="text-secondary font-weight-bold">
Floor
<span>{{ alarm.location.floor }}</span>
</span>
</div>
</div>
Create a helper method to get you the value you want to display, and then just call that from the template.
You can do anything to the object to get the value you want to display in here since it's just a funciton:
createMyString(alarm) {
return alarm.sensor.sentinel.monitorings_sentinels[0].monitoring.patient.location[0].floor;
}
Then in your template
{{createMyString(alarm)}}

how to rendering data from api in vue.js

I am new to vue.js I am going to make a news app using a API.
In here I used v-for to iterate some codes. I think there is a problem in v-for. because it gives error. error has been included end of this question.
I used following codes to show search results
<template>
<div class="col-md-12">
<div>
<h2>Results</h2>
<div class="card mb-3" v-for="item in resultList">
<img class="card-img-top" src="" alt="Card image cap" height="100" width="200">
<div class="card-body">
<h5 class="card-title">{{ item.name }}</h5>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="button">
<button type="button" class="btn btn-primary">Show more</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: ['resultList']
}
</script>
following codes used to get the api data(search api data)
<template>
<div class="container1">
<div class="form-group row">
<label for="exampleInputPassword1">Search Music</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="exampleInputPassword1" placeholder="Type here"
#input="keypressed">
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default{
data () {
return {
newset: []
}
},
methods: {
keypressed () {
var key = event.target.value
axios.get('http://newsapi.org/v2/everything?q=' + key +
'&sortBy=publishedAt&apiKey=b5ac77726d0a4460bd82b57210b2efb7')
.then(response => {
this.newset = response.data.articles
})
.catch(e => {
this.error.push(e)
})
this.$emit('newDataset', this.newset)
}
}
}
</script>
but it gives an error. error is
https://google.com/#q=vue%2Frequire-v-for-key Elements in iteration expect to have 'v-
bind:key'
directives
src\components\ResultArea.vue:5:5
<div class="card mb-3" v-for="item in resultList">
^
You need to add :key binding:
<div class="card mb-3" v-for="(item, index) in resultList" :key="index">
key value should be unique for every item. If your list items has any id it is good idea to use this id here:
<div class="card mb-3" v-for="item in resultList" :key="item.id">
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. check the link for better understanding
https://v2.vuejs.org/v2/guide/list.html#Maintaining-State

get data from html page using javascript

I have some HTML - pretty nasty, but not mine and so I don't have control over it. I need to extract some data from the form, the First name value (ABDIGANI) and the Surname value (AHMED). What is the best way to do this with javascript?
<div class="voffset3"></div>
<div class="container well panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-3">
<span class="ax_paragraph">
First name
</span>
<div class="form-group">
<div class="ax_h5">
ABDIGANI
</div>
</div>
</div>
<div class="col-md-3">
<span class="ax_paragraph">
Surname
</span>
<div class="form-group">
<div class="ax_h5">
AHMED
</div>
</div>
</div>
</div>
</div>
</div>
</div>
You could consider HTML in most cases well structured. Try this the following snippet.
Edit: did a change due to the first comment.
Edit: if you have more than one rows, you should use
document.querySelectorAll('.container > .panel-body > .row');
and fetch the pairs for each found element as below.
const markers = ['First name', 'Surname'];
const mRx = [new RegExp(markers[0]), new RegExp(markers[1])];
function findMarker(element) {
for(let i = 0; i < mRx.length; i++) {
if(element.innerHTML.match(mRx[i])) {
return markers[i];
}
}
return null;
}
function findValue(el) {
return el.parentElement.querySelector('.form-group > div').innerHTML.trim();
}
const pairs = [... document.querySelectorAll('.ax_paragraph')]
.map(el => {
return {el: el, mk: findMarker(el)};
})
.filter(n => n.mk !== null)
.map(o => {
return {key: o.mk, value: findValue(o.el)};
});
console.log(pairs);
var x = document.querySelectorAll(".panel-body > div >.col-md-3 > div > div");
x.forEach(myFunction);
function myFunction(item, index) {
//console.log(item.innerHTML.trim());
if (index===0){
console.log("First name : "+item.innerHTML.trim());
}
if (index===1){
console.log("Surname : "+item.innerHTML.trim());
}
}
<div class="voffset3"></div>
<div class="container well panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-3">
<span class="ax_paragraph">
First name
</span>
<div class="form-group">
<div class="ax_h5">
ABDIGANI
</div>
</div>
</div>
<div class="col-md-3">
<span class="ax_paragraph">
Surname
</span>
<div class="form-group">
<div class="ax_h5">
AHMED
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Check this
const firstName = document.querySelector('.row .form-group div').textContent.trim();
const surname = document.querySelector('.row > div:last-child .form-group div').textContent.trim();
note: Its better to change html according to functionality needs, like if you need firstname then you must keep an id attribute to div which is having first name, same goes to surname. then select those fields using id selector, because even if you change html page structure in future, functionality will not get effected.
Check below for reference on how the html should actually be(just to make sure you know it, but the solution you are seeking is above in first two lines)
eg:
<div class="voffset3"></div>
<div class="container well panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-3">
<span class="ax_paragraph">
First name
</span>
<div class="form-group">
<div class="ax_h5" id="firstNameField">
ABDIGANI
</div>
</div>
</div>
<div class="col-md-3">
<span class="ax_paragraph">
Surname
</span>
<div class="form-group">
<div class="ax_h5" id="surnameField">
AHMED
</div>
</div>
</div>
</div>
</div>
</div>
</div>
document.querySelector('.form-group > div').textContent but without modifying the html there is no way to distinguish first name and surname.
If you can't edit the HTML, you can use the XPATH for Example.

dynamically add classes to content coming from *ngFor from an array

Im not quite sure something like this is possible but say in my html component I have an ngFor like so..
<div *ngFor="let card of cards">
... stuff in here
</div>
now say I have an array of classNames like so
classNames = [
'red',
'yellow',
'blue',
'green'
]
and inside my *ngFor I have a div like so
<div *ngFor="let card of cards">
<div [class]='...'>
<div class="card">
</div>
</div>
</div>
basically what I want to happen is for every item in the ngFor give loop through the classNames array and dynamically add it to the incoming data so for example
say I have 6 items in cards so each card needs a classname so it loops through classNames and gives it a class so like this..
<div [class]='red'>
<div class="card">
</div>
</div>
<div [class]='yellow'>
<div class="card">
</div>
</div>
<div [class]='blue'>
<div class="card">
</div>
</div>
<div [class]='green'>
<div class="card">
</div>
</div>
<div [class]='red'>
<div class="card">
</div>
</div>
<div [class]='yellow'>
<div class="card">
</div>
</div>
and so on and so forth..
how could i accomplish something like this?
EDIT
component.html
<div class="card" *ngFor="let card of cards; let i = index">
<div [class]="classNames[i%classNames.length]">
....
</div>
</div>
component.ts
export class...
classNames = [
'light-green',
'dark-green',
'aqua',
'blue',
'blue-purple',
'purple',
'purple-pink',
'purple-orange'
];
You can leverage remainder (%) operator to achieve that:
<div *ngFor="let card of cards; let i = index">
<div [class]="classNames[i%classNames.length]">
<div class="card">
{{ card }}
</div>
</div>
</div>
Ng-run Example
Update:
You should define array as follows:
classNames = [
'light-green',
'dark-green',
'aqua',
'blue',
'blue-purple',
'purple',
'purple-pink',
'purple-orange'
];
Note: i use = instead of :
Instead of randomly applying any class to any card or deciding it on view based on some %, the best way, I believe would be read it from the Cards object itself, since it is logical to have all details of a card read from the card itself.
So that view is independent of those extra stuffs.
classNames = ['red','yellow','blue','green'];
cards = [{text: 1, class: this.classNames[0]},{text: 2, class: this.classNames[1]}];
your view should simply do its task (render)
<div *ngFor="let card of cards; let i = index">
<div [class]="card.class">
<div class="card">
{{ card.text}}
</div>
</div>
</div>

Categories