How to show Different data in UI then Backend Data - javascript

I am new to angular. From Backend i am getting space data as attached in console image.
<div class="row">
<div class="col-12 mt-3 pr-0 overflow-auto d-flex align-items-center">
<div *ngFor="let data of spaces; let i=index;" class="spaceTabs cursorPointer"
[ngClass]="{ active: selectedSpace === data.space }">
<p class="rangeTag">Rs: {{data.range}}</p>
<span (click)="spaceTabHandler(data)">{{data.space | titlecase}}</span>
</div>
</div>
</div>
This is how i am showing space data as data.space in UI.
My requirement is livingroom should display as Living, diningroom as Dining Room .
How can i manipulate data in UI according to requirement.
Any lead would be helpful.

You can use a pipe to transform your output
#Pipe({
name: 'space'
})
export class SpacePipe implements PipeTransform {
transform(value:string, args?:string): any {
switch(value || null) {
case 'livingroom':
return 'Living';
case 'diningroom':
return 'Dining Room';
default:
return value;
}
}
}
<span (click)="spaceTabHandler(data)">{{data.space | titlecase | space}}</span>

you can create a pipe, then use that pipe to show what you want,
like below
import {Pipe} from ‘angular2/core’;
#Pipe({
name: ‘unCamelCase’
})
export class UnCamelCasePipe {
transform(value: string) {
return value
// insert a space between lower & upper
.replace(/([a-z])([A-Z])/g, '$1 $2')
// space before last upper in a sequence followed by lower
.replace(/\b([A-Z]+)([A-Z])([a-z])/, '$1 $2$3')
// uppercase the first character
.replace(/^./,(str) => str.toUpperCase())
}
}
then use it like
<span (click)="spaceTabHandler(data)">{{data.space | unCamelCase}}</span>
make sure you add UnCamelCasePipe to your module providers

Create a viewModel with the fields u need in the front end. Then after the backend call returns populate your data where u set Living Room instead of livingroom. That is usually the correct approach
SO in typescript:
export class DataViewModel{
public SpaceName:string;
public .... other properties you need
}
Then in your angular component in the subscribe to the back end call:
this.mybackendmethod.get().subscribe((backenddata)=>{
const viewModel = new Array<DataViewModel>();
backenddata.foreach((item)=>{
const viewModelItem = new DataViewModel();
switch(item.space){
case 'livingroom':
viewModelItem.SpaceName= 'Living Room';
break;
}
viewModel.push(viewModelItem);
});
});
Something like this.

Related

How to create a comma seperated list from multiple items in angular?

At my angular application, I'm trying to create a comma separated list of values I've got from my API response.
The API response looks like this:
{
"release_date":"2012-03-14",
"some_relation":[
{
"id":"2604ebbf-4eb5-46e3-89d8-ab4e8ecc8275",
"name":"ABC"
},
{
"id":"5267a0c6-9423-4e28-a413-133cc3435612",
"name":"DEF"
},
{
"id":"13d1454a-fc0e-457c-9f8e-9a9952984d8c",
"name":"GHI"
}
],
}
some_relation in nested, so I created an interface for this logic to work:
export interface SomeResponse {
some_relation: some[];
}
export interface some {
id: string;
name: string;
}
At my html template, I now do this:
<div *ngFor="let some of some_response.some_relation">
<small>{{ some.name }}</small>
</div>
Which returns the following output:
ABC
DEF
GHI
Instead of multiple lines outputted, I would like to have it that way:
ABC, DEF, GHI
How can this be accomplished?
Thanks in advance.
You can do it like this:
Change div for span so it would not put each item in new line
Add , only if it's not the last item
<span *ngFor="let item of some_response.some_relation; let last = last">
<small>{{ item.name }}</small><span *ngIf="!last">, </span>
</span>
That's because you are using *ngFor on division tag instead of small tag and it is creating 3 division with a small tag in each division. Division is a block element and each division is rendered on the next line. Try doing something like this for single line rendering:
<small *ngFor="let some of some_response.some_relation" >
{{ some.name }}
</small>
Edit: Since you want the comma in between you can use this:
<small>
{{ some_response.some_relation.reduce((total, obj, index) => index? total + ", " + obj.name: obj.name, "") }}
</small>

