I am using ionic 2 and am trying to transfer data from one page to another. More specifically one list item on the first page(quickSearch) to another page(quickSearchDetail). I have a picture below to demonstrate this.
When I click on a list it should transfer that data to the next page, however I am having an issue where only the first list item is being transferred irrespective of which item I click (over writing my local storage data).
quickSearch Template list item
<ion-item *ngFor="let item of items" (click)="gotoQuickSearchDetail(item._id)">
<p id="clusterNameFormValue">{{ item.clusterName }}</p>
<p id="currentBUNameFormValue">{{ item.currentBUName }}</p>
<p id="technologyFormValue">{{ item.technology }}</p>
<p id="customerFormValue">{{ item.Customer }}</p>
<p id="clusterHeadNumberFormValue">{{ item.clusterHeadNumber }}</p>
</ion-item>
quickSearch.ts
gotoQuickSearchDetail(){
var clusterName: any = clusterName = document.getElementById("clusterNameFormValue").innerHTML;
var currentBUName: any = currentBUName = document.getElementById("currentBUNameFormValue").innerHTML;
var technology: any = technology = document.getElementById("technologyFormValue").innerHTML;
var customer: any = customer = document.getElementById("customerFormValue").innerHTML;
var clusterHeadNumber: any = clusterHeadNumber = document.getElementById("clusterHeadNumberFormValue").innerHTML;
localStorage.setItem('clustername', clusterName);
localStorage.setItem('currentBUName', currentBUName);
localStorage.setItem('technology', technology);
localStorage.setItem('customer', customer);
localStorage.setItem('clusterHeadNumber', clusterHeadNumber);
this.navCtrl.setRoot(QuickSearchDetail);
}
quickSearchDetail item
<ion-list>
<ion-item>
<p id="clusternameDetail"></p>
<p id="currentBUNameDetail"></p>
<p id="technologyDetail"></p>
<p id="customerDetail"></p>
<p id="clusterHeadNumberDetail"></p>
</ion-item>
</ion-list>
quickSearchDetail.ts
ionViewDidLoad() {
document.getElementById('clusternameDetail').innerHTML = localStorage.getItem('clustername');
document.getElementById('currentBUNameDetail').innerHTML = localStorage.getItem('currentBUName');
document.getElementById('technologyDetail').innerHTML = localStorage.getItem('technology');
document.getElementById('customerDetail').innerHTML = localStorage.getItem('customer');
document.getElementById('clusterHeadNumberDetail').innerHTML = localStorage.getItem('clusterHeadNumber');
}
This is too much to handle :D but, where you are going wrong is using getElementById, and id.. and.. stuff. an ID is unique. And with the *ngFor you are creating more elements with the same ID, this is not allowed. But the browser copes, and gives back the first element it finds with that ID, which explains the behaviour you are seeing.
Another point is the weird usage of localStorage. This does not seem like a use-case for what you are trying to do. I suggest you look at the angular documentation, and specifically at inter component communication.
In short, you probably should create a service, which holds the array list. Then create a navigation parameter on your detail page which will hold the id number to an item in the array list of the service. On that page you can obtain this id and access the service to get the right data from the items.
I could write it all for you, but this is not the right site for that, which will bring me back to my previous mentioned suggestion. Look at angular documentation, to see how you should create a master-detail structure, because getElementById, innerHtml and localStorage is not the way to go
Try to use this
//quickSearch Template list item
<ion-item *ngFor="let item of items" (click)="gotoQuickSearchDetail(item)">
<p id="clusterNameFormValue">{{ item.clusterName }}</p>
<p id="currentBUNameFormValue">{{ item.currentBUName }}</p>
<p id="technologyFormValue">{{ item.technology }}</p>
<p id="customerFormValue">{{ item.Customer }}</p>
<p id="clusterHeadNumberFormValue">{{ item.clusterHeadNumber }}</p>
</ion-item>
//quickSearch.ts
gotoQuickSearchDetail(item: any){
var clusterName: any = clusterName = item.currentBUName;
var currentBUName: any = currentBUName = item.currentBUName;
var technology: any = technology = item.technology;
var customer: any = customer = item.Customer;
var clusterHeadNumber: any = clusterHeadNumber = item.clusterHeadNumber;
localStorage.setItem('clustername', clusterName);
localStorage.setItem('currentBUName', currentBUName);
localStorage.setItem('technology', technology);
localStorage.setItem('customer', customer);
localStorage.setItem('clusterHeadNumber', clusterHeadNumber);
this.navCtrl.setRoot(QuickSearchDetail);
You have loop for items. In that case each items contain same id. For this reason when you try to find a value by element id it always gives you 1st item.
You just need to use navParams . You have a very seriously creative solution but the answer is very simple. For my answer I am going to remove all the ID's , add them back if you need it for styles. Also one thing to note, try not to approach angular with a jquery mindset, because they are 2 different animals.
quickSearch Template list item
<ion-item *ngFor="let item of items">
<p>{{ item.clusterName }}</p>
<p>{{ item.currentBUName }}</p>
<p>{{ item.technology }}</p>
<p>{{ item.Customer }}</p>
<p>{{ item.clusterHeadNumber }}</p>
</ion-item>
So there is your list wrapper in a *ngFor . Your assignment of item is all you need. That item holds all of the data that you want on the next page. It is your "package" ( if you will ). Now all you want to do is pass the "package" to the next page. And we want the passing to start when it is clicked.
Therefore we pass it on the (click) event.
(click)="gotoQuickSearchDetail(item)"
So now we just need to add the method in our Component file, and then with the power of routing in ionic 2 we just send it to the next page.
gotoQuickSearchDetail(item){
this.navCtrl.push(QuickSearchDetail, item); // Notice the 2nd parameter
}
The 2nd parameter passes the item to the page declared in the 1st parameter.
So now you are going to the page QuickSearchDetail and you are passing your "package" item along for the ride.
Now when you get to the next page you need to tell that page .. "Hey i have "package" called item and it has some data.
So in your QuickSearchDetail Component you can add this.
import { NavController, NavParams } from 'ionic-angular';
#Component({
selector: '...',
templateUrl: '...html'
})
export class QuickSearchDetailPage {
itemDetails: any;
constructor(public navCtrl: NavController, public navParams: NavParams){
this.itemDetails = this.navParams.data;
// All the data is now held in the itemDetails variable.
console.log(this.itemDetails);
}
}
You now have assigned the "package" holding the clicked item's data to the variable itemDetails which was "passed" (by default ) to the navParams.data. You can now use the itemDetails in your view.
{{ itemDetails.PROPERTYHERE }}
BONUS:
You can get this stuff for free when you use your terminal to generate a page.
>_ ionic generate page QuickSearchDetail
Also the navCtrl has a third property which has options for animation. Which is fun.
this.navCtrl.push(QuickSearchDetail, item, {
animate: true,
direction: 'forward',
mode: 'ios'});
Read more about the navCtrl here
And read more about the navParams here
Add all data to your Click function and in .ts
gotoQuickSearchDetail(data:any,datab:any.........){
navpushhere(SearchPage,{firstPassed:data,secondpassed:datab}); }
Related
I am building a post website in vue.
I want to implement the functionality so that people can see how many views ('vues') the post got.
Every time someone clicks on a post, the view count should increase by 1.
I use firebase as my backend.
This is my simplified code based on my question:
<template>
<h1>{{ title }}</h1>
<p>This page has {{ views }}</p>
</template>
<script>
export default {
setup() {
const title = "Blog 1"
let views = 0
return { title, views }
}
};
</script>
I'm trying to display an array of images via their source link using an ngFor but I am receiving errors and it's not working!
The image HTML code which is within my card component:
<div class="Session-Participants">
<div *ngFor="let image of {{ participantImages }}" >
<img src='{{image}}'/>
</div>
</div>
In the JS file:
#Input() participantImages? = [];
The data is coming from my other component and the html and JS are as below:
<div *ngFor="let sessions of getSortedSessionList()">
<div *ngFor="let session of sessions">
<tl-session-card
[title]="session.name"
[path]="sessionPath(session.id, session.name)"
[participantImages]="getParticipantDetails(session)" // HERE
>
</tl-session-card>
</div>
</div>
the JS:
participantImage = [];
getParticipantDetails(session) {
this.participantImage = session.roles[0].account.thumbnailURL;
return this.participantImage;
}
I'm expecting for the 'this.participantImage' to be returned and then sent to the card component and then broken down to be displayed separately.
I'm sure I am missing something?
Thanks if you can help at all?
Actually in the stackblitz link, you have a proper array and that not reflect perfectly the code you just provide above.
You need to ensure participantImage stay an array otherwise, the ngFor will not work as expected and you will see some errors in your console.
as #prashant-pimpale says, you need to append to the list of participantImage the result.
In the ts file It would be better to use
participantImage = [];
getParticipantDetails(session) {
// ensures this.participanImage keeps its array type and will process all
// other roles
this.participantImage = session.roles.map(account => account.thumbnailURL);
return this.participantImage;
}
Hope it helps
PS: would be better to set the attribute name of participantImage to participantImages since it represent a list of images ;)
I am using Angular 6 and Angular Angular Material,
I have dynamic list of polls with list of options. I want to show selected option with two way data binding. as my list is dynamic i want to pass variable in [(ngModel)]. tried passing variable but no luck please suggest alternate solution stackblitz example code
You were using the same variable (mySelection) for both questions. Instead, you need to use two different variables, or even better, an array. Here's the fixed version on StackBlitz and here's the relevant code.
Template
<ol>
<li *ngFor="let poll of polls;let i = index">
{{poll.name}}
<mat-radio-group class="example-radio-group" [(ngModel)]="selectedAnswers[i]">
<mat-radio-button class="example-radio-button" *ngFor="let option of poll.options" [value]="option.answer">
{{option.answer}}
</mat-radio-button>
</mat-radio-group>
<strong>Seleted Answer : {{selectedAnswers[i]}}</strong>
<button [disabled]="selectedAnswers[i]==undefined" mat-raised-button color="accent">save</button>
</li>
</ol>
<pre>{{ selectedAnswers | json }}</pre>
Component
#Component({
/* ... */
})
export class AppComponent {
selectedAnswers = []
polls = [
/* ... */
]
}
I'm using Ionic 2 and trying to use getElementbyId on
<ion-content padding>
<ion-list id="list"></ion-list>
</ion-content>
so that i can add items the following way:
var listIon=document.getElementById("list");
var li = document.createElement("ion-item");
li.innerText=usern+": "+mensaje;
var p = document.createElement("p");
p.appendChild(li);
listIon.appendChild(p);
It works perfectly fine the first time, but if I exit the page and reopen it I get an error saying that
cannot read property appendChild on null
. I have already read other questions where they say that the script is not at the bottom. By taking in count that in Ionic you don't put the script in the html at all I don't know what could be causing this.
Edit
I have uploaded the files here main problem is in chat-details, chat-details is opened from chat.
What exactly are you trying to do? I think you're thinking in an old Jquery way, but Ionic works on top of Angular2, so things are done in a different way.
I assume you want to append a new item to a list, which can be done by using *ngFor and an array of data. Or if you want to show a message under certain conditions, you can accomplish that by using *ngIf. You should try to avoid referencing the DOM directly, and update the component code instead to change the data shown in the view.
I've created this demo plunker just to show you how to do these things in a Angular2 way. Please let me know what would be the expected result, so I can update the plunker accordingly.
Component code:
#Component({
selector: 'page-home',
templateUrl: 'app/home.page.html'
})
export class HomePage {
private count: number;
public users: Array<string>;
public showMessage: boolean;
public message: string;
constructor() {
this.count = 1;
this.users = [];
this.showMessage = false;
this.message = 'This is a secret message!';
}
public addUser(): void {
this.users.push(`User ${this.count++}`);
}
public toggleMessage(): void {
this.showMessage = !this.showMessage;
}
}
View code:
<ion-header>
<ion-navbar>
<ion-title>HomePage</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>Users</h2>
<p *ngIf="showMessage">{{ message }}</p>
<ion-list *ngFor="let user of users">
<ion-item>
<p>{{ user }}</p>
</ion-item>
</ion-list>
<button ion-button (click)="addUser()">Add a user</button>
<button ion-button (click)="toggleMessage()">Togle message</button>
</ion-content>
See this plunker.
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of <span ng-init="subCats = subCats + ' ' + subCategory.name">{{subCats}}</span>
</div>
This HTML page shows a filtered result from an object. However, I want to display a consolidated result of the names after "You are now seeing details of" like for example, "You are now seeing details of jim tom". This consolidated list should appear after the element which has ng-repeat directive.
How can this be done?
Thanks
I made an updated plunker for you.
Please try to make your example plunker way more reduced to the specific problem in the future as this helps us to help you.
First I added the search binding as filter to the ng-repeat to make the filter workable:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id'">
To avoid executing the filter twice you can save the filter result directly into a scope variable by simply assinging it (in my example to subCategorysFilter):
<div ng-repeat="subCategory in subCategorysFilter = (subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id')">
I further changed your getAllFilteredNames() method to take a filter object as argument and made it loop through the results, build an array of the names and join them with a , as separation:
$scope.getAllFilteredNames = function(filter){
var names = [];
angular.forEach(filter, function(element){
names.push(element.name);
});
return names.join(", ");
};
This is now called outside the ng-repeat directive:
You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}
Have fun!
Update
Two possible solutions for getting a multilined output:
1 - You might change the line
<div>You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}</div>
to
<div>You are now seeing details of <span ng-bind-html="getAllFilteredNames(subCategorysFilter)"></span></div>
Then any html tags within the expression are compiled as html code. But there are meaningful reasons for angular disabling this feature by default. If your objects are editable by users you need to prevent them from breaking your design by escaping all html tags...
2 - But if you do not need to display the cosolidated information within a single string, you might simply use another ng-repeat combined with an <ul> like this:
<div>You are now seeing details of <br/>
<ul>
<li ng-repeat="subCategory in subCategorysFilter">{{subCategoryName}}</li>
</ul>
</div>
Just style your li accordingly to be displayed underneath each other and you're ready to go.
You can do this in your HTML by moving your consolidated list outside of the ngRepeat and calling the filter again:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
The drawback to this approach is that you are calling the filter twice. A better alternative would be to set up a $watch in your parent controller and invoke the $filter manually. I.e. Save the filtered results in a scope variable. The benefit is that the filter is called half as many times and the scope variables you set up are visible to the original list and the consolidated list.
app.controller('ParentController', function($scope, $filter) {
$scope.subCategorys = [{...}];
$scope.tag = {...};
$scope.$watchCollection('subCategorys', function(newList){
//if the collection changes, create a new tag
//reference that is a copy of the old one to trigger
//the tag watch listener
if (newList)
$scope.tag = angular.copy($scope.tag);
});
$scope.$watch('tag', function(newTag){
// if tag changes, apply the filter,
// and save the result to a scope variable
if(newTag)
$scope.filteredList = $filter('filter')
($scope.subCategories, { tags: newTag}, true);
});
});
HTML
<div ng-controller="ParentController">
<div ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
</div>
I'm afraid there is no way of doing that except for selecting the subCategory back. Fortunately, there is a pretty elegant 'angular' way of doing that. Add this to your controller:
$scope.getSubCatById = function(someId) {
return $filter('filter')($scope.subCategorys, {id:someId})[0];
}
And then your html:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of {{ getSubCatById(2).name }}
</div>
I hope I interpreted your question correctly.