Angular 2 ngFor object property change not updated its view - angular2-changedetection

I have did the following
<div *ngFor="let item of documentData">
<polymer-component [data]="item"></polymer-component>
</div>
<button (click)="ChangePropertyValue()">ChangePropertyValue</button>
ChangePropertyValue(){
this.documentData[0].documentname="Document changed";
}
ngOnInit(){
this.documentData={"documentname":"Document"}
}
Polymer-Component has properties such as
documentname
When firing the ChangePropertyValue() , the object and its property is being updated but not its view. Please provide solution as soon as possible.
I have also tried ChangeDetectorRef,still it results nothing

Try running inside of angular's zone
import { NgZone } from '#angular/core';
constructor(private ngZone: NgZone) {
}
ChangePropertyValue(){
this.ngZone.run(() => {
this.documentData[0].documentname="Document changed"
})
}

Related

Angular2 How to POST data using a service class

I have a simple application form which I am trying to post to the server. I am fairly new to Angular2
How can I pass the data from the component to the service and onto the server for a POST request.
The POST is working fine when I try it directly from FireFox plugin 'httpRequester'
This is the TaskComponent.ts
#Component({
selector: 'tasks',
template: `<div mdl class="mdl-grid demo-content">
<div class="demo-graphs mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--8-col">
<h3>Create Task Page</h3>
<form action="#" (ngSubmit)="onSubmit()">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" pattern="[A-Z,a-z]*" id="taskname" [(ngModel)]="data.taskname"/>
<label class="mdl-textfield__label" for="taskname">Task Name</label>
<span class="mdl-textfield__error">Only alphabet and no spaces, please!</span>
</div>
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" type="submit">Create Task</button>
</form>
`,
directives: [ROUTER_DIRECTIVES, MDL]
})
export class CreateTaskComponent {
data: any
constructor() {
this.data = {
//taskname: 'Example Task'
};
}
onSubmit(form) {
console.log(this.data.taskname); <--Data is passed upon submit onto the console. Works fine.
//Need to call the postApartment method of ApartmentService
}
}
ApartmentService.ts
import {Http, Response} from 'angular2/http'
import {Injectable} from 'angular2/core'
import 'rxjs/add/operator/map';
#Injectable()
export class ApartmentService {
http: Http;
constructor(http: Http) {
this.http = http;
}
getEntries() {
return this.http.get('./api/apartments').map((res: Response) => res.json());
}
getProfile(userEmail :string){
return this.http.get(`./api/apartments/getprofile/${userEmail}`).map((res: Response) => res.json());
}
postApartment(){
// Not familiar with the syntax here
}
}
Server.ts
router.route('/api/apartments')
.post(function(req, res) {
var apartment = new Apartment();
apartment.name = req.body.name;
apartment.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Apartment created!' });
});
})
You can inject service via dependency injection and use it in the component
export class CreateTaskComponent {
constructor(){private _apartmentService: ApartmentService}{}
}
And you can access this in any of the component function via
onSubmit(form) {
console.log(this.data.taskname); <--Data is passed upon submit onto the console. Works fine.
//Need to call the postApartment method of ApartmentService
this._apartmentService.postApartment()
}
And when bootstraping the component you have to add it as dependency via
bootstrap(AppComponent, [ApartmentService]);
Another option for doing the last step is by added providers in the Component decorator like
#Component{
providers: [ApartmentService]
}
Inject the apartmentService in the component, No need of providers as I have bootstrapped it. (If you bootstartp the service, Do not include it in providers. It breaks the system)
export class CreateTaskComponent {
data: any
constructor(private apartmentService: ApartmentService) {
this.data = {};
}
onSubmit(form) {
this.apartmentService.postApartment(this.data);
}
}
The critical piece is the postApartment() method in the service
postApartment(data :any){
return this.http.post('/api/apartments',
JSON.stringify(data),{headers : new Headers({'Content-Type':'application/json'})
})
.map((res: Response) => res.json()).subscribe();
}
Also make sure on the server.js code, the mongoose fields match the http body parameters being passed.
I had to fix it to make it work.

How to select tabs in ionic 2?