Force change detection inside template for an object part of an array of objects inside an array of objects

Whatever I try, I just cannot seem to trigger change detection inside the template for an object that is part of an array of objects inside an array of objects. This is the (top) object structure:
export interface CompatibleCards {
appid: number,
banned: boolean,
items: { addedToTrade: boolean, appPosition: number, banned: boolean, classid: number, iconUrl: string, marketHashName: string, name: string, numberOfListings: number, position: number, price: number, quantity: number, type: string }[],
name: string,
numberOfCardsInBadge: number,
numberOfDifferentCards: number,
totalNumberOfItems: number
}
And here is the template:
<ng-container *ngIf="this.compatibleCards?.length else no_compatible_cards">
<h3>{{ numberOfCompatibleCards }} cartes compatibles</h3>
<div class="conteneur-ensembles">
<div *ngFor="let currentApp of compatibleCards" class="conteneur-ensemble-compatible">
<h3>{{ currentApp.name }} ({{ currentApp.numberOfDifferentCards }} / {{ currentApp.numberOfCardsInBadge }} cards)</h3>
<div class="conteneur-images-cartes">
<span *ngFor="let currentItem of currentApp.items" title="{{ currentItem.name }}" [ngClass]="{ 'conteneur-carte': true, 'ajoute-echange': currentItem.addedToTrade }" (click)="ajouterRetirerObjetPourEchange(currentItem, 'ajouter')">
<img class="image-carte" src="http://cdn.steamcommunity.com/economy/image/{{ currentItem.iconUrl }}" alt="{{ currentItem.name }}" title="{{ currentItem.addedToTrade }}">
<span *ngIf="currentItem.price && currentItem.price > 0" class="prix-carte" title="{{ currentItem.name }}">{{ currentItem.price / 100 | currency }}</span>
<span *ngIf="currentApp.banned" class="prix-carte" title="{{ currentItem.name }}">--</span>
</span>
</div>
</div>
</div>
</ng-container>
As you can see, I am using the [ngClass] directive to populate the css classes based on the addedToTrade property of the CompatibleCards.items object. It populates properly, meaning that if I change the addedToTrade property before the first render, the class is present. However, when I try to update the object at a later time, using the code below, the template does not show or remove the class.
ajouterRetirerObjetPourEchange(objetAEchanger: any, operation : "ajouter" | "retirer") : void {
console.log(objetAEchanger);
if(operation == "ajouter") {
this.itemsForTrade.push({...objetAEchanger});
} else {
const positionObjetEchangeTrouve = this.itemsForTrade.findIndex((currentItem: any) => currentItem.classid == objetAEchanger.classid && currentItem.position == objetAEchanger.position);
this.itemsForTrade.splice(positionObjetEchangeTrouve, 1);
}
this.compatibleCards[objetAEchanger.appPosition].items[objetAEchanger.position].addedToTrade = ! this.compatibleCards[objetAEchanger.appPosition].items[objetAEchanger.position].addedToTrade;
// this.compatibleCards[objetAEchanger.appPosition].items = [].concat(this.compatibleCards[objetAEchanger.appPosition].items);
// this.compatibleCards = [].concat(this.compatibleCards);
}
Even reassigning the arrays with [].concat (or using spread [...this.compatibleCards] does not update the template (see the comments at the end of the function above).
Is there any way I can force my template to update itself after an update of an object inside the items array?
1st Solution
Try forcing a detect changes programatically.
Use in your compnent's constructor (via Injection) changeDetectorRef: ChangeDetectorRef
and then where you update your table use this.changeDetectorRef.detectChanges()
angular doc
2nd Solution
If that does not work then you can also try a 2nd alternative.
You can declare your component to have a different detection strategy changeDetection:ChangeDetectionStrategy.OnPush
This will be applied on component level like
#Component({
selector: 'my-app',
template: `some template code`,
changeDetection: ChangeDetectionStrategy.OnPush
})
Please take care however as this can have side effects. Detection strategy works from top to bottom. This means that if you declare this strategy on your component all of it's children component will follow this strategy also.
Also on this type of strategy Angular does not detect the changes on primitive fields. It detects changes only on references.
So when you do this.compatibleCards = [...this.compatibleCards] you change the reference of that field so it must be caught as change from Angular.
angular detection strategy doc

Can I search filter through a child component? (Angular)

I've got a parent component that I'm passing data down to a child component with *ngFor / #input. That child component is created x number of times depending on how many objects are in the pciData array
pciData is an array of about 700 data objects, all with a "hostname" property/value. when passed down to app-system, the hostname of that object shows on a button. I want the end user to be able to filter through those buttons by that hostname, only showing the app-system components that match.
On the parent component template, how would I create a search bar that can filter the app-system components by their hostname property values? In the code below, I've tried piping this like:
<ng-container *ngFor="let system of pciData | searchFilter: system.hostname | paginate: { itemsPerPage: 180, currentPage: p }; let i = index ">
but "system" comes back undefined. However, when I just type in {{system.hostname}} in the template under the loop, it does loop through every single object and display the hostname. Any assistance would be appreciated.
Here's the parent component. I've removed the implimentation that I tried to do with the filter to avoid confusion:
import { Observable } from 'rxjs';
import { Ipcidata } from '../Shared/Ipcidata';
import { map, filter, switchMap } from 'rxjs/operators';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
#Component({
selector: 'app-system-status',
templateUrl: './system-status.component.html',
styleUrls: ['./system-status.component.css'],
})
export class SystemStatusComponent implements OnInit {
#Input() pciData: any;
constructor() {}
searchText;
p: number;
filteredValues : any;
ngOnInit() {
}
}
Here's the parent template, "app-system" is what I'm trying to search through the values of :
<ngx-spinner></ngx-spinner>
<section class="app-container">
<div class ="card-container">
<ng-container *ngFor="let system of pciData | paginate: { itemsPerPage: 180,
currentPage: p }; let i = index ">
<div class='cardboi'>
<app-system [systemInput]="system"></app-system>
</div>
</ng-container>
</div>
<div class="pagination-container">
<pagination-controls class='paginator' (pageChange)="p = $event"></pagination-controls>
</div>
</section>
lastly, here is the child template, to which the search bar filter should only show what the user inputs as the hostname. This is essentially just a button that pops up more data about that host when clicked. Again, the point of this is only to show the buttons with a certain hostname.
<button (click)="openDialog()" [ngClass]="{
'btn buttonGood':isValid(),
'btn buttonDateAlert':isValid()=='datewarning',
'btn buttonAlert':isValid()==false
}">{{systemInput.hostname.slice(0,13) | uppercase}}</button>
</div>
Thanks again for anyone who can help me with this.
In your *ngFor loop, the array you are looping though is being piped. The pipe has to be processed before the loop begins. So pciData is piped through the two pipes and then the resulting array is looped through. Which is why system doesn't exist at the time that the pipes are being processed.
My recommendation would be to not use a pipe for the search filters. Instead, create another pciData variable. You can call it something like filteredPciData. Bind to the onChange event of the search box in the parent component. When the search value changes, filter pciData (which should have all values) and use filteredPciData to store the results. Then in your HTML loop through filteredPciData instead of pciData

Facing issue while rendering index value of *ngFor directive in Angular 5

