I need to remove an ionic 2 card when someone clicks on the trash icon. The code I have so far gives a querySelector is null error when I run it.
The view html looks like this:
<ion-card *ngFor="#mediaItem of mediaItems" id="item{{mediaItem.id}}" class="media-item">
<ion-card-header class="title-header">
<div class="title-item">
{{mediaItem.title}}
</div>
<ion-icon name="trash" class="bookmark_trash_icon" (click)="removeItem(mediaItem.id)"></ion-icon>
</ion-card-header>
<ion-card-content class="outer-content" >
<img src='{{mediaItem.url}}'>
</ion-card-content>
<ion-item class="bookmark-media-item">
<!-- <button (click)="topup(mediaItem)" clear item-left> -->
<div style="float:left;">
<ion-icon name="heart"></ion-icon>
{{mediaItem.liked}}
<ion-icon name="close-circle"></ion-icon>
{{mediaItem.disliked}}
</div>
<a (click)="showUserProfile(mediaItem.owner, mediaItem.username)" style="float:left;">
{{mediaItem.username}}
</a>
<div style="float:right;">
<img src="img/tiny-v.png" class="bookmark-v-icon">
{{mediaItem.credits_left}}
</div>
</ion-item>
</ion-card>
My controller javascript code looks like this:
removeItem(theItemID) {
let cardToHide = '#item'+theItemID;
document.querySelector(cardToHide).style.display = 'none';
}
The error I get is
querySelector is null
You can send the object instead of the id by changing that part of the code:
(click)="removeItem(mediaItem)"
And then find that object in you mediaItems array and delete it from there. Then your view will be automatically updated.
First get the index of that object in the array, and then you can use the mediaItems.splice(index, 1) to delete it from the array:
removeItem(theItem) {
let index = this.mediaItems.indexOf(theItem);
this.mediaItems.splice(index,1);
}
Related
Using Angular 13, I have been trying to add a menu using Angular MatMenu (https://material.angular.io/components/menu/overview) which is conditionally shown. Basically a bar shows button (operations) and some may have suboperations. In this case I want to display the suboperations when clicking on the button.
Here is the basic code I have, which is the complete html since there is another menu in the beginning. Note that removing it does not change the behavior.
<div class="rounded-box" *ngIf="!!selectedCategory">
<div class="selector" *ngIf="rateOrDislocationEnabled; else simpleAggregation">
<button color="primary" mat-button mat-icon-button [matMenuTriggerFor]="categoryMenu" class="dropdownButton">
<div class="dropdownText">{{ selectedCategory | agReplace: "_":" " }}</div>
<mat-icon class="dropdownIcon">arrow_drop_down</mat-icon>
</button>
<mat-menu #categoryMenu="matMenu" [overlapTrigger]="true" data-testid="categories-menu">
<button mat-menu-item *ngFor="let category of availableCategories" (click)="setSelectedCategory(category)">
<span [ngClass]="{ selectedLabel: isSelected(category) }">{{ category | agReplace: "_":" " }}</span>
<div *ngIf="category === BlockCategory.RATE_MAKING" class="alpha">ALPHA</div>
</button>
</mat-menu>
</div>
<ng-container *ngFor="let operationCategory of getOperations(); let lastItem = last">
<ng-container *ngFor="let operation of operationCategory">
<button *ngIf="operation.subOperations.length === 0"
mat-icon-button
class="iconWrapper"
id="operation-icon-{{ operation.value }}"
(click)="addOperation(operation.value)"
[disabled]="operation.disabled$ | async">
<mat-icon [ngClass]="operation.icon" class="icon" [agToolTip]="operation.tooltip$ | async"></mat-icon>
</button>
<ng-container *ngIf="operation.subOperations.length !== 0">
<button
mat-button
mat-icon-button
class="iconWrapper"
id="operation-menu-icon-{{ operation.value }}"
[matMenuTriggerFor]="subMenu">
<mat-icon [ngClass]="operation.icon" class="icon" [agToolTip]="operation.tooltip$ | async"></mat-icon>
</button>
<mat-menu #subMenu="matMenu">
<button mat-menu-item>Settings</button>
<button mat-menu-item>Log off</button>
</mat-menu>
</ng-container>
</ng-container>
<div class="divider" *ngIf="!lastItem"></div>
</ng-container>
</div>
I have created a stackblitz reproducing the issue: https://angular-ivy-cvv2xk.stackblitz.io
The issue is that when I click on the button, nothing happens. When I move the button out of the ngFor loops though, it works properly. I have tried things such as removing the ngIf condition (so all buttons are doubled) and none show the menu when clicking on it.
So the "submenu" is never displayed.
I wonder if I need to make the mat-menu specific or give it some id to ensure there are no conflicts? Since I'm a bit new to Angular I maybe be missing something.
Thanks in advance
Your button which triggers submenu got multiple button tags.
<button
mat-button
mat-icon-button
Can you remove one and try again?
Edit:
You use <ng-container *ngFor="let operationCategory of getOperations();
to populate your array.
If you press your button, angular will trigger change detection and this will trigger again your getOperations().
This will lead to this behaviour and dosent open your submenu.
So you should try to replace getOperations() with async pipe (if this is the case) or use properties.
I use the [#ng-select/ng-select][1] component many places in my software. It works well, it searches by a backend method as the user types and shows the results for selection.
Now I would like to let the user to create new items that are not on the list. How can I do this with an async list? productNames is an Observable.
<ng-select [name]="'productname'+index$" [items]="productNames | async"
[attr.id]="'productname'+index$"
[(ngModel)]="detail.productNameNew"
[typeahead]="productNameInput"
(change)="productNameChanged($event, detail)"
(keydown)="productNameKeyPress($event, detail)"
[disabled]="currentSuggestion.acceptUser === null ? null : true"
[clearable]="false">
<ng-template ng-label-tmp let-item="item">
{{item.productName}}
</ng-template>
<ng-template ng-option-tmp let-item="item">
<div class="noproduct-item">
<div class="noproduct-head">
<div class="productname">{{item.productName}}</div>
<div class="description">{{item.description}}</div>
</div>
<div class="noproduct-details">
<div class="headid">
<span class='glyphicon glyphicon-send'></span>
{{item.suggestionHeadId}}
</div>
<div class="createduser">
<span class='glyphicon glyphicon-user'></span>
{{item.createUser?.name}}
</div>
<div class="createddate">
<span class='glyphicon glyphicon-calendar'></span>
{{item.createDate | date: 'yyyy.MM.dd.'}}
</div>
</div>
</div>
</ng-template>
</ng-select>
Add a second observable to capture the user added products and then use combineLatest to merge them. Subscribing to the combined list will produce a new list that you can use in the ng-select.
https://stackblitz.com/edit/angular-rxjs-combinelatest
I am making an internal live chat system using Firebase. I make this call to get a list of all chat messages:
firebase.database().ref('chatrooms/'+this.roomkey+'/chats').on('value', resp => {
this.chats = [];
this.chats = snapshotToArray(resp);
setTimeout(() => {
if(this.content._scroll) { this.content.scrollToBottom(0); }
}, 1000);
});
In my view I have this HTML to loop through the chats:
<ion-content>
<ion-list>
<ion-item *ngFor="let chat of chats" no-lines>
<div class="chat-status" text-center *ngIf="chat.type==='join'||chat.type==='exit';else message">
<span class="chat-date">{{chat.sendDate | date:'short'}}</span>
<span class="chat-content-center">{{chat.message}}</span>
</div>
<ng-template #message>
<div class="chat-message" text-right *ngIf="chat.user === nickname">
<div class="right-bubble">
<span class="msg-name">Me</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
<div class="chat-message" text-left *ngIf="chat.user !== nickname">
<div class="left-bubble">
<span class="msg-name">{{chat.user}}</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
</ng-template>
</ion-item>
</ion-list>
</ion-content>
And here is my relevant CSS:
ion-content {
height: 100%;
overflow: hidden;
.item-md, .item-ios {
background: none;
}
}
There are currently 30 chat messages. They are loaded from Firebase and then after a second, when the setTimeout finishes, the user is automatically scrolled to the bottom of the page so they can see the most recent message. This makes the page load a bit odd with the jump down to the bottom of the page after a second.
Is there any way to replace the timeout with something that will achieve the goal of the user initially seeing the most recent messages? Maybe there is some trigger that can be made that detects that chats has changed in the view and then does the scroll? This would seem better than a fixed 1 second pause?
You could scroll to the bottom of the list when changes to the list of chats are detected:
Associate a template reference variable to the ion-item elements (e.g. #chatItems)
Use ViewChildren to get the QueryList of ion-item elements
In ngAfterViewInit, subscribe to the QueryList.changes event
Scroll to the bottom of the list when the list changes
<ion-content>
<ion-list>
<ion-item #chatItems *ngFor="let chat of chats" no-lines>
...
</ion-item>
</ion-list>
</ion-content>
export class MyComponent {
#ViewChildren("chatItems") chatItems: QueryList<any>;
ngAfterViewInit() {
this.scrollContentToBottom();
this.chatItems.changes.subscribe(() => {
this.scrollContentToBottom();
});
}
scrollContentToBottom() {
if(this.content._scroll) {
this.content.scrollToBottom(0);
}
}
...
}
Note: I used QueryList<any> because I don't know the ion-item component type. You can put the appropriate type if you know what it is.
I am displaying users in a list with their photo, name etc... and then I am grabbing the value of the element that has the id "uid" :
$scope.toUserView = function() {
var uid = document.getElementById('userUID').textContent;
console.log(uid);
$state.go('tab.userview');
};
Problem is that whoever I click on, I always get the reference of the FIRST user in the list of the page and I would like to grab only the one I click on.
I tried to add .click() at the end of my var but it is not a function.
Any idea ?
EDIT : Corresponding HTML :
<ion-list class="item-border-off list-fav">
<a ><ion-item ng-click="toUserView()" ng-repeat="user in users" class="item-remove-animate item-avatar">
<img ng-src="{{user.photoURL}}">
<h2>
{{user.name}}
</h2>
<p >{{user.description}}</p>
<p id="userUID">{{user.uid}}</p>
<!-- button options -->
<ion-option-button>
<i class="icon ion-ios-trash-outline"></i>
</ion-option-button>
<ion-option-button>
<i class="icon ion-ios-chatbubble-outline"></i>
</ion-option-button>
</ion-item></a>
</ion-list>
Corresponding HTML
<a ><ion-item ng-click="toUserView($index)" ng-repeat="user in users" class="item-remove-animate item-avatar">
<img ng-src="{{user.photoURL}}">
<h2 id={{user.uid}}>
{{user.name}}
</h2>
<p >{{user.description}}</p>
<p id="userUID{{$index}}">{{user.uid}}</p>
<!-- button options -->
<ion-option-button>
<i class="icon ion-ios-trash-outline"></i>
</ion-option-button>
<ion-option-button>
<i class="icon ion-ios-chatbubble-outline"></i>
</ion-option-button>
</ion-item></a>
JS
$scope.toUserView = function(ind) {
var uid = document.getElementById('userUID'+ind).textContent;
console.log(uid);
$state.go('tab.userview');
};
You seem to repeat the <p id="userUID"></p> in a loop.
id attributes should be unique to a page by definition.
document.getElementById('test') returns the first element having the id attribute set to test.
You should either add something unique to your ids, or use class="userUID" instead.
I have created a ionic hybrid app in that i am getting issue in angularjs filter.let us explained clearly in below
<input type="search" placeholder="Search personalities" ng-model="name" ng-change='alert("changed!")'>
</label>
<ion-list id="personalities-list3">
<!--<ion-item class="item-divider" id="personalities-list-item-divider1"> A</ion-item> auto-list-divider auto-list-divider-value="{{per.name}}"-->
<ion-item class="item-avatar" id="personalities-list-item27" ng-repeat="per in personality|limitTo:showlength|filter:{name:name}" ng-click="selectedpersonality(per)" auto-list-divider auto-list-divider-value="{{per.name}}" auto-list-divider-visible="{{divvisible}}">
<!--<img image-lazy-src="http://www.podxref.com/img/personality/small/{{per.profileimage}}" ng-if='per.profileimage.length > 0' image-lazy-loader="lines" image-lazy-distance-from-bottom-to-load="1000">-->
<img src="http://www.podxref.com/img/personality/small/{{per.profileimage}}" ng-if='per.profileimage.length > 0'>
<img src="img/no-image.jpg" ng-if='per.profileimage==""'>
<h2>{{per.name}}</h2>
</ion-item>
</ion-list>
<ion-infinite-scroll ng-if="!noMoreItemsAvailable" on-infinite="loadMore()" distance="10%"></ion-infinite-scroll>
the above code i have used filter in ng-repeat and also i have used a infinite scroll for list and then personality array contains 18000 values that is in alphabetical order .so for the problem is if i search "zolten rhimer"the list is empty even though the name is present in array .please any one help me to solve this issue
Thanks,
You use like this
<ion-list id="personalities-list3">
<!--<ion-item class="item-divider" id="personalities-list-item-divider1"> A</ion-item> auto-list-divider auto-list-divider-value="{{per.name}}"-->
<ion-item class="item-avatar" id="personalities-list-item27" ng-repeat="per in personarraytosearch=personality|filter:{name:name}|limitTo:showlength " ng-click="selectedpersonality(per)" auto-list-divider auto-list-divider-value="{{per.name}}" auto-list-divider-visible="{{divvisible}}">
<!--<img image-lazy-src="http://www.podxref.com/img/personality/small/{{per.profileimage}}" ng-if='per.profileimage.length > 0' image-lazy-loader="lines" image-lazy-distance-from-bottom-to-load="1000">-->
<img src="http://www.podxref.com/img/personality/small/{{per.profileimage}}" ng-if='per.profileimage.length > 0'>
<img src="img/no-image.jpg" ng-if='per.profileimage==""'>
<h2>{{per.name}}</h2>
</ion-item>
</ion-list>
i think this will help you