Change detection running infinitely when function is used with ngIf - angularjs

I am working on an Ionic 2 project. The template has a ngIf to check if its the right user.
show.ts
constructor( public book: Bookemon ){}
rightUser(){
console.log('right user');
return this.book.rightUser(this.hunt.user.id);
}
bookemon.ts (bookemon and auth are services )
rightUser(id: number){
return this.auth.user.id === id;
}
show.html
<ion-content fullscreen="true">
<div id="image-modal" >
<img [src]="imageSource(hunt.picture_url)" (click)="dismissModal()" >
<button ion-fab mini *ngIf = " !hunt.picture_url " (click)="showEditHuntModal()" color="ionic">
<ion-icon name="add" ></ion-icon>
</button>
<button color="danger" class="new-claim" ion-button round icon-left *ngIf = "rightUser() && hunt.claims.length " (click)="openClaimModal(hunt)">
<ion-icon name="trophy"></ion-icon>
New Claim
</button>
</div>
<div class="hunt-content">
<button small clear ion-button icon-only class="options" *ngIf="rightUser()" (click)="presentAction()">
<ion-icon name="md-more"></ion-icon>
</button>
<hunt [hunt]="hunt" [huntedUser]="huntedUser" ></hunt>
</div>
<ion-fab *ngIf="hunt.dfu !== undefined" bottom (click)="dismissModal('rejected')" class="close-fab">
<button ion-fab color="danger" ><ion-icon name="close" ></ion-icon></button>
</ion-fab>
<ion-fab *ngIf="hunt.dfu !== undefined" bottom (click)="dismissModal('accepted')" class="heart-fab">
<button ion-fab color="primary" ><ion-icon name="heart" ></ion-icon></button>
</ion-fab>
</ion-content>
The template displays some content based on *ngIf = "rightUser()"
Everything works fine except , the console is logged with infinitely many right user which comes from rightUser() function. The expected behaviour should be right user logged once in console and no more change detection being run.
Is it a bad practice to use functions in ngIf ? What is triggering change detection infinite times here?
Edit
I tried a function with ngIf in a fresh project and it doesnt run change detection infinite times. I am not sure whats causing it here. Here is my show.ts in case that helps.
export class ShowPage {
hunt: Hunt ;
huntedUser: User;
editHuntPage= EditHuntPage;
claimHuntPage = ClaimHuntPage;
rightUser: Boolean;
constructor(public alert: AlertController , public modal: ModalController, public events: Events
, public toastCtrl: ToastController, private navParams: NavParams , public action: ActionSheetController
, public book: Bookemon , public viewCtrl: ViewController, public nav: NavController) {
console.log(navParams.data);
this.hunt = navParams.data.hunt;
this.rightUser = this.book.rightUser(this.hunt.id);
}
ionViewDidLoad(){
console.log('view');
if(this.hunt.status == "hunted"){
this .book.getHuntedUser(this.hunt).subscribe( (res) => {
this.huntedUser = res.json();
console.log(this.huntedUser);
})
}
}
imageSource(pic: string){
return pic ? pic : CONFIG.noHuntPic ;
}
dismissModal(status?: string){
console.log('dismiss');
this.viewCtrl.dismiss(status);
}
presentAction(){
let actionSheet = this.action.create(
{
title: this.hunt.title ,
buttons: [
{
text: 'Edit',
handler: () => {
let trans = actionSheet.dismiss();
trans.then( () => {
this.showEditHuntModal();
})
return false;
}
},
{
text: 'Delete',
role: 'destructive',
handler: () => {
let trans = actionSheet.dismiss();
trans.then( () => {
this.deleteAlert();
})
return false;
}
},
{
text: 'Cancel',
role: 'cancel',
}
]
})
actionSheet.present();
}
showEditHuntModal(){
let modal = this.modal.create(EditHuntPage,this.hunt);
modal.present();
modal.onDidDismiss( (data) => {
if(data){
this.hunt = data;
}
})
}
deleteAlert(){
let alert = this.alert.create({
title: 'Confirm',
message: 'Do you want to delete this hunt?',
buttons: [{
text: "Delete",
handler: () => { this.deleteHunt() }
}, {
text: "Cancel",
role: 'cancel'
}]
})
alert.present();
}
deleteHunt(){
this.book.deleteHunt(this.hunt.id).subscribe( (res) => {
// this.events.publish("user-hunts:deleted",this.hunt);
this.viewCtrl.dismiss("deleted",this.hunt);
}, (err) => {
this.handleError(err);
})
}
openClaimModal(hunt){
let claimModal = this.modal.create(ShowClaimPage,{hunt: hunt})
claimModal.present();
}
rightUser(){
console.log('right user');
return this.book.rightUser(this.hunt.user.id);
}
handleError(err){
console.log(err);
let error= err.message ? err.message : "Error occured";
let toast = this.toastCtrl.create({ message: error, duration: 2000 , position: 'top'});
toast.present();
}
}