I am facing an issue while rendering index of *ngFor directive for a particular use case as follows.
Lets say we have an array of objects as
this.temp = [
{name:'John',age:24,visibility:'visible',
{name:'Kane',age:26,visibility:'hidden',
{name:'Ross',age:28,visibility:'visible',
{name:'Lui',age:21,visibility:'visible'
]
For rendering this in my app.component.html file I have html as follows
<div *ngFor="let user of temp; let i = index">
<div *ngIf="user.visibility === 'visible' ">
<div>{{i+1}}</div>
<div>{{user.name}}</div>
</div>
</div>
So as per the above array example, it renders users
1.John
2.Ross
3.Lui
Now there is a button name 'Change visibility' against each user in my UI, where in it will toggle the visibility state of user from 'hidden' to 'visible' and viceversa.
So clicking on button mentioned against John, it will set its visibility as
hidden but the UI rendered is
2.Ross
3.Lui
My expected output is
1.Ross
2.Lui
How to make the index render properly ?
The use case here is that I cannot modify/alter my this.temp array in terms of length.Basically I need the entire array with only visiblity property changed in it as per user actions.
Please help.
you can filter array first:
<div *ngFor="let user of temp.filter(us => us.visibility === 'visible'); let i = index">
<div>
<div>{{i+1}}</div>
<div>{{user.name}}</div>
</div>
</div>
like this way, you dont analize all array items too, more efficient and desired output.
Cheers!
You can also achieve your required result by using Pipe like this
HTML component
<div *ngFor="let user of temp | visiblefilter ; let i=index">
<span>{{i+1}} {{user.name}}</span> <button name={{user.name}} (click)="onHide($event)">Hide</button>
</div>
PIPE
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'visiblefilter',
pure: false
})
export class VisibleFilterPipe implements PipeTransform {
transform(items: any[]): any {
return items.filter(({visibility}) =>visibility=='visible');
}
}
You can check here with working example stackblitz
use a custom trackby function :
*ngFor="let user of temp; let i = index; trackBy: customTB"
customTB(index, item) {
return index + ' - ' item.name;
}

Adding html in Vue.js using data filters?

I am trying to use the Filter feature in Vue.js to add html tags inside a String, the documents suggests this should be somehow feasible but I'm getting nowhere. The point is the data should just a String that's brought into the html and before it's mounted the filter should search the data for key words (e.g. 'See REFERENCE') and the REFERENCE word should be turned into an anchor link.
E.g.
<p>{{String | filterFunction}}</p>
Instead of piping out say:
<p>The text string with a link</p>
It should pipe out the string but with a node insert.
<p>The text string with a link</p>
The Vue documentation suggests javascript component assemblage is possible but so far the testing has gone poorly.
Filters only replace as text. Since you are trying to transform plain text in HTML, you'll have to resort to v-html or equivalent. Check your options in the demo below.
function _linkify(text) {
return text.replace(/(https?:\/\/[^\s]+)/g, '$1');
}
Vue.filter('linkify', function (value) {
return _linkify(value)
})
Vue.component('linkify', {
props: ['msg'],
template: '<span v-html="linkifiedMsg"></span>',
computed: {
linkifiedMsg() { return _linkify(this.msg); }
}
});
Vue.component('linkify-slot', {
render: function (h) {
let html = _linkify(this.$slots.default[0].text);
return h('span',{domProps:{"innerHTML": html}})
}
});
new Vue({
el: '#app',
data: {
message: 'The text string with a http://example.com'
},
methods: {
linkifyMethod(text) {
return _linkify(text); // simply delegating to the global function
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>Doesn't work: {{ message | linkify }}</p>
<p v-html="$options.filters.linkify(message)"></p>
<p :inner-html.prop="message | linkify"></p>
<p v-html="linkifyMethod(message)"></p>
<p><linkify :msg="message"></linkify></p>
<p><linkify-slot>{{ message }}</linkify-slot></p>
</div>

Categories