I'm struggling to create a basic application in Ionic2 that uses both the side-menu and the tabs navigation. I understand the concepts of the navigation stack and that each tab has its own navigation stack, but I can't grasp the control over the tabs themselves.
The tabs starter template initializes a project with one ion-nav having its rootpage pointing to "rootPage", a property of the #App pointing to a TabsPage class.
<ion-nav id="nav" [root]="rootPage" #content></ion-nav>
The TabsPage class defines 3 properties, one for each page, pointing to their respective classes (each class decorated with #Page). But the TabsPage class itself doesn't seem to have any function, or be injected with a tabs controller and I find little to no documentation on how to acquire a Tabs instance (there are instance methods referenced on http://ionicframework.com/docs/v2/api/components/tabs/Tabs/)
What I managed to do:
Use one tab to control the other.
import {Page, Tabs} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/timeline/timeline.html'
})
export class Timeline {
tabs:Tabs;
constructor(tabs:Tabs) {
this.tabs=tabs;
this.selectTab(2);
}
selectTab(i:number) {
this.tabs.select(i);
}
}
The page above is injected with a Tabs instance, which inherits from NavController. The Tabs instance has the desired select method, and I can point to a different tab (by index, not by name). So in this situation selecting my 'timeline' tab will trigger the constructor, and instead of going to the timeline tab we end up selecting the 2nd tab.
What I would like to do: navigate to a tab with a link in the side-menu.
My side-menu consists of two ion-items, simple buttons with a click listener. In Ionic 1.x I could use a ui-sref or a href to match a certain state, but in Ionic 2 I can't figure out how to control my tabs.
I can access the ion-nav by giving it an id and using app.getComponent('nav'), but I can not target the ion-tabs this way (hoping it would be bound to a Tabs controller instance).
Each ion-tab is a declarative component for a NavController. Basically, each tab is a NavController. For more information on using navigation controllers take a look at the NavController API Docs.
So to access the array of tabs from inside a specific tab page (component) we can set our target path with as simple as :
NavController.parent
Now suppose, we are in a child page of one of our tabs - the component class will be somewhat similar as below:
import { Component } from '#angular/core';
import { NavController, Nav , Tabs } from 'ionic-angular';
// import Tabs
import { Page2} from '../page-2/page-2';
import { Page3} from '../page-3/page-3';
#Component({
templateUrl: 'build/pages/page-1/page1.html'
})
export class Page1 {
tab:Tabs;
// create a class variable to store the reference of the tabs
constructor(public navCtrl: NavController, private nav: Nav) {
this.tab = this.navCtrl.parent;
/*Since Tabs are declarative component of the NavController
- it is accessible from within a child component.
this.tab - actually stores an array of all the tabs defined
in the tab.html / tab component.
*/
}
goToTab2 (){
this.tab.select(1);
// the above line is self explanatory. We are just calling the select() method of the tab
}
goToTab3 (){
this.tab.select(2);
}
}
Hope this helps.
I get this working by following:
app.ts
import {App, IonicApp, Platform} from 'ionic-angular';
#App({
template: '<ion-nav id="nav" [root]="rootPage"></ion-nav>',
})
export class TestApp {
rootPage: any = TabsPage;
constructor(
private platform: Platform,
private app: IonicApp
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
});
}
}
tabs.html
<ion-menu [content]="content">
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<ion-item (click)="openPage(p)" *ngFor="#p of pages;>
<span>{{p.title}}</span>
</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<ion-tabs id="navTabs" #content swipe-back-enabled="false">
<ion-tab [root]="tab1"></ion-tab>
<ion-tab [root]="tab2"></ion-tab>
<ion-tab [root]="tab3"></ion-tab>
</ion-tabs>
tabs.ts
import {Page, NavController, MenuController} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/tabs/tabs.html'
})
export class TabsPage {
pages: Array<{ title: string, component: any }>;
tab1: any = Tab1;
tab2: any = Tab2;
tab3: any = Tab3;
page: any = Page;
constructor(private nav: NavController,
private menu: MenuController) {
this.tab1 = Tab1;
this.tab2 = Tab2;
this.tab3 = Tab3;
this.pages = [
{ title: 'page-menu', component: Page }
];
}
openPage(page) {
// close the menu when clicking a link from the menu
this.menu.close();
// navigate to the new page if it is not the current page
}
}
my solution:
tabs.html : The main tip here is use #tabs
<ion-tabs tabs-only tabsLayout="icon-start" color="light" #tabs>
<ion-tab [root]="tabPage1" tabTitle="Mapa" tabIcon="qi-location arrow">
</ion-tab>
<ion-tab [root]="tabPage2" tabTitle="Em Andamento" tabIcon="qi-th-list" tabBadgeStyle="danger"> </ion-tab>
</ion-tabs>
tabs.ts: Use #ViewChild to bind the wild card used in tabs.html
#Component({
selector: 'page-tabs',
templateUrl: 'tabs.html',
})
export class TabsPage {
#ViewChild('tabs') tabRef: Tabs;
tabPage1: any = HomePage;
tabPage2: any = InProgressPage;
constructor() {
}
switchToHomePage(){
this.tabRef.select(0);
}
switchToInProgressPage(){
this.tabRef.select(1);
}
}
Another page to redirectc : Use #Inject(forwardRef(() => TabsPage)) to get access to parent page methods.
export class AnotherPage {
constructor(#Inject(forwardRef(() => TabsPage)) private tabsPage:TabsPage){}
switchToHome(){
this.tabsPage.switchToHomePage();
}
}

Angular 2 http post reloads page, logout doesn't delete session cookie

I have an Angular 2 service that has a logout function. When the function is called from the app component it cause a full page refresh. When using angular 1 projects I haven't experienced this behavior. If I call my logout endpoint with postman the session cookie is deleted. It is not deleted if I use my angular 2 authentication service.
Service
import {Injectable} from 'angular2/core';
import {User} from './user';
import {Headers, RequestOptions, Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import {Cookie} from '../extensions/cookies';
#Injectable()
export class AuthenticationService {
constructor (private http: Http) {}
private _prepTestHost = 'http://localhost:8000/';
private _prepTestLoginUrl = this._prepTestHost + 'login/';
private _prepTestLogoutUrl = this._prepTestHost + 'logout/';
private _authenticated: boolean;
getUser() {}
isAuthenticated() {
return this._authenticated;
}
setAuthenticated() {
this._authenticated = true;
}
loginUser(username, password) : Observable<User> {
let body = JSON.stringify({username, password});
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this._prepTestLoginUrl, body, options)
.map(res => <User> res.json(), this.setAuthenticated())
.catch(this.handleError)
}
logoutUser() : Observable<void> {
let body = JSON.stringify({});
let csrfcookie = Cookie.getCookie('csrftoken');
let headers = new Headers({
'X-CSRFToken': csrfcookie,
'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers});
return this.http.post(this._prepTestLogoutUrl, body, options)
.map(res => <void> res.json())
.catch(this.handleError);
}
private handleError (error: Response) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
App Component
import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {WelcomeCenterComponent} from './welcome-center/welcome-center.component';
import {AuthenticationService} from './authentication/authentication.service';
import {LoginModalComponent} from './authentication/login-modal.component';
import {BrowserXhr, HTTP_PROVIDERS} from "angular2/http";
import {CORSBrowserXHR} from './extensions/corsbrowserxhr';
import {provide} from "angular2/core";
#Component({
selector: 'my-app',
template: `
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#" [routerLink]="['WelcomeCenter']">Brand</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li *ngIf="!authenticated()">
Login
</li>
<li *ngIf="authenticated()">
Logout
</li>
</ul>
</div>
</nav>
<router-outlet></router-outlet>
<login-modal></login-modal>
`,
directives: [ROUTER_DIRECTIVES, LoginModalComponent],
providers: [HTTP_PROVIDERS,
provide(BrowserXhr, {useClass: CORSBrowserXHR}),
AuthenticationService]
})
#RouteConfig([
{
path: '/welcome-center/...',
name: 'WelcomeCenter',
component: WelcomeCenterComponent,
useAsDefault: true
}
])
export class AppComponent {
constructor(private _authenticationService: AuthenticationService) {}
authenticated() {
return this._authenticationService.isAuthenticated();
}
logout() {
console.log("Logout button pressed");
this._authenticationService.logoutUser().subscribe();
}
}
Setting withCredentials attribute:
import {BrowserXhr, HTTP_PROVIDERS} from "angular2/http";
import {Injectable, provide} from "angular2/core";
#Injectable()
export class CORSBrowserXHR extends BrowserXhr{
build(): any{
var xhr:any = super.build();
xhr.withCredentials = true;
return xhr;
}
}
I think that the page reload is due to the fact that you don't prevent event propagation when blocking on the layout button (you an 'a' HTML element with an 'href' attribute). You could use 'return false' at the end of your logout function or '$event.stopPropagation()'.
See the question for more details:
Stop event propagation in Angular 2
Regarding the cookie problem, I these that you use cross domain requests (CORS). I think that you should try to set to true the 'withCredentials' attribute on the underlying XHR object. See this question for more details:
Set-cookie in response not set for Angular2 post request
You could do something kind of hacky but I can't think of another way.
Cookie.setCookie(nameOfCookie, "", -1);
This would effectively delete the cookie on logout. I'd love to know if there was a better way though!
I also am not sure why you are getting any kind of page reload at all, I have yet to experience that on anything I've done yet, hopefully someone else will know.

