I'm working an on an Ionic 2 project and my front end code renders as I would expect it on Android and web but by all accounts nothing renders correctly on iOS.
I'm using Xcode 8.0 with an iPhone 6 version 9.3.4 and Mac version 10.12
package.json
{
"name": "ionic-hello-world",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
"scripts": {
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve"
},
"dependencies": {
"#angular/common": "2.1.1",
"#angular/compiler": "2.1.1",
"#angular/compiler-cli": "2.1.1",
"#angular/core": "2.1.1",
"#angular/forms": "2.1.1",
"#angular/http": "2.1.1",
"#angular/platform-browser": "2.1.1",
"#angular/platform-browser-dynamic": "2.1.1",
"#angular/platform-server": "2.1.1",
"#ionic/storage": "1.1.6",
"#types/parse": "^1.2.32",
"ionic-angular": "2.0.0-rc.4",
"ionic-native": "2.2.11",
"ionicons": "3.0.0",
"rxjs": "5.0.0-beta.12",
"underscore": "^1.8.3",
"moment": "2.10.3",
"sweetalert": "1.1.3",
"zone.js": "0.6.26"
},
"devDependencies": {
"#ionic/app-scripts": "0.0.47",
"typescript": "2.0.9"
},
"cordovaPlugins": [
"cordova-plugin-device",
"cordova-plugin-console",
"cordova-plugin-whitelist",
"cordova-plugin-splashscreen",
"cordova-plugin-statusbar",
"ionic-plugin-keyboard",
"cordova-plugin-facebook4",
"cordova-plugin-nativestorage",
"parse-push-plugin"
],
"cordovaPlatforms": [
"ios",
{
"platform": "ios",
"version": "",
"locator": "ios"
}
],
"description": "CPM: An Ionic project"
}
directory.html
<ion-header class="opaque">
<ion-navbar no-border-bottom>
<ion-title class="navtitle">Directory</ion-title>
</ion-navbar>
<ion-toolbar class ="customtoolbar">
<ion-grid>
<ion-row>
<ion-segment value="filters" *ngFor="let personType of ageGender">
<ion-segment-button value="personType" >{{personType}}</ion-segment-button>
</ion-segment>
</ion-row>
</ion-grid>
</ion-toolbar>
</ion-header>
<ion-content fullscreen class="card-background-page">
<ion-list no-lines >
<ion-item *ngFor="let offer of displayOffers">
<ion-thumbnail item-left>
<img src="{{offer.get('logo')._url}}">
</ion-thumbnail>
<ion-grid>
<ion-row>
<ion-col>
<button class="listButton">Details</button>
</ion-col>
</ion-row>
</ion-grid>
<button ion-button clear item-right>{{offer.get("cashBackPercent")| percent}}</button>
</ion-item>
</ion-list>
</ion-content>
directory.ts
import { Component } from '#angular/core';
import { Parse } from 'parse-js-sdk';
import { DataService } from '../../services/dataService';
#Component({
selector: 'directory',
templateUrl: 'directory.html'
})
export class Directory {
displayOffers: any[];
ageGender: any = ["Women", "Men", "Children"];
constructor(public dataService: DataService) {}
ionViewDidLoad() {
var self = this;
this.dataService.directoryOffers().then(function(offers){
self.displayOffers = offers;
})
}
}
dataService.ts
import { Injectable } from '#angular/core';
import { Parse } from 'parse-js-sdk';
#Injectable()
export class DataService {
constructor() {
}
directoryOffers(): any{
var Offers = Parse.Object.extend("Offer");
var offerQuery = new Parse.Query(Offers);
offerQuery.equalTo("hasContract", true);
offerQuery.notEqualTo("canceled", true);
offerQuery.greaterThanOrEqualTo("untilDate", new Date());
offerQuery.include('vendor');
offerQuery.exists('logo');//display no offers that lack logos!
return offerQuery.find();
}
}
This is how it looks on web/Android:
Here is what you see on iOS:
After 10 hour of work I'm pretty certain that iOS doesn't like the change in scope represented by 'self' instead of 'this' within the query promise. Because of this I'm unable to render the data on the client.
The query returns an A+ promise and as far as I know, there is no way to resolve the promise so that the query result stands outside of the promise so that we can use 'this'. I could be totally wrong but I saw nothing in the 'Promise Resolution Procedure' that indicated otherwise.
There was a problem with the polyfills and Angular 2 being recognized on the iOS web browser.
For newbies, I'll be detailed in my answer.
First find the iOS console log:
1) Open Safari
2) Load (play button) the app via Xcode
3) Go to the develop menu
4) Select your device
5) Right before the app finishes loading you will see an option to go to your app. Do so.
From the hints in the logs I found the real error. After a search I added:
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>
to the head of my index.html above
<script src="cordova.js"></script>.
Related
This is the simple HTML that I have written for single page web app
<div class="bg">
<h1 class="faq_heading">Frequently Asked Questions</h1>
<div class="search">
<mat-form-field class="search_input">
<input type="text" placeholder="Search" class="search_input" matInput [(ngModel)]="inp" (keyup)="onKey($event)" />
</mat-form-field>
<mat-form-field>
<mat-label>Categories</mat-label>
<mat-select class="dropdown" placeholder="Categories">
<mat-option value="Default">Default</mat-option>
</mat-select>
</mat-form-field>
<img src="../assets/images/search.svg" alt="" class="search_img" />
</div>
</div>
There is nothing much in the component.ts file just the basic template and required import statements
Following is the app.module.ts
import { NgModule } from "#angular/core";
import { BrowserModule } from "#angular/platform-browser";
import { MatFormFieldModule } from "#angular/material/form-field";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { MatGridListModule } from "#angular/material/grid-list";
import { MatSelectModule } from "#angular/material/select";
import { MatPaginatorModule } from "#angular/material/paginator";
import { MatDialogModule } from "#angular/material/dialog";
import { MatSnackBarModule } from "#angular/material/snack-bar";
import { BrowserAnimationsModule } from "#angular/platform-browser/animations";
import { HttpClientModule } from "#angular/common/http";
import { faqservice } from "./faq.service";
import { MatExpansionModule } from "#angular/material/expansion";
import { FormsModule } from "#angular/forms";
#NgModule({
declarations: [AppComponent],
imports: [
FormsModule,
MatExpansionModule,
HttpClientModule,
BrowserModule,
AppRoutingModule,
MatFormFieldModule,
MatGridListModule,
MatSelectModule,
MatDialogModule,
BrowserAnimationsModule,
MatSnackBarModule,
MatPaginatorModule,
],
providers: [faqservice],
bootstrap: [AppComponent],
})
export class AppModule {}
Following are the errors
Error1:
ERROR TypeError: Cannot read property 'controlType' of undefined
at MatFormField.ngAfterContentInit (form-field.js:527)
at callHook (core.js:2526)
at callHooks (core.js:2495)
at executeInitAndCheckHooks (core.js:2446)
at refreshView (core.js:9486)
at refreshComponent (core.js:10616)
at refreshChildComponents (core.js:9242)
at refreshView (core.js:9495)
at renderComponentOrTemplate (core.js:9559)
at tickRootContext (core.js:10790)
Error2:
ERROR TypeError: Cannot read property 'errorState' of undefined
at MatFormField_HostBindings (form-field.js:830)
at processHostBindingOpCodes (core.js:9213)
at refreshView (core.js:9491)
at refreshComponent (core.js:10616)
at refreshChildComponents (core.js:9242)
at refreshView (core.js:9495)
at renderComponentOrTemplate (core.js:9559)
at tickRootContext (core.js:10790)
at detectChangesInRootView (core.js:10815)
at RootViewRef.detectChanges (core.js:22865)
As you can see there is not much in the code as I have just started the development , but I am already getting errors in basic templates direct from the docs.
How to handle these errors?
(please note that I am new to angular material, if possible just mention some details around the answer)
Here is the package.json
{
"name": "faq1",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"#angular/animations": "~12.1.1",
"#angular/cdk": "^12.1.1",
"#angular/common": "~12.1.1",
"#angular/compiler": "~12.1.1",
"#angular/core": "~12.1.1",
"#angular/forms": "~12.1.1",
"#angular/material": "^12.1.1",
"#angular/platform-browser": "~12.1.1",
"#angular/platform-browser-dynamic": "~12.1.1",
"#angular/router": "~12.1.1",
"rxjs": "~6.6.0",
"tslib": "^2.2.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"#angular-devkit/build-angular": "~12.1.1",
"#angular/cli": "~12.1.1",
"#angular/compiler-cli": "~12.1.1",
"#types/jasmine": "~3.6.0",
"#types/node": "^12.11.1",
"jasmine-core": "~3.7.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"typescript": "~4.3.2"
}
}
Import and declare the MatInputModule in the module file solve the issue
....
import {MatInputModule} from '#angular/material/input';
#NgModule({
...
,
imports: [
...
MatInputModule,
],
providers: [
....
]
})
export class MyModule {}
I hade same problem, but when changed to this, issue fixed:
<mat-form-field appearance="fill">
<mat-label>Status</mat-label>
<mat-select>
<mat-option *ngFor="let status of ['active', 'inactive']" [value]="status">
{{status}}
</mat-option>
</mat-select>
</mat-form-field>
I had the same problem, but I solved it adding an ngIf in the mat-form-field tag.
<mat-form-field appearance="fill" *ngIf="role$ | async as roles">
<mat-label>Select a role</mat-label>
<mat-select formControlName="codeRole">
<mat-option *ngFor="let r of roles" [value]="r.code>
{{r.description}}</mat-option>
</mat-select>
</mat-form-field>
I updated from Vue2 to Vue3 and now kind of stuck. I use it inside Rails.
The problem that everything is loading and compiling, not errors at all. I load my web page and see everything instead of Vue files. Don't understand how to fix it at all :(
Any ideas? Maybe I don't see smth or don't know where to looking for it.
If I change smth on the application file it shows some changes or errors. I even deleted everything and add Vue files like in tutorials shows and still nothing to work.
application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import Routes from '../routes/index.js.erb';
import '../css/index.css'
window.Routes = Routes;
import {createApp} from 'vue';
import Customer from '../customer.vue'
import CustomerSearch from '../customer_search.vue'
import CustomerPackages from '../customer_packages.vue'
import BusinessCases from '../business_cases.vue'
import SearchPanel from '../components/business_case/SearchPanel.vue'
import {turbolinksAdapterMixin} from "vue-turbolinks";
import Clipboard from 'v-clipboard'
document.addEventListener('turbolinks:load', () => {
const app = createApp({
el: "[data-behavior='vue']",
mixins: [turbolinksAdapterMixin],
})
})
app.component('customer', Customer);
app.component('customer-search', CustomerSearch);
app.component('packages', CustomerPackages);
app.component('business-cases', BusinessCases);
app.component('search-panel', SearchPanel);
envirement.js
const { environment } = require('#rails/webpacker')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
const erb = require('./loaders/erb')
const pug = require('./loaders/pug')
environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.prepend('vue', vue)
environment.loaders.prepend('erb', erb)
environment.loaders.prepend('pug', pug)
module.exports = environment
package.json
{
"name": "backyard",
"private": true,
"dependencies": {
"#rails/actioncable": "^6.0.0",
"#rails/activestorage": "^6.0.0",
"#rails/ujs": "^6.0.0",
"#rails/webpacker": "5.2.1",
"#tailwindcss/aspect-ratio": "^0.2.0",
"#tailwindcss/forms": "^0.2.1",
"#tailwindcss/typography": "^0.4.0",
"#vue/cli": "^5.0.0-alpha.8",
"axios": "^0.21.0",
"css-loader": "^5.0.2",
"dayjs": "^1.10.4",
"litepie-datepicker": "^1.0.13",
"node-sass": "4.14",
"pug": "^3.0.0",
"pug-plain-loader": "^1.1.0",
"rails-erb-loader": "^5.5.2",
"sass": "^1.32.7",
"sass-loader": "^11.0.1",
"turbolinks": "^5.2.0",
"v-clipboard": "^2.2.3",
"vue": "^3.0.2",
"vue-clickaway": "^2.2.2",
"vue-clipboards": "^1.3.0",
"vue-fuse": "^2.2.1",
"vue-loader": "^16.2.0",
"vue-nav-tabs": "^0.5.7",
"vue-pdf": "^4.2.0",
"vue-pug": "^4.0.0",
"vue-turbolinks": "^2.1.0",
"webpack": "4"
},
"version": "0.1.0",
"devDependencies": {
"#tailwindcss/postcss7-compat": "^2.0.3",
"#vue/compiler-sfc": "^3.0.11",
"#webpack-cli/serve": "^1.3.0",
"autoprefixer": "9",
"postcss": "7",
"tailwindcss": "npm:#tailwindcss/postcss7-compat",
"vue-cli-plugin-pug": "~2.0.0",
"webpack-cli": "3.3.12",
"webpack-dev-server": "^3.11.2"
},
"browserslist": [
"defaults"
]
}
I've had a similar problem and my best guess is the turbolinksAdapterMixin. Vue 3 changed and renamed destroy hooks to unmounted hooks (see here) besides other changes to their Component APIs. I believe, the package vue-turbolinks still uses Vue 2 functionalities, such as $on, $off, and $once for events.
The way Turbolinks works is that the "next HTML body" is fetched via AJAX which in turn replaces the document.body.
So, what you need to do – and already did correctly – is to create the Vue 3 App with createApp in the event listener for turbolinks:load (when the body was successfully replaced).
And then you have to tear down, i.e., unmount, your Vue app as soon as you leave the page (i.e., click a turbolinks link) with unmount().
Hence, you have to replace your code:
document.addEventListener('turbolinks:load', () => {
const app = createApp({
el: "[data-behavior='vue']",
mixins: [turbolinksAdapterMixin],
})
})
with this:
let app;
document.addEventListener('turbolinks:load', () => {
app = createApp({});
app.mount("[data-behavior='vue']");
});
// this event listener is executed as soon as
// the new body was fetched successfully but
// before replacing the `document.body`
document.addEventListener('turbolinks:before-render', () => {
if (app) app.unmount();
});
I have an ionic 3 app.
On a page I have a form with some fields.
<form>
<ion-item>
<ion-label>First item</ion-label>
<ion-input type="text" [(ngModel)]="title" name="title"></ion-input>
</ion-item>
... some more simple fields ...
<ion-item>
<ion-label>Item below keyboard region</ion-label>
<ion-textarea [(ngModel)]="description" name="description"></ion-textarea>
</ion-item>
<button ion-button type="submit" block>Add Todo</button>
</form>
When I tab the first, the keyboard is shown and the input item is properly focussed, that is: shows a blinking caret.
Though, when I click a field at a position below the area needed to show the keyboard, I do not get a caret, although the field is actually focussed. When I type, the karakters are put in the field.
The main difference is that when clicking on the lower field, the form is shifted upwards when the keyboard shows.
How to fix this?
I am running the app on an iPad 2017, iOS 11.2.2.
package.json:
{
"name": "my app",
"version": "1.0.1",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build --release",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve"
},
"dependencies": {
"#angular/animations": "5.0.0",
"#angular/common": "5.0.0",
"#angular/compiler": "5.0.0",
"#angular/compiler-cli": "5.0.0",
"#angular/core": "5.0.0",
"#angular/forms": "5.0.0",
"#angular/http": "5.0.0",
"#angular/platform-browser": "5.0.0",
"#angular/platform-browser-dynamic": "5.0.0",
"#ionic-native/app-version": "^4.5.2",
"#ionic-native/calendar": "^4.3.2",
"#ionic-native/call-number": "^4.4.2",
"#ionic-native/camera": "^4.3.2",
"#ionic-native/core": "4.3.0",
"#ionic-native/date-picker": "^4.4.2",
"#ionic-native/file": "^4.4.2",
"#ionic-native/in-app-browser": "^4.3.3",
"#ionic-native/keyboard": "^4.4.2",
"#ionic-native/media-capture": "^4.4.0",
"#ionic-native/native-page-transitions": "^4.3.2",
"#ionic-native/splash-screen": "4.3.0",
"#ionic-native/status-bar": "4.3.0",
"#ionic/pro": "^1.0.9",
"#ionic/storage": "2.0.1",
"#ngx-translate/core": "^9.1.0",
"#ngx-translate/http-loader": "^2.0.1",
"call-number": "^1.0.1",
"com.telerik.plugins.nativepagetransitions": "^0.6.5",
"cordova-ios": "^4.5.4",
"cordova-plugin-app-version": "^0.1.9",
"cordova-plugin-calendar": "^4.6.0",
"cordova-plugin-camera": "^2.4.1",
"cordova-plugin-compat": "^1.2.0",
"cordova-plugin-datepicker": "^0.9.3",
"cordova-plugin-device": "^1.1.7",
"cordova-plugin-file": "^5.0.0",
"cordova-plugin-file-transfer": "^1.7.0",
"cordova-plugin-inappbrowser": "^1.7.2",
"cordova-plugin-ionic-webview": "^1.1.16",
"cordova-plugin-media-capture": "^1.4.3",
"cordova-plugin-privacyscreen": "^0.4.0",
"cordova-plugin-splashscreen": "^4.1.0",
"cordova-plugin-statusbar": "^2.3.0",
"cordova-plugin-whitelist": "^1.3.3",
"cordova-windows": "^5.0.0",
"intl": "^1.2.5",
"ionic-angular": "3.9.2",
"ionic-plugin-keyboard": "^2.2.1",
"ionicons": "3.0.0",
"mx.ferreyra.callnumber": "0.0.2",
"ng2-datepicker": "^2.2.1",
"plist": "^2.1.0",
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"
},
"devDependencies": {
"#ionic/app-scripts": "3.1.0",
"cors": "^2.8.4",
"typescript": "2.4.2",
"ws": "3.3.2"
},
"description": "An Ionic project",
"cordova": {
"plugins": {
"com.telerik.plugins.nativepagetransitions": {},
"cordova-plugin-camera": {
"CAMERA_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-calendar": {
"CALENDAR_USAGE_DESCRIPTION": "This app uses your calendar to plan sessions."
},
"cordova-plugin-privacyscreen": {},
"ionic-plugin-keyboard": {},
"cordova-plugin-whitelist": {},
"cordova-plugin-device": {},
"cordova-plugin-splashscreen": {},
"cordova-plugin-ionic-webview": {},
"cordova-plugin-inappbrowser": {},
"cordova-plugin-media-capture": {
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-datepicker": {},
"mx.ferreyra.callnumber": {},
"cordova-plugin-statusbar": {},
"call-number": {},
"cordova-plugin-file": {
"PHOTOLIBRARY_USAGE_DESCRIPTION": "This allows",
"PHOTOLIBRARY_ADD_USAGE_DESCRIPTION": "This allows",
"FILE_USAGE_DESCRIPTION": "This app uses your files to upload on sessions.",
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " "
},
"cordova-plugin-app-version": {}
},
"platforms": [
"windows",
"ios"
]
}
}
app.module.ts:
...
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
IonicModule.forRoot(MyApp, {scrollAssist: false, autoFocusAssist: 'delay'})
],
in app.component.ts:
this.platform.ready().then(() => {
console.log('Platform is ready!');
this.keyboard.disableScroll(false);
...
thanks!
I have this hackish solution, but I guess there have to be a better one. But because of the comment of user2158259, I'll post it anyway. Please don't punish me for posting it ;-)
1) Remove packages/plugins if you have them:
ionic cordova plugin remove ionic-plugin-keyboard
npm uninstall ionic-plugin-keyboard --save
npm uninstall #ionic-native/keyboard --save
2) Remove any references in app.module.ts such as
this.keyboard.disableScroll(true);
3) create a sub-folder in the app-folder and add these two files:
device.service.ts
The following code is a subset of my device.service.ts
import {ApplicationRef, EventEmitter, Injectable} from '#angular/core';
#Injectable()
export class DeviceService {
public onTick: EventEmitter<string> = new EventEmitter();
private tickValue = new Date().getTime();
constructor(public appRef: ApplicationRef) {
window.addEventListener('onresize', () => {
console.log('DeviceService: on resize');
this.doTick();
setTimeout(() => {
this.doTick();
}, 100);
}, false);
window.addEventListener('transitionend', () => {
console.log('transition ended');
this.doTick();
}, false);
this.tickValue = new Date().getTime();
setTimeout(() => {
this.doTick();
});
}
/**
* getTickValue() returns a different value when something changed (orientation, keyboard up/down).
* by adding this to the screen, the Ionic caret will be adjusted properly
*/
getTickValue(): string {
return this.tickValue + ' ' + window.innerWidth + ' ' + window.innerHeight + ' ' + window.orientation;
}
doTick(): void {
this.tickValue = new Date().getTime();
this.onTick.emit(String(this.tickValue));
this.appRef.tick();
}
}
kb-scroll.ts
import {ApplicationRef, Directive, ElementRef, HostListener, Renderer2} from '#angular/core';
import {DeviceService} from './device.service';
#Directive({
selector: '[kb-scroll]' // Attribute selector
})
export class KbScrollDirective {
constructor(public appRef: ApplicationRef,
public elm: ElementRef,
public renderer: Renderer2,
public device: DeviceService) {
}
#HostListener('click', ['$event'])
onClick($event) {
let elmClickedY;
let scrollContent: HTMLElement;
if ('TEXTAREA' === this.elm.nativeElement.tagName) {
scrollContent = $event.toElement;
elmClickedY = $event.offsetY;
} else if ('ION-CONTENT' === this.elm.nativeElement.tagName) {
// calculate Y offset between click and top of scroll-content area
scrollContent = this.elm.nativeElement.querySelector('.scroll-content');
if (scrollContent) {
// $event.offsetY is most likely small offset in clicked input field in scroll content
// calculate the offsetY opposed to the container (div.scroll-content)
let clickScreenY = $event.toElement.getBoundingClientRect().top + $event.offsetY;
let scrollContentScreenY = scrollContent.getBoundingClientRect().top;
elmClickedY = clickScreenY - scrollContentScreenY;
} else {
console.warn('KbScrollDirective: could not find .scroll-content div in ', this.elm.nativeElement);
}
} else {
console.warn('KbScrollDirective: Can\'t handle ', this.elm.nativeElement.tagName);
}
//TODO: OK to 'RE-ASSIGN' window.onresize ?
window.onresize = () => {
if (scrollContent) {
setTimeout(() => {
let elmHeight = scrollContent.clientHeight;
if (elmClickedY > elmHeight) {
let toScroll = elmClickedY - elmHeight;
scrollContent.scrollTop += toScroll + 40;
this.device.doTick();
}
}, 100);
}
}
}
}
4) Add this service and directive to you module
5) Use the kb-scroll directive on your <ion-content> HTML tag:
<ion-content kb-scroll>
... your form here ...
</ion-content>
6) now comes the really dirty part (and it will become clear why we need the service).
It seems that the repaint of the Ionic caret is triggered when something changes on the screen, so we need to force that.
I added a <span> to my HTML template that holds the root <ion-nav> tag:
app.html
<!-- invisible ticker, needed to get Keyboard caret into place -->
<div style="position: absolute; top:-100px">{{tickValue}}</div>
<!-- Disable swipe-to-go-back because it's poor UX to combine STGB with side menus -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false" class="root-content"></ion-nav>
7) Add this code in your app.component.ts:
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
...
tickValue: string;
constructor(...,
public device: DeviceService) {
this.device.onTick.subscribe(tickValue => {
this.tickValue = tickValue;
});
}
So whenever the DeviceService#tickValue get's updated, this subscriber will update the app component's tick-value, causing a repaint of the screen, although the tick value is rendered outside the visible area of the screen. This seems to cause the Ionic caret to be positioned correctly.
Bonus: this also works for a textarea that takes more place than half the screen (= screen height - keyboard height). Just add the kb-scroll directive to your <textarea> HTML tag.
Please note this is a very hackish solution.
Any comments/improvements are really welcome!
I'm attempting to merge an angular and MVC application together. I'm just trying it on a blank MVC app for beginning purposes by following instructions from a course on pluralsight. However, I'm getting the following error in the console when I try to use the my app tags to test the functionality.
Pricing:56 Error: (SystemJS) XHR error (404 Not Found) loading
http://localhost:5794/src/app/app.module Error: XHR error (404 Not
Found) loading http://localhost:5794/src/app/app.module
at XMLHttpRequest.wrapFn (http://localhost:5794/node_modules/zone.js/dist/zone.js:1166:39)
at ZoneDelegate.invokeTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:425:31)
at Zone.runTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:192:47)
at ZoneTask.invokeTask [as invoke] (http://localhost:5794/node_modules/zone.js/dist/zone.js:499:34)
at invokeTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:1540:14)
at XMLHttpRequest.globalZoneAwareCallback (http://localhost:5794/node_modules/zone.js/dist/zone.js:1566:17)
Error loading http://localhost:57594/src/app/app.module as
"./app/app.module" from http://localhost:57594/src/main.js
at XMLHttpRequest.wrapFn (http://localhost:5794/node_modules/zone.js/dist/zone.js:1166:39)
at ZoneDelegate.invokeTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:425:31)
at Zone.runTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:192:47)
at ZoneTask.invokeTask [as invoke] (http://localhost:5794/node_modules/zone.js/dist/zone.js:499:34)
at invokeTask (http://localhost:5794/node_modules/zone.js/dist/zone.js:1540:14)
at XMLHttpRequest.globalZoneAwareCallback (http://localhost:5794/node_modules/zone.js/dist/zone.js:1566:17)
Error loading http://localhost:5794/src/app/app.module as
"./app/app.module" from http://localhost:5794/src/main.js
Looks like it's obviously related to the zone.js file, but I can't figure this out for the life of me.
systemjs.config.js code:
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': '/node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': '/app',
// angular bundles
'#angular/core': 'npm:#angular/core/bundles/core.umd.js',
'#angular/common': 'npm:#angular/common/bundles/common.umd.js',
'#angular/compiler': 'npm:#angular/compiler/bundles/compiler.umd.js',
'#angular/platform-browser': 'npm:#angular/platform-browser/bundles/platform-browser.umd.js',
'#angular/platform-browser-dynamic': 'npm:#angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'#angular/http': 'npm:#angular/http/bundles/http.umd.js',
'#angular/router': 'npm:#angular/router/bundles/router.umd.js',
'#angular/forms': 'npm:#angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: 'main.js',
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
package.json code:
{
"name": "angular-quickstart",
"version": "1.0.0",
"description": "QuickStart package.json from the documentation, supplemented with testing support",
"scripts": {
"build": "tsc -p src/",
"build:watch": "tsc -p src/ -w",
"build:e2e": "tsc -p e2e/",
"serve": "lite-server -c=bs-config.json",
"serve:e2e": "lite-server -c=bs-config.e2e.json",
"prestart": "npm run build",
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
"pree2e": "npm run build:e2e",
"e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first",
"preprotractor": "webdriver-manager update",
"protractor": "protractor protractor.config.js",
"pretest": "npm run build",
"test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
"pretest:once": "npm run build",
"test:once": "karma start karma.conf.js --single-run",
"lint": "tslint ./src/**/*.ts -t verbose"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"#angular/common": "~4.3.4",
"#angular/compiler": "~4.3.4",
"#angular/core": "~4.3.4",
"#angular/forms": "~4.3.4",
"#angular/http": "~4.3.4",
"#angular/platform-browser": "~4.3.4",
"#angular/platform-browser-dynamic": "~4.3.4",
"#angular/router": "~4.3.4",
"angular-in-memory-web-api": "~0.3.0",
"systemjs": "0.19.40",
"core-js": "^2.4.1",
"rxjs": "5.0.1",
"zone.js": "^0.6.23"
},
"devDependencies": {
"concurrently": "^3.2.0",
"lite-server": "^2.2.2",
"typescript": "~2.1.0",
"canonical-path": "0.0.2",
"tslint": "^3.15.1",
"lodash": "^4.16.4",
"jasmine-core": "~2.4.1",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~4.0.14",
"rimraf": "^2.5.4",
"#types/node": "^6.0.46",
"#types/jasmine": "2.5.36"
},
"repository": {}
}
main.ts code
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
app.module.ts code:
import { NgModule } from '#angular/core';
import { APP_BASE_HREF } from '#angular/common';
import { BrowserModule } from '#angular/platform-browser';
import { ReactiveFormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { routing } from './app.routing';
import { HomeComponent } from './components/home.component';
#NgModule({
imports: [BrowserModule, ReactiveFormsModule, HttpModule, routing],
declarations: [AppComponent, HomeComponent],
providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component code:
import { Component } from "#angular/core"
#Component({
selector: "user-app",
template: `
<div>
<nav class='navbar navbar-inverse'>
<div class='container-fluid'>
<ul class='nav navbar-nav'>
<li><a [routerLink]="['home']">Home</a></li>
</ul>
</div>
</nav>
<div class='container'>
<router-outlet></router-outlet>
</div>
</div>
`
})
export class AppComponent {
}
home.component.ts code
import { Component } from "#angular/core";
#Component({
template: `<img src="../../images/users.png" style="text-align:center"/>`
})
export class HomeComponent {
}
Thoughts?
this is my Component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'homeView',
templateUrl: '/home/home'
})
export class HomeViewComponent implements OnInit {
ngOnInit(): void {}
}
I am using AngularJS 2 with TypeScript 1.8.5 and trying to create component that will load template from Controller Action.
I am getting this error
main.bundle.js:667 Uncaught Error: Cannot find module ".//home/home"
I also tried it with home/home, the error is almost the same
main.bundle.js:667 Uncaught Error: Cannot find module "./home/home"
Error is not compile time error however - it pops up in browser console when page and the component is being loaded
So as you can see i dont want to load static template like home.template.html - that is working correctly. I want to load an HTML template from asp.net MVC Controller Action. I am not even hitting the debug point in the HomeController Home action.
Is there any way to make this work ? Seems like Angular keeps inserting this './' sign. Is there a way to configure this ? I ve read several tutorials on angular2 + mvc and it seems that this should be possible, but for some reason its not working for me.
My app.routes.ts
import {Routes} from "#angular/router";
import {MainViewComponent} from "../views/main-view/main-view.component";
import {MinorViewComponent} from "../views/minor-view/minor-view.component";
import {HomeViewComponent} from "../views/home-view/home-view.component";
export const ROUTES:Routes = [
// Main redirect
{ path: '', redirectTo: 'mainView', pathMatch: 'full'},
// App views
{path: 'mainView', component: MainViewComponent},
{path: 'minorView', component: MinorViewComponent},
{path: 'homeView', component: HomeViewComponent}
// Handle all other routes
//{path: '**', component: MainViewComponent }
];
app.module.ts
#NgModule({
declarations: [AppComponent],
imports : [
// Angular modules
BrowserModule,
HttpModule,
// Views
MainViewModule,
MinorViewModule,
// Modules
NavigationModule,
FooterModule,
TopnavbarModule,
RouterModule.forRoot(Approutes.ROUTES)
],
providers : [{provide: LocationStrategy, useClass: HashLocationStrategy}],
bootstrap : [AppComponent]
})
export class AppModule {}
EDIT : Some more info :
packages.json
{
"name": "inspinia_angular2_starter",
"version": "1.0.0",
"description": "Inspinia Admin Theme",
"repository": "https://wrapbootstrap.com/theme/inspinia-responsive-admin-theme-WB0R5L90S",
"scripts": {
"typings-install": "typings install",
"postinstall": "npm run typings-install",
"build": "webpack --inline --colors --progress --display-error-details --display-cached",
"server": "webpack-dev-server --inline --colors --progress --display-error-details --display-cached --port 3000 --content-base src",
"start": "npm run server"
},
"dependencies": {
"#angular/common": "2.0.0",
"#angular/compiler": "2.0.0",
"#angular/core": "2.0.0",
"#angular/forms": "2.0.0",
"#angular/http": "2.0.0",
"#angular/platform-browser": "2.0.0",
"#angular/platform-browser-dynamic": "2.0.0",
"#angular/router": "3.0.0",
"#angular/upgrade": "2.0.0",
"angular2-in-memory-web-api": "0.0.20",
"animate.css": "3.1.1",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"font-awesome": "^4.6.1",
"ie-shim": "^0.1.0",
"jquery": "^3.1.0",
"metismenu": "^2.5.0",
"pace": "0.0.4",
"pace-progress": "^1.0.2",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"typings": "^1.3.2",
"zone.js": "^0.6.23"
},
"devDependencies": {
"angular2-template-loader": "^0.4.0",
"awesome-typescript-loader": "^1.1.1",
"bootstrap-webpack": "0.0.5",
"css-loader": "^0.23.1",
"exports-loader": "^0.6.3",
"expose-loader": "^0.7.1",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"imports-loader": "^0.6.5",
"raw-loader": "^0.5.1",
"style-loader": "^0.13.1",
"to-string-loader": "^1.1.4",
"typescript": "~1.8.5",
"url-loader": "^0.5.7",
"webpack": "^1.12.9",
"webpack-dev-server": "^1.14.0",
"webpack-merge": "^0.8.4"
}
}
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "dist",
"rootDir": ".",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"moduleResolution": "node"
},
"exclude": [
"node_modules",
"bower_components"
],
"awesomeTypescriptLoaderOptions": {
"useWebpackText": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}
App is built using webpack
main.browser.ts
import {platformBrowserDynamic} from "#angular/platform-browser-dynamic";
import {AppModule} from "../src/app/app.module";
/*
* Bootstrap Angular app with a top level NgModule
*/
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
So, after what seemed like an eternity I finally got the the bottom of this.
The problem was in webpack using angular2-template-loader.
This module is responsible for inlining html template. In other words, it will do a require (using your templateUrl) behind the scenes on the template tag of your component. This, of course, breaks the functionality if you are using actual URLs as templateUrl and not PATHs, therefore the template is not there.
Based on what i read, it seems like the authors wont support this in the future. (If template is not found -> try to load it from URL)
More info here - https://github.com/angular/angular-cli/issues/1605
So, basically I just removed this component from config file :
webpack.config.js
BEFORE
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.css$/,
loader:
AFTER
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader']
},
{
test: /\.css$/,
loader: