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>
Related
I have two components to show data. The table view and the graph view. Both components has the same data. I use a toggle switch button to switch between these views. This is my code:
<ion-header>
<ion-toolbar class="toolbar-color">
<ion-title>Views</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-grid>
<ion-row>
<ion-col>
<div class="ion-text-start">
<ion-item>
<ion-label>Switch to table view</ion-label>
<ion-toggle (click)="showTable()"></ion-toggle>
</ion-item>
</div>
</ion-col
</ion-row>
<ion-row>
<ion-col>
<app-tableview hidden="true" id="tableview"></app-tableview>
<canvas height="200" #lineCanvas></canvas>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
The default view is the graphview via chart.js. Both components works fine. This is my code to hide and show:
showTable() {
if (this.tableHidden === true) {
this.tableHidden = false;
document.getElementById("tableview").hidden = false;
} else if (this.tableHidden === false) {
this.tableHidden = true;
document.getElementById("tableview").hidden = false;
}
}
When I click on the toggle switch it doesn't hide the graphview but it shows the table view among each other. I have tried the visibility from this post but doesn't work.
JavaScript hide/show element
How can I switch between the components and show only view?
JSFiddle link: https://jsfiddle.net/f8a3wgxb/
An example based off #alex87's comment:
<ng-container *ngIf="!tableHidden">
<app-tableview id="tableview"></app-tableview>
</ng-container>
<ng-container *ngIf="tableHidden">
<canvas height="200" #lineCanvas></canvas>
</ng-container>
Doesn't have to be an ng-container, but that Angular-only tag will not add anything to the DOM. You can always use a or anything else to do just the same...
You can also neaten it up by using an *ngIfElse...
https://angular.io/api/common/NgIf
Note: I tend to go overboard in my tag wrapping, thus the ng-containers, but *ngIf is typically available on almost all tags, as is hidden - per your usage.
Edit: Fixed the hidden variable name - I was lazy and didn't go back up to find what exactly you'd called it.
Edit #2: You can tidy up and minimise your setting of the hidden variable, instead of needing the if/else.
// This toggles between true/false
this.tableHidden = !this.tableHidden;
document.getElementById("tableview").hidden = !this.tableHidden;
Reduced to only two lines.
I am facing the problem, that I need to pass multiple ng-template into another component as a template, example:
<app-datatable>
<ng-template tdDataTableTemplate="description" let-value="value" let-row="row" let-column="column">
<p matTooltip="{{ row[column ]}}">{{ row[column] }}</p>
</ng-template>
<ng-template tdDataTableTemplate="vendorName" let-value="value">
<p matTooltip="{{ value }}">{{ value }}</p>
</ng-template>
</app-datatable>
app-datatable is my component and I need to pass these templates to it as a template.
td-data-table is Teradata covalent DataTable component. It has functionality, that I can template each column with ng-template. But I want to pass ng-template through my component into td-data-table - hope it makes sence
Solution:
I didn't manage to find Covalent way solution. But I've managed to rewrite Teradata grid to custom grid, which allows me more control over it. Here is Stackblitz: Demo
I encountered the same problem and managed to fix it with the following:
In datatable.component.ts:
#Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.scss']
})
export class DataTableComponent implements OnChanges, AfterContentInit {
ngAfterContentInit(): void {
for (let template of this.templates.toArray()) {
if (!template.tdDataTableTemplate) {
continue
}
this.tdDataTable._templateMap.set(template.tdDataTableTemplate, template.templateRef)
}
}
#ViewChild(TdDataTableComponent) tdDataTable;
#ContentChildren(TdDataTableTemplateDirective) templates;
}
This mimics how TdDataTableCompponent handles the templates under the hood.
Also, for completeness, I spoke to the OP on the covalent gitter and they said that they solved it by rewriting the data table from the individual covalent components. They shared a stackblitz here.
I have the problem that I tried to extract the headerbar into a own component but if I use this new component the content of the page goes behind the headerbar.
Without extracting the header and using the same code directly in the HTML it works correctly.
My new Component:
header-bar.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'my-header-bar',
templateUrl: 'header-bar.html'
})
export class MyHeaderBar {
#Input() pageTitle : String;
constructor() {}
}
header-bar.html
<ion-header>
<ion-navbar>
<button ion-button menuToggle icon-only>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>{{pageTitle}}</ion-title>
</ion-navbar>
</ion-header>
The page I try to use it:
home.html
<my-header-bar pageTitle="Einstellungen"> </my-header-bar>
<ion-content>
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps
that are going to primarily use a Tabbed UI.
</p>
...
</ion-content>
Why does it not work with my own component?
I'm using following code and have not faced any issue till now. I tried with following;
<ion-navbar navbar>
<ion-title >{{title}}</ion-title>
</ion-navbar>
Following is ts file code:
#Component({
selector: 'navbar',
templateUrl: 'navbar.html'
})
export class CommonNavbar {
public title: string;
constructor(
private nav: NavController) {
}
setTitle(title: string){
this.title = title;
console.log(this.title);
}
}
And using this in a following way:
<ion-header>
<navbar></navbar>
</ion-header>
I had also added some styling to common navbar( like centering the title) and configured this from the component/page by using following code snippet.
import { CommonNavbar } from '../../components/shared_nav/navbar';
#ViewChild(forwardRef(() => CommonNavbar)) commonNavbar: CommonNavbar;
ngAfterViewInit() {
this.commonNavbar.setTitle(title);
this.commonNavbar.centerTitle(true);
}
I tried to do the same in an older version of Ionic, and I was facing the same issues. I asked that on the Ionic Slack channel and #mhartington (from Ionic team) said:
There is no way to create a global ion-navbar, as this is done on
purpose. The point of having a navbar defined for each component is so
that we can properly animate the titles, navbar background color (if
you change them) and animate other properties needed.
And about creating a custom directive to avoid duplicating ion-header / ion-navbar html code:
That will still creat errors with how angular2 content projection
works. We have several issues that have been open when people try this
and the best answer is to not do it.
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}); }
I'm building an ionic app. I have a state where the user can see all of their posts. Each post has many fields, one of which is 'active' - boolean field.
I'm rendering the list by:
<ion-item ng-repeat="post in posts | filter:{active:true}" ng-click="goToPost(post.id)"
class="item item-thumbnail-left">
<some fields here />
</ion-item>
Then the user has an option to deactivate a post.
<ion-option-button class="button-assertive" ng-click="deactivate(post.id, $index)">
DEACTIVATE
</ion-option-button>
This option makes a request to the backend, and also it locally sets the active field of the post into "false". After the deactivation is complete, I can't get the ng-repeat to re-filter the list - since the current post should no longer appear in the list of active posts.
I tried $scope.$apply() and it threw the $digest error... I tried to add ng-change and ng-model, and that didn't work either. I also tried reloading the state completely, but for some reason, I can't get this done.
Can anyone help me? Thanks in advance!
Look at the fiddle
<div ng-app="app" ng-controller="demoController">
<ion-item ng-repeat="post in data | filter:{activated:true}" ng-click="deativate(post.id)">
{{post.col1}} {{post.activated }} {{"click Me"}} </br>
</ion-item>
</div>
var app= angular.module("app",[]);
app.controller('demoController', ['$scope',
function($scope) {
$scope.data = [
{id:1,col1:"abc",activated:true},
{id:2,col1:"abc1",activated:true},
{id:3,col1:"abc2",activated:true},
{id:4,col1:"abc3",activated:true},
{id:5,col1:"abc4",activated:false},
{id:6,col1:"abc5",activated:true},
];
$scope.deativate = function(id){
angular.forEach($scope.data,function(item){
if(item.id === id){
item.activated = false;
}
})
}
}]);
and for your code you can share your code snippet.