Select a specific tab in Ionic 2

I'm trying to use the Ionic 2 and I'm still struggling with most basic tasks, such as select a tab when the app is loading.
I've tried to inject the Tabs controller and call select on the onPageLoaded event, but to no avail.
Can someone help maybe?
To default to a tab in ionic 2 change the selectedIndex property:
<ion-tabs [selectedIndex]="1">
<ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="rewind"></ion-tab> <!-- Index 0-->
<ion-tab [root]="tab2Root" tabTitle="About" tabIcon="md-time"></ion-tab><!-- Index 1 (Selected)-->
<ion-tab [root]="tab3Root" tabTitle="Contacts" tabIcon="fastforward"></ion-tab><!-- Index 2-->
</ion-tabs>
The About tab will be the selected tab when the page loads.
//importing tabs for manipuling our ion-tabs
import {Tabs} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1
{
//provide Angular with metadata about things it should inject in the constructor
static get parameters()
{
return [[Tabs]];
}
//after injecting ,passing an instance of [Tabs] in the page constructor
constructor(tab) {
this.tab = tab;
}
//"onPageWillEnter" function fires every time a page becomes the active view.
onPageWillEnter()
{
//make the second tab selected From the first tab (within the current Page 'page1')
// 1 IS the index of the target tab
this.tab.select(1);
}
}
try
var t: Tabs = this.nav.parent;
t.select(index);
In Ionic 3 and angular 4.
import { Tabs } from 'ionic-angular/navigation/nav-interfaces';
#ViewChild('myTabs') tabRef: Tabs; - With in the class about constructor.
this.tabRef.select(0, {}); // In the method where you want set tab.
This is the way to do it in 2022 (Ionic 5)
<ion-tabs #tabs>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="map">
<ion-icon name="map"></ion-icon>
<ion-label>Map</ion-label>
</ion-tab-button>
...
</ion-tab-bar>
<ion-tabs>
Then in your typescript
import { IonTabs } from '#ionic/angular';
#Component({
selector: 'app-some-component',
templateUrl: './some-component.page.html',
styleUrls: ['./some-component.page.scss'],
})
export class SomeComponentPage implements OnInit {
#ViewChild('tabs') tabs: IonTabs;
constructor() {}
ngOnInit() {
this.tabs.select('pre-operative-preparation')
}
}