angular uses zones.js to detect if something changes in your app so it will check ngIf each time, you can this something like this :
<button small clear ion-button icon-only class="options" *ngIf="isRightUser" (click)="presentAction()">
<ion-icon name="md-more"></ion-icon>
</button>
//...
<button small clear ion-button icon-only class="options" *ngIf="isRightUser" (click)="presentAction()">
//show.ts
isRightUser:boolean;
constructor( public book: Bookemon ){}
ngOnInit(){
this.isRightUser= rightUser();
}
rightUser(){
return this.book.rightUser(this.hunt.user.id);
}

Related

when i push select2OptionData object in an array, select2 don't display any value

select2 in my angular project. I want to display a select of an array of certificate requested from an api. For each element if I create and push a select2OptionData Object in an array, but when i want to display it in the Html page it is selected but without any option. When i create an array of object like the gitHub demo everything's right.
Can you help me please.
Here is the ts
export class DashboardCvComponent implements OnInit, OnDestroy {
certificateForm: FormGroup;
certificates: Array<Certificate>;
certificatesTypes: Array<Select2OptionData>;
exampleData: Array<Select2OptionData>;
constructor(private formBuilder: FormBuilder, private certificatesService: CertificatesService,
private trainingsService: TrainingsService, private authService: AuthenticationService,
private datepipe: DateFormatPipe, private CvService: CVService, private fileUploaService: FileUploadService) {
this.certificatesTypes = [];
this.exampleData = [];
}
ngOnInit() {
// get certificate types
this.certificatesService.getAllCertificatesType('50').subscribe(
(response) => {
// console.log('-> CERTIFICATES TYPES LOADING successful : ', response);
/* this.certificatesTypes = response.data; */
response.data.forEach(element => {
const certif = {id: element.id.toString(), text: element.name};
this.certificatesTypes.push(certif);
});
this.exampleData = [{
id: 'basic1',
text: 'Basic 1'
},
{
id: 'basic2',
disabled: true,
text: 'Basic 2'
},
{
id: 'basic3',
text: 'Basic 3'
},
{
id: 'basic4',
text: 'Basic 4'
}]
console.log('les certif', this.exampleData, this.certificatesTypes);
},
(error) => {
console.log('-> CERTIFICATES TYPES LOADING failed : ', error);
},
() => {
}
);
and the html
<div class="dashboard--degrees">
<app-certificate *ngFor="let certificate of certificates" [certificate]=certificate [url]=url></app-certificate>
<button class="button button--bluegreen" type="button" data-modal="modal-certificate">
<svg class="icon icon-plus-o">
<use xlink:href="#icon-plus-o"></use>
</svg> <span i18n="##cvcomponent-addcertificate">J'ajoute un diplôme </span>
</button>
<select2 [data]="certificatesTypes"></select2>
<select2 [data]="exampleData"></select2>
</div>
Here the exampleData select display well but not the certificatesTypes
Don't use this.certificate.push inside for loop. it will not work. instead of you can use something like :
let arrCertificates = [];
response.data.forEach(element => {
const certif = {id: element.id.toString(), text: element.name};
arrCertificates.push(certif);
});
this.certificatesTypes = arrCertificates;

How to pass JSON Array {{this.row}} to submit() function to get posted using Axios.post

this.row is Generating form input into JSON Array which I'm trying to post via submit function using Axios but unable to the value please help what's wrong ??
Axios postcode
axios.post('/submit', this.rows).then(response => {
this.rows; //Clear input fields.
this.loaded = true;
Here is my complete code
<template>
<form #submit.prevent="submit">
{{ /* These are 3 buttons which are calling 3 different function to create input boxes */ }}
<div class="d-flex mt-5"><div>
<label>Add A</label>
<button type="button" #click="addRow">01</button>
</div>
<div> <label>Add B</label>
<button type="button" #click="addRow1">02</button>
</div>
<div> <label>Add c</label>
<button type="button" #click="addRow3">03</button>
</div>
</div>
{{ /* this section calls button-counter template from script code */ }}
<div v-for="row in rows" :key="row.id">
<button-counter :name ="row.name" :id="row.id" :value.sync="row.value"></button-counter>
</div>
<div>
<button type="submit" class="btn btn-primary">Add Points</button>
</div>
<div v-if="success" class="alert alert-success mt-3">
Message sent!
</div>
</form>
</template>
<script>
Vue.component("button-counter", {
props: {
value: {
default: "",
}
},
/* This is my template which gets called fro the addition of new inputs ...guess here I need to add v-model so that dynamically generated fields will be posted but I'm unable to get it posted */
template: '<input class="form-control" id= id.row name=row.name type="number" style="margin-top: 10px;" :value="value" #change="$emit(\'update:value\', $event.target.value)">'
});
export default {
props: ['gameId','userId'],
mounted() {
console.log('Component mounted.')
},
data() {
return {
gamex: this.gameId,
rows: [],
count: 0,
fields: {},
errors: {},
success: false,
loaded: true,
};
},
computed: {
total() {
if (this.rows.length) {
return this.rows.reduce((acc, row) => acc += parseInt(row.value), 0);
}
return 0;
}
},
methods: {
addRow: function() {
var txtCount = 1;
let id = "txt_" + txtCount;
this.rows.push({ name:'zero',value:100, description: "textbox1", id });
},
addRow1: function() {
var txtCount = 1;
let id = "txt2_" + txtCount;
this.rows.push({name:'one',value:200, description: "textbox2", id });
},
addRow3: function() {
var txtCount = 1;
let id = "txt3_" + txtCount;
this.rows.push({name:'two',value:300, description: "textbox3", id });
},
submit: function() {
if (this.loaded) {
this.loaded = false;
this.success = false;
this.errors = {};
axios.post('/submit', this.rows).then(response => {
this.rows; //Clear input fields.
this.loaded = true;
this.success = true;
}).catch(error => {
this.loaded = true;
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
});
}
},
followUser() {
axios.post('/chklet/' + this.userId)
.then(response => {
return response.data ;
});
}
},
mounted () {
this.followUser();
}
};
</script>
You can use JSON.stringify(array_to_convert_in_string_to_send_in_ajax) but you will have to json_decode it also in backend server
In server section for example laravel:
$result = json_decode($request->your_array);

Edit Items in a List Using Modal in Ionic 3

I have created an Array and I'm adding items to the array using Modal.
Now i need to edit the item by selecting it from the list.
please help me with the Code.
How to fetch the item to modal and bring back the edited item in the same place in the list
HOME.HTML
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let grocery of itemsArray">{{grocery}}</ion-item>
</ion-list>
<button ion-button round (click)="addItem()">Add Item</button>
</ion-content>
HOME.TS
export class HomePage {
public itemsArray = [];
newItem: any;
constructor(public navCtrl: NavController, public modalCtrl: ModalController, public navParams: NavParams) {
}
ionViewDidLoad() {
this.newItem = this.navParams.get('data');
this.itemsArray = [
];
}
public addItem() {
let modalPage = this.modalCtrl.create(ModalPage);
modalPage.onDidDismiss(data => {
this.itemsArray.push(data.name
);
});
modalPage.present();
}
}
MODAL.HTML
<ion-header>
<ion-navbar>
<ion-title>Add Item</ion-title>
<ion-buttons end>
<button ion-button (click)="closeModal()">Close</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item>
<ion-label>Item</ion-label>
<ion-input type="text" [(ngModel)]="newItem"></ion-input>
</ion-item>
<button ion-button color="secondary" (click)="add()">Add Item</button>
</ion-list>
</ion-content>
MODAL.TS
export class ModalPage {
name:any;
newItem: any;
constructor(public navCtrl: NavController, public viewCtrl: ViewController, public navParams: NavParams) {
}
ionViewDidLoad() {
console.log('ionViewDidLoad ModalPage');
}
public closeModal() {
this.viewCtrl.dismiss();
}
//add() {
// let data = {"name": this.newItem};
// this.viewCtrl.dismiss(data.name)
// }
add() {
let data = {"name": this.newItem};
this.viewCtrl.dismiss(data)
}
}
The code untill now works fine.
I would recommend using alert controller to simplify your code in your use case. If you would need a modal - you can elaborate the code later.
Try this approach:
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list *ngFor="let grocery of itemsArray; let i = index">
<ion-item (click)="changeItemName(grocery, i)">{{grocery}}</ion-item>
</ion-list>
<button ion-button round (click)="addItem()">Add Item</button>
</ion-content>
Place iterator on the list and capture "index" per item, so that you could pass that value if particular item from the list is clicked together with actual grocery item name.
In your home.ts:
import { Component } from '#angular/core';
import { NavController, NavParams, AlertController, ViewController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public itemsArray = ["milk", "butter", "bread"];
newItem: any;
constructor(public navCtrl: NavController, public alertCtrl: AlertController, public navParams: NavParams) {
}
public changeItemName(currentName, index) {
let alert = this.alertCtrl.create({
title: 'Change grocery item name:',
message: 'current: "' + currentName + '"',
inputs: [
{
placeholder: 'type in a new name'
}
],
buttons: [
{
text: 'Cancel',
role: 'cancel',
},
{
text: 'Confirm',
handler: data => {
if (data[0].length === 0) {
this.itemsArray[index] = currentName;
} else {
this.itemsArray[index] = data[0];
}
}
}
]
});
alert.present();
};
public addItem() {
let index = this.itemsArray.length;
this.changeItemName("New item", index);
}
}
You can use alert controller to show a small pop-up with input field. In the code you will see that we pass parameters to it and we modify list item name OR we add item to the list if change name method was called by addItem method.
Let me know if this is helpful for you.
Here is a working snippet: https://stackblitz.com/edit/ionic-urbtag

laravel vue - axios's methods not working correctly after upload project

After that I uploaded site in xampp's virtualHost axios methods stopped working.(but site is working well)
httpd-vhosts:
NameVirtualHost *:80
<VirtualHost *:80>
DocumentRoot C:\xampp\htdocs\zbudWew\public
ServerName localhost
<Directory C:\xampp\htdocs\zbudWew\public>
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
.env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=zbudwew
DB_USERNAME=root
DB_PASSWORD=
example axios method:
axios.post('api/news/', this.info).then(response => {
this.news.unshift(response.data.info);
this.info = {title: '', body:''};
console.log(response.data);
}, response => {
this.errors = response.data;
});
On my localhost:8000 address site and adding content is working good, but if I am trying add content on my 192.168.0.199 address I am getting errors:
[Vue warn]: Error in render function: "TypeError: Cannot read property 'title' of undefined"
found in
---> <Info> at C:\xampp\htdocs\zbudWew\resources\assets\js\components\Info.vue
<NewsManagement> at C:\xampp\htdocs\zbudWew\resources\assets\js\components\NewsManagement.vue
<Root>
Which is werid, because:
axios.get('api/news').then(response => {
this.news = response.data.news;
});
is working correctly. Could you guys give me an advice how to solve this?
Data property and axios.post looks like this:
data() {
return {
show: false,
news: [],
errors: [],
info: {
title: '',
body: '',
}
}
}, components: {
Info
}, created() {
this.fetchNews();
}, methods: {
fetchNews() {
axios.get('api/news').then(response => {
this.news = response.data.news;
});
}, createInfo() {
axios.post('api/news/', this.info).then(response => {
this.news.unshift(response.data.info);
this.info = {
title: '',
body: ''
};
});
}
Info component looks like this:
<template>
<tr>
<td>
<span id="errorInfo"></span>
<input type="text" class="form-control"
v-model="editForm.title" v-if="edit" placeholder="Tytuł">
<span v-else>{{ info.title }}</span>
<br>
<div v-if="edit">
<textarea id="editorInfo" name="editorInfo" type="text" class="form-control"
v-model="editForm.body" ></textarea>
</div>
<span v-else></span>
</td>
<td align="right">
<button v-on:click="editInfo" type="button" class="btn btn-info"
v-if="!edit"
>Edytuj</button>
<button v-on:click="$emit('delete-info', info)" type="button" class="btn btn-danger"
v-if="!edit">Usuń</button>
<button v-on:click="updateInfo(info, editForm)" type="button" class="btn btn-primary"
v-if="edit"
>Gotowe</button>
<button v-on:click="cancelEdit" type="button" class="btn btn-default"
v-if="edit"
>Anuluj</button>
</td>
</tr>
</template>
<script>
export default {
props: ['info'],
data(){
return {
edit: false,
editForm:{
title: '',
body:''
}
}
},
methods: {
editInfo(){
this.edit = true;
this.editForm.title = this.info.title;
this.editForm.body = this.info.body;
window.setTimeout(function(){
try{
CKEDITOR.replace('editorInfo');
}catch(e){}
}, 1);
$('#errorInfo').html('');
},
cancelEdit(){
this.edit = false;
this.editForm.title = '';
this.editForm.body = '';
$('#errorInfo').html('');
},
updateInfo(oldUser, newUser){
newUser.body = CKEDITOR.instances.editorInfo.getData();
$('#errorInfo').html('');
if (newUser.body == '' || newUser.title == ''){
if(newUser.body == ''){
$('#errorInfo').append('Treść jest wymagana i nie może być pusta.<br>');
}
if(newUser.title == ''){
$('#errorInfo').append('Tytuł jest wymagany i nie może być pusty.');
}
} else {
axios.patch('/api/news/' + oldUser.id, newUser).then(response=>
{
this.$emit('update-info');
this.cancelEdit();
console.log(response.data);
});
}
}
}
}
</script>
<style>
#errorInfo {
color: #660000;
}
</style>
I solved problem!
Firstly I needed to separate methods in my web.php file like this:
Route::post('/api/newsAdd', 'NewsController#store');
that was before:
Route::resource('api/news', 'NewsController');
Sedcondly there was a problem with slashes:
before: axios.post('api/newsAdd/', this.info)
after: axios.post('api/newsAdd', this.info)

Ion-list not refreshing afer model change

I am having a problem with the ion-list, I can list all the items when the app loads for the first time, but when I make any changes to it, adding or removing data, the list isn't updating for the user.
I am using Pouch db to add and remove data for persistence in the app, all the functions are working correctly, it can be confirmed through console.log in the contents or using the fauxton PouchDb extension for chrome.
Resuming, the data is changing, but the view is not reflecting it.
the view code is this notas.html
<ion-content>
<ion-list>
<ion-item-sliding *ngFor="let n of notas">
<ion-item>
<h2>{{n.descricao}}</h2>
<p>URL: {{n.endereco}}</p>
<p>Data de expiração: {{n.vencimento}}</p>
</ion-item>
<ion-item-options side="right">
<button ion-button (click)="editar(n)">
<ion-icon name="paper"></ion-icon>
Editar
</button>
<button ion-button color="danger" (click)="excluir(n)">
<ion-icon name="trash"></ion-icon>
Excluir
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
And the controler code is this notas.ts
...
#Component({
selector: 'page-notas',
templateUrl: 'notas.html'
})
export class NotasPage {
notas: any;
constructor(public navCtrl: NavController, public alertCtrl: AlertController, public pouchDBService: PouchdbProvider) {
//PARA USO DO EXTENSION DO CHROME *POUCHDB Fauxton
(<any>window).PouchDB = PouchDB;
this.notas = [];
}
ionViewDidLoad(){
this.refresh();
}
refresh(){
//console.log(this.notas);
this.notas = [];
this.pouchDBService.getData().then((data) => {
this.notas = data;
console.log(this.notas);
});
// console.log(this.notas);
}
excluir(nota){
let prompt = this.alertCtrl.create({
title: 'Excluir',
message: 'Deseja realmente excluir o registro?',
buttons: [
{
text: 'Cancelar'
},
{
text: 'Excluir',
handler: data => {
this.pouchDBService.delete(nota);
this.notas.pop(nota);
this.refresh();
}
}
]
});
prompt.present();
}
}
...
Not 100% sure on this but usually if the view is not updating, it has to do with the code not running on zone, so:
import { NgZone } from '#angular/core';
...
zone: any = new NgZone({ enableLongStackTrace: false });
And then change your refresh method to:
refresh(){
//console.log(this.notas);
this.notas = [];
this.pouchDBService.getData().then((data) => {
this.zone.run(() => {
this.notas = data;
console.log(this.notas);
});
});
// console.log(this.notas);
}
I believe that should work.

Resources