I'm trying to add a custom component dynamically (from a directive) into my Ionic 2 app root...
I've tried many things, but nothing works!
I know that I could add a container with an id to hold my custom component and reach it through an Input in my directive, but I don't want to use that solution.
I need to be able to add any custom component into app root.
This is my last attempt:
import { Directive, ElementRef,
ComponentFactoryResolver,
ViewContainerRef,
ApplicationRef
} from '#angular/core';
import { App, ViewController } from 'ionic-angular';
import { MyComponent } from '../../components/myComponent/myComponent';
#Directive({
selector: '[myDirective]',
host: {
'(click)': 'onClick()',
}
})
export class MyDirective {
myComponent: any;
constructor( private el: ElementRef,
private app: App,
private appRef: ApplicationRef,
private vcr: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver
) {
this.myComponent = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
}
onClick() {
this.app._appRoot._overlayPortal._viewport.createComponent(this.myComponent);
}
}
It should create my component as a sibling of _overlay elementin app root, but I only receive an error message:
No provider for ViewController
What am I doing wrong? Any suggestion or any other solution?
Related
I am getting this error
"__zone_symbol_currentTask":{"type":"microTask","state":"notScheduled","source":"Promise.then","zone":"angular","cancelFn":null,"runCount":0}}
while login facebook from my hybrid application. I have used
ng2-cordova-oauth
plugin to achieve facebook login. My code look like.
import { Component } from '#angular/core';
import {IonicPage, NavController, Platform} from 'ionic-angular';
import {OauthCordova} from '../../../node_modules/ng2-cordova-oauth/platform/cordova';
import {Facebook} from '../../../node_modules/ng2-cordova-oauth/core';
#IonicPage()
#Component({
selector: 'page-facebook',
templateUrl: 'facebook.html',
})
export class FacebookPage {
public oauth: OauthCordova;
private provider: Facebook;
public constructor(public navCtrl: NavController, private platform: Platform) {
this.oauth = new OauthCordova();
this.provider = new Facebook({
clientId: "1807864452579635",
appScope: ['id','story','picture','link','type','full_picture','message']
});
}
public login() {
this.platform.ready().then(() => {
this.oauth.logInVia(this.provider).then((success) => {
alert(JSON.stringify(success));
}, (error) => {
console.log(JSON.stringify(error));
});
});
}
}
I ran into this on a project I was working on too. The problem turned out to be that the ng2-cordova-oauth dependencies weren't installed. Specifically we had to run:
cordova plugin add cordova-plugin-inappbrowser
cordova plugin add cordova-plugin-whitelist
cordova prepare
You may also have to whitelist your site using the information found at https://github.com/apache/cordova-plugin-whitelist
Edit: You can't use ng2-cordova-oauth from the browser. You have to use a device or a simulator.
note that it is unlikely for anyone to see the original error in this code since it in part depends on external dependencies
You can use
JSON.stringify(error,Object.getOwnPropertyNames(e));
to get a clear description of the error you getting, because The __zone_symbol_currentTask is a property inserted into the Error object by Angular and JSON.stringify does not output the Error object's own properties (by default)
see also: Is it not possible to stringify an Error using JSON.stringify?
I have a dashboard page. and this page has two tabs to show recent sales and recent inventory.
When user inters dashboard, loadDashboardItems method is called.
My dashboard.ts
import { Component } from '#angular/core';
import {NavController, NavParams, LoadingController } from 'ionic-angular';
import {Storagehelper} from '../../providers/storagehelper';
import { DashboardrecentsalesPage } from '../dashboardrecentsales/dashboardrecentsales';
import { DashboardrecentinventoryPage } from '../dashboardrecentinventory/dashboardrecentinventory';
import {Webservice} from '../../providers/webservice';
/**
* Generated class for the Dashboard page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
#Component({
selector: 'page-dashboard',
templateUrl: 'dashboard.html',
})
export class DashboardPage {
private recentSales;
private recentInventory;
private loading;
private ajaxRequest;
constructor(public navCtrl: NavController, public navParams: NavParams, private storagehelper: Storagehelper, public loadingCtrl: LoadingController, public webservice: Webservice) {
this.loadDashboardItems();
this.recentSales = DashboardrecentsalesPage; //This is the default tab in dashboard page
this.recentInventory = DashboardrecentinventoryPage;
}
ionViewWillLeave(){
if(this.loading) this.loading.dismiss().catch(() => {});
if(this.ajaxRequest!=undefined){
this.ajaxRequest.unsubscribe();
}
}
private loadDashboardItems(){
//HERE API REQUEST IS MADE AND data is saved to Localstorage
}
}
And default tab dashboardrecentsales.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, Tabs } from 'ionic-angular';
import {Storagehelper} from '../../providers/storagehelper';
/**
* Generated class for the Dashboardsummaryitems page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
#Component({
selector: 'page-dashboardrecentsales',
templateUrl: 'dashboardrecentsales.html',
})
export class DashboardrecentsalesPage {
private RecentItems;
constructor(public navCtrl: NavController, public navParams: NavParams, private storagehelper: Storagehelper) {
this.getDashboardItems();
}
getDashboardItems(){
this.RecentItems = this.storagehelper.getStorageItem("RecentItems");
//This RecentItems property is used to render view dashboardrecentsales
}
}
Here in dashboard.ts the API request is made and data are saved in LocalStorage
And 'dashboardrecentsales.ts' fetches data from local storage and renders view to show on tab.
PROBLEM
The problem I have is if user is accessing dashboard page for the first time, Even though data are saved in localstorage, the tab page doesn't get any data from localstorage.
It seems Tab is rendered before execution of loadDashboardItems method of dashboard page gets completed.
I tried putting
this.recentSales = DashboardrecentsalesPage; //This is the default tab in dashboard page
this.recentInventory = DashboardrecentinventoryPage;
inside loadDashboardItems method when everything is saved to localstorage, but no tab view was rendered at all.
Can anybody suggest what can I do in this scenario.
For your case, you can make use of ionViewDidLoad() to load your dashboard items. Try something like this,
ionViewDidLoad(){
this.loadDashboardItems();
}
check this documentation to know more about all the predefined NavController methods
Hope that helps!
Anyway I solved it using event.publish and event.subscribe. More info Here
The sample code from the link. I used this logic to refresh the contents.
import { Events } from 'ionic-angular';
// first page (publish an event when a user is created)
constructor(public events: Events) {}
createUser(user) {
console.log('User created!')
events.publish('user:created', user, Date.now());
}
// second page (listen for the user created event after function is called)
constructor(public events: Events) {
events.subscribe('user:created', (user, time) => {
// user and time are the same arguments passed in `events.publish(user, time)`
console.log('Welcome', user, 'at', time);
});
}
I'm already created cordova plugin and already used in Ionic 1, its worked. Then I tried to use it in Ionic 2 but I don't really know how to call that plugin. I follow the step from here to create my own plugin. And this is what i did:
plugin.xml
<name>myPlugin</name>
<js-module src="www/myPlugin.js" name="myPlugin">
<clobbers target="myPlugin" />
</js-module>
myPlugin.js
module.exports = {
myFunction: function (success, failure) {
cordova.exec(success, failure, "myPlugin", "myFunction", []);
}
};
hello-ionic.ts
import { Component } from '#angular/core';
declare var cordova: any;
#Component({
selector: 'page-hello-ionic',
templateUrl: 'hello-ionic.html'
})
export class HelloIonicPage {
constructor() {
}
click() {
if (typeof cordova !== 'undefined') {
cordova.plugins.myPlugin.myFunction();
}
}
}
But unfortunately it return me an error "Undefined myFunction" in hello-ionic.ts.
Here is what I did.
hello-ionic.ts
import { Component } from '#angular/core';
declare var myPlugin: any;
#Component({
selector: 'page-hello-ionic',
templateUrl: 'hello-ionic.html'
})
export class HelloIonicPage {
constructor() {
}
click() {
myPlugin.myFuntion(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
});
}
}
declare var myPlugin: any; , myPlugin name I get from <clobbers target="myPlugin" />.
Note: Need to run the project in device only.
Following tutorial is a good resource to learn how to create custom cordova plugin :
https://taco.visualstudio.com/en-us/docs/createplugintutorial/
I have followed this tutorial to create multiple custom plugins and those are working fine in Ionic2.
One more thing to point out that the tutorial has not mentioned that:
You have to add your custom plugin in your ionic 2 project using following command:
ionic plugin add "folder path of your custom plugin"
Updated:
In your plugin.xml file, you have set "myPlugin" as target in clobbers tag.
So you should call your function as followed
window.myPlugin.myFunction();
Tip: Whenever you use custom plugin created by you(or someone else), inspect the application using Chrome Developer tools. In console tab of developer tools, you can inspect the window and other available objects and can find out correct way to call plugin's methods.
I started a new ionic 2 rc0 app by copying the files of my old beta11 project. I did the necessary steps as described under
Copying your Project to a New Project : ionic 2 rc0 changelog
Now finally after getting no more compiler errors when I run
ionic run android -c
I'm just getting a whitescreen on my android phone.
Chrome debug is logging me
Uncaught Error: No provider for t!
When I'm running
ionic serve -c
the firefox logs me
Unhandled Promise rejection: No provider for ConnectionBackend! ;
Zone: ; Task: Promise.then ; Value: Object { ...
app.module.ts looks like:
import { NgModule } from '#angular/core';
import { IonicApp, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
import { Storage } from '#ionic/storage'; // special thing
import { Http } from '#angular/http';#
// other imports ....
#NgModule({
declarations: [
MyApp,
// PAGES
// MODALS
// CUSTOM COMPONENTS
// DIRECTIVES
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
// PAGES
// MODALS
],
providers: [
Storage,
Http,
// SERVICES/PROVIDERS
]
})
export class AppModule {}
I'm guessing there is something wrong with a provider somewhere, but I just can't find a solution ...
System : ubuntu 16.04 / node v6.7.0 / npm v3.10.3
EDIT :
I started a new sidemenu project with
ionic start debugProject sidemenu --v2
I did this to to debug the providers by sequentially adding the providers of my original Project. It appears that, when I inject the first provider called "Config" in the constructor of app.components.ts
import { Component } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import { TabsPage } from '../pages/tabs/tabs';
// PROVIDERS -> providers
import { Config } from '../providers/config/config';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
rootPage = TabsPage;
constructor(
platform : Platform,
config : Config <-------------(HERE !!! )
) { .....
I get the the Error message like before:
Unhandled Promise rejection: No provider for ConnectionBackend! ;
Zone: ; Task: Promise.then ; Value: Object { ...
config.ts
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
// providers
import { Http } from '#angular/http';
//import { DB } from '../db/db';
#Injectable()
export class Config {
public data : any;
constructor(
public http : Http,
//public db : DB
){
this.data = {};
}
loadDefault() {
this.data = {
..........................
// DATA OBJECT DEFINITIONS
...........................
};
return Promise.resolve("default settings applied");
}
loadSettingsFromDB(){
return Promise.resolve("no local settings from db for now");
}
// TODO: send settings to server
saveSettingsToDB(settings){
return Promise.resolve("cant save settings for now");
}
handleError(err) : void {
// error stacktrace gets returned on development mode
try{
err = err.json();
}catch(e){}
var msg = err.message || "LOADING ERROR";
if(err.error){
msg += err.error;
//Toast.showLongBottom(msg).subscribe();
}else{
//Toast.showShortBottom(msg).subscribe();
}
console.log("ERROR in config.ts");
console.log(msg);
console.log(err);
}
}
any ideas ? if not, any experience about opening a new issue on github ?
I found the solution. It seems like I can't/shouldn't use the Http module in the providers array in app.modules.ts (In beta11 this was not a problem). My Config module relied on Http, so after constructing Config the application Error (No provider for ConnectionBackend) fired ...
Im building a ionic 2 rc0 app. My rootPage depends on some configuration I have to load before I start my App. I'm using a provider called Config all over over the application. How can I delay/wait until configs loaded before constructing my Home page component. I need the configs in the constructor of Home. Is there sth. like a preloading module from ionic for situation like that ?
Is that even the best location for tasks like that ?
My Config module uses
http.get(../assets/config/config.json)
.toPromise()
.then(res => this.data = res);
to load the json file locally into data object of of my Config provider. And my code in app.component.ts fires a the function config.loadDefault() to load these default settings from the config.json file for the application .....
app.components.ts
import { Component } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import { Home } from '../pages/home/home';
import { Config } from '../providers/config/config';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class LoddenApp {
public rootPage = Home;
constructor(
public platform : Platform,
public config : Config
){
// loading config
config.loadDefault()
.then(data => {
console.log(data);
return config.loadSettingsFromDB();
})
.catch(err => {
console.log("error occured while loading config:");
console.log(err);
});
platform.ready().then(() => {
...
});
}
}
By not defining the rootPage until i got back my promise response, I can hold back the constructor of my Home page from firing.
...
export class LoddenApp {
public rootPage : any; // hold back
constructor(
...
Then inside the then() block, i can set the rootPage of my app.
this.rootPage = Home; // Home is the Homepage component
I am not sure this is the best way to do. But one option is to hide the splash screen after your data gets loaded.
import {Splashscreen} from 'ionic-native';
constructor(
public platform : Platform,
public config : Config
){
platform.ready().then(() => {
// loading config
config.loadDefault()
.then(data => {
console.log(data);
return config.loadSettingsFromDB();
})
.catch(err => {
console.log("error occured while loading config:");
console.log(err);
});
Splashscreen.hide();
});
}