ionicView.enter not fired on child view and its controller

I have two nested states, consisted of a parent abstract state and a child state:
.state('app.heatingControllerDetails', {
url: "/clients/:clientId/heatingControllers/:heatingControllerId",
abstract: true,
views: {
'menuContent': {
templateUrl: "templates/heatingController.html",
controller: 'HCDetailsCtrl'
}
}
})
.state('app.heatingControllerDetails.wdc', {
url: "/wdc",
views: {
'hc-details': {
templateUrl: "templates/heatingControllers/wdc.html",
controller: 'WdcDetailsCtrl'
}
},
resolve:{
hcFamily: [function(){
return 'wdc';
}]
}
})
and two controllers are:
.controller('HCDetailsCtrl',function($scope){
$scope.$on("$ionicView.enter", function (scopes, states) {
...
});
})
.controller('WdcDetailsCtrl',function($scope){
$scope.$on("$ionicView.enter", function (scopes, states) {
...
});
})
When I invoke state app.heatingControllerDetails.wdc, both controllers are created, but $ionicView.enter is only invoked on the parent controller. Any idea?
In heatingController.html, hc-details view is defined as follows:
<ion-content class="has-header" ng-show="hc">
<div ui-view name="hc-details"></div>
<div class="disableContentDiv" ng-hide="hc.state=='Online'"></div>
</ion-content>
When working with nested views, you have to use $ionicNavView events instead of $ionicView
That being said, at the last release these events are bugged, and they're currently working in a fix: Github issue
I found a work around for this. Place this in a parent of the view, or place it in the first controller that is loaded when running your application.
You can use this to observe any 'to and from' view changes. Just doing a string comparison on the url of the toState emulated ionicView.enter well enough to achieve what I needed it for. Keep in mind you need to be using UI-router to do this. Hope this helps!
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams, options){
if(toState.url == "/video/:Id"){
console.log("Leaving the view.");
}
});