Router in AngularDart - OnActivate is called again on caller component when navigate to another path/component - angular-dart

I have a list component OrganizationsComponent and a detail component OrganizationDetailComponent (modal dialog) that using route to navigate.
When the navigation occur from list to detail [OrganizationsComponent (list) >>> OrganizationDetailComponent (detail)], the onActivate from list is called again (is the method from OnActivate interface).
I hoped that only onActivate on detail would be called, like is, but not of the list again (caller).
If this is a normal behaviour, is there a way to stop the calling this method onActivate of the origin, in this case, on the list?
Environment:
Dart SDK: 2.0.0-dev.43.0
Angular: 5.0.0-alpha+8
Angular_router: 2.0.0-alpha+8
Following the code:
LIST CODE (partialy):
-HTML
<div class="container-list">
<material-fab raised (trigger)="goToDetail(null)">
<material-icon icon="add"></material-icon>
</material-fab>
<br/>
<material-list class="shadow-box-material-list" elevation="2">
<div *ngFor="let o of organizations">
<material-list-item>
<div>
<h4 class="material-list-item-content"><strong>{{o.name}}</strong></h4>
<p class="material-list-item-content">{{o.code}}</p>
</div>
<span class="row-spacer"></span>
<div class="material-list-item-secondary">
<div>
<material-menu [menu]="menuModel" (click)="selectOrganization(o)">
</material-menu>
</div>
</div>
</material-list-item>
<hr>
</div>
</material-list>
</div>
<router-outlet [routes]="rotas"></router-outlet>
-DART
class OrganizationsComponent implements OnActivate, OnDestroy {
final List<RouteDefinition> rotas = [
new RouteDefinition(
routePath: AppRotas.appLayoutHomeRoute,
component: app_layout_home.AppLayoutHomeComponentNgFactory,
useAsDefault: true
),
new RouteDefinition(
routePath: AppRotas.organizationDetailRoute,
component: organization_detail.OrganizationDetailComponentNgFactory,
),
new RouteDefinition(
routePath: AppRotas.organizationDetailAddRoute,
component: organization_detail.OrganizationDetailComponentNgFactory,
),
];
final Router _router;
List<Organization> _organizations;
OrganizationsComponent(this._router);
// ** HERE IS CALLED AGAIN WHEN NAVEGATES TO DETAIL (calling goToDetail() method) **
#override
onActivate(routeStatePrevious, routeStateCurrent) async {
_organizations = await _organizationService.getOrganizations();
}
#override
ngOnDestroy() async {
_appLayoutService.searchEnabled = false;
}
void goToDetail(Organization organization) {
if (organization == null) {
_router.navigate(AppRotas.organizationDetailAddRoute.toUrl());
} else {
_router.navigate(AppRotas.organizationDetailRoute.toUrl(parameters: {
AppRotas.organizationUuidParameter: organization != null ? organization.id : null
}));
}
}
List<Organization> get organizations {
return _organizations;
}
// ...
}
DETAIL CODE (partialy):
-HTML
<modal [visible]="true">
<material-dialog headered class="headered-dialog">
<div header>
<h3>{{labelOrganization}}</h3>
</div>
<material-content>
<material-input class="width-300" [(ngModel)]="organization.name" floatingLabel [label]="labelName" required [requiredErrorMsg]="msgValorRequerido"></material-input>
<material-input [(ngModel)]="organization.code" floatingLabel [label]="labelCode"></material-input>
</material-content>
<div footer>
<material-button (trigger)="saveOrganization()">{{buttonLabelSave}}
<material-icon icon="save"></material-icon>
</material-button>
<material-button autoFocus (trigger)="goBack()">{{buttonLabelBack}}
<material-icon icon="arrow_back"></material-icon>
</material-button>
</div>
</material-dialog>
</modal>
-DART
class OrganizationDetailComponent implements OnActivate {
final Location _location;
Organization organization = new Organization();
OrganizationDetailComponent( this._location);
#override
Future onActivate(routeStatePrevious, routeStateCurrent) async {
if (routeStateCurrent.parameters.isNotEmpty) {
var uuid = routeStateCurrent.parameters[AppRotas.organizationUuidParameter];
if (uuid != null && uuid.isNotEmpty) {
organization = await _organizationService.getOrganizationById(uuid);
}
}
}
void goBack() {
_location.back();
}
}

Related

How to convert axios API call to vuex action dispatch?

I'm new with Vue3JS and I am developing a todo list built from several components like that :
I need to retrieve data to display all tasks, TaskInfo component is used to display informations.
I try to convert my local component TaskInfo works well with API call (method async loadTask()) into Vuex store action/mutation, but I didn't quite understand how to process in my case, because in component TaskInfo data are always the same.
I try to load data from current taskId with :
mounted() {
this.loadTask(this.taskId);
},
But, only last item loaded, with id 26, display data loaded by getTaskinfo computed variable loaded this.$store.state.taskInfo.
Any suggestion to load component data correctly when is mouted ?
TaskInfo.vue
<template>
<div class="task-container mb-4 ml-2 rounded-lg flex flex-grow flex-col flex-nowrap items-left shadow-test border border-solid border-gray-dark">
<div class="task--labels flex p-4 border-1 border-solid border-b border-gray-dark">
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2"> </span>
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2">label 2</span>
<div class="font-bold absolute right-1">
<button
v-if="this.taskId"
#click="this.$store.dispatch('removeTask', this.taskId)" >
<svg width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="removeIconTitle" stroke="#EF5350" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000">
<title id="removeIconTitle">Remove</title>
<path d="M17,12 L7,12"/>
<circle cx="12" cy="12" r="10"/>
</svg>
</button>
</div>
</div>
<div
class="task--title p-4 title text-m font-normal"
:class="[ getTaskinfo.finish === true ? 'finished' : '' ]"
>
{{ getTaskinfo.title }}
</div>
<div class="task-time p-4 text-xs text text-gray1 flex items-center">
<span class="pt-1"></span>
<span class="ml-1">11h - 12h</span>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { mapActions } from 'vuex'
axios.defaults.baseURL = 'http://localhost:3000';
export default {
name: 'taskInfo',
props: ['taskId'],
data() {
return {
error: '',
taskinfo: []
};
},
mounted() {
this.loadTask(this.taskId);
},
methods: {
...mapActions([
'loadTask' // map `this.incrementBy(this.taskId)` to `this.$store.dispatch('loadTask', this.taskId);`
]),
// Old API call replaced by mapActions, it's works fine.
async loadTask() {
try {
if(!this.taskId) return;
const taskInfo = await axios.get(`/task/${this.taskId}`);
if (!taskInfo.data) {
console.log('no taskInfo');
}
else {
this.taskinfo = taskInfo.data;
}
} catch (error) {
console.log('no taskInfo error');
}
},
},
computed: {
getTaskinfo() {
return this.$store.state.taskInfo;
},
}
}
</script>
Store
import { createStore } from 'vuex'
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
export default createStore( {
state: {
taskInfo: [],
error: '',
},
// Mutations have to be synchronous.
mutations: {
loadTask(state, taskInfo) {
state.taskInfo = taskInfo;
},
updateError(state, error) {
state.error = error;
},
},
actions: {
setError({ commit }, error) {
commit('updateError', error);
},
// LOAD task info.
async loadTask({ commit }, taskId) {
try {
if(!taskId) commit('updateError', 'taskId empty');
const taskInfo = await axios.get(`/task/${taskId}`);
if (!taskInfo.data) {
commit('updateError','Aucune info sur la tache');
// Set an empty task list.
commit('loadTask', []);
}
else {
console.log('task : ' + taskId);
commit('loadTask', taskInfo.data);
}
} catch (error) {
commit('updateError', error);
}
},
},
});

Argument of type 'AxiosResponse<any, any>' is not assignable to parameter of type 'SetStateAction<any[]>' and the data is not being accessed in return

I have used next js, useEffect and axios to fetch the data from API's endpoint. I want to get the data to be refreshed each second on my website. I have my code here and at the line number 11, it says "const Data: AxiosResponse<any, any>
Argument of type 'AxiosResponse<any, any>' is not assignable to parameter of type 'SetStateAction<any[]>'". If this is solved, I am not able to access the data in line 38 and 40 by accesing the particular objects. Please help!!
import axios from 'axios';
import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';
export default function HomeMatch() {
const [data, setData] = useState([]);
const getData = async () => {
try {
const Data = await axios('http://api.open-notify.org/iss-now.json');
setData(Data); // set State
console.log(Data);
} catch (err) {
console.error(err.message);
}
};
useEffect(() => {
getData();
const interval = setInterval(() => {
getData();
}, 5000);
return () => clearInterval(interval);
}, []); // includes empty dependency array
return (
<div className="relative bg-black rounded-xl w-[400px] h-[200px]">
<div className="py-2 px-5">
<div className="grid grid-cols-2 text-center justify-between pt-4 text-xl font-bold">
<p className="text-white uppercase underline-offset-4 decoration-2 decoration-red-600">
longitude
</p>
<p className="text-white uppercase">latitude</p>
</div>
<div className="bg-white rounded-xl w-[350px] h-[50px] absolute top-16">
<div className="flex flex-row items-center justify-center h-full text-xl font-bold gap-14">
<p className="">{data.data.iss_position.longitude}</p>
<p>-</p>
<p className="">{data.data.iss_position.latitude}</p>
</div>
</div>
</div>
</div>
);
}

Function Query.where() requires a valid third argument, but it was undefined when trying to view the page

I'm trying to implement rating system in my shopping app, but received error console when trying to open the page.
The error on console are:
Function Query.where() requires a valid third argument, but it was undefined. - it points to:
this.stars = this.starService.getProductStars(this.movieId)
AND
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
Below is my code:
rating.page.html: (where I put my 2 of components which are TestRate and Star-Review)
<ion-content>
<app-testrate></app-testrate>
<app-star-review-component></app-star-review-component>
</ion-content>
testrate.component.html:
<div *ngIf="movie | async as m">
<h1>
{{m.title}}
</h1>
<img [src]="m.image" width="100px">
<p>
{{m.plot}}
</p>
<star-review [movieId]="movieId" [userId]="userId"></star-review>
</div>
testrate.component.ts:
export class TestrateComponent implements OnInit {
userDoc: AngularFirestoreDocument<any>;
movieDoc: AngularFirestoreDocument<any>;
user: Observable<any>;
movie: Observable<any>;
constructor(private afs: AngularFirestore) { }
ngOnInit() {
this.userDoc = this.afs.doc('users/test-user-3')
this.movieDoc = this.afs.doc('movies/battlefield-earth')
this.movie = this.movieDoc.valueChanges()
this.user = this.userDoc.valueChanges()
}
get movieId() {
return this.movieDoc.ref.id
}
get userId() {
return this.userDoc.ref.id
}
}
star-review.component.html:
<h3>Average Rating</h3>
{{ avgRating | async }}
<h3>Reviews</h3>
<div *ngFor="let star of stars | async">
{{ star.userId }} gave {{ star.movieId }} {{ star.value }} stars
</div>
<h3>Post your Review</h3>
<fieldset class="rating">
<ng-container *ngFor="let num of [5, 4, 3, 2, 1]">
full star
<input (click)="starHandler(num)"
[id]="'star'+num"
[value]="num-0.5"
name="rating"
type="radio" />
<label class="full" [for]="'star'+num"></label>
half star
<input (click)="starHandler(num-0.5)"
[value]="num-0.5"
[id]="'halfstar'+num"
name="rating"
type="radio" />
<label class="half" [for]="'halfstar'+num"></label>
</ng-container>
</fieldset>
star-review.component.ts:
export class StarReviewComponentComponent implements OnInit {
#Input() userId;
#Input() movieId;
stars: Observable<any>;
avgRating: Observable<any>;
constructor(private starService: StarService) { }
ngOnInit() {
this.stars = this.starService.getProductStars(this.movieId)
this.avgRating = this.stars.pipe(map(arr => {
const ratings = arr.map(v => v.value)
return ratings.length ? ratings.reduce((total, val) => total + val) / arr.length : 'not reviewed'
}))
}
starHandler(value) {
this.starService.setStar(this.userId, this.movieId, value)
}
}
star.service.ts:
export class StarService {
constructor(private afs: AngularFirestore) { }
// Star reviews that belong to a user
getUserStars(userId) {
const starsRef = this.afs.collection('stars', ref => ref.where('userId', '==', userId));
return starsRef.valueChanges();
}
// Get all stars that belog to a Product
getProductStars(movieId) {
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
return starsRef.valueChanges();
}
// Create or update star
setStar(userId, movieId, value) {
// Star document data
const star: Star = { userId, movieId, value };
// Custom doc ID for relationship
const starPath = `stars/${star.userId}_${star.movieId}`;
// Set the data, return the promise
return this.afs.doc(starPath).set(star)
}
}

Load form value from state

First I show a list of transactions, when a user selects a single transaction a new page is opened with the transaction ID in the URL. On this page are details of the transaction displayed.
The code below is just the details page. It shows all the right details.
One of the details is a list of 0 or more tags, I'd like to be able to edit the list of tags and save the result.
At this point, I always end up with a clean Input field and I do not understand how to populate this field with the existing transaction['tags'] data.
It seems that the transaction['tags'] is not initialized until the page is rendered, I cannot use it in the constructor or in the componentDidMount.
What I expect is that the transaction object as stated in the mapStateToProps is available and I can change the line in the constructor from: this.state = {value: ''}; to this.state = {value: transaction['tags']}
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchTransaction } from '../actions';
class TransactionsIndex extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const { _key } = this.props.match.params;
this.props.fetchTransaction(_key);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
const { transaction } = this.props;
if (!transaction) {
return <div>Loading...</div>;
}
let tags = null;
tags =
<div>
<form onSubmit={this.handleSaveTagsclick}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
</div>
// console.log(transaction['tags']);
return (
<div className="container">
<div className="well">
<div>Transactiedatum: { transaction["Transactiedatum"] }</div>
<div>Valutacode: { transaction["Valutacode"] }</div>
<div>CreditDebet: { transaction["CreditDebet"] }</div>
<div>Bedrag: { transaction["Bedrag"] }</div>
<div>Tegenrekeningnummer: { transaction["Tegenrekeningnummer"] }</div>
<div>Tegenrekeninghouder: { transaction["Tegenrekeninghouder"] }</div>
<div>Valutadatum: { transaction["Valutadatum"] }</div>
<div>Betaalwijze: { transaction["Betaalwijze"] }</div>
<div>Omschrijving: { transaction["Omschrijving"] }</div>
<div>Type betaling: { transaction["Type betaling"] }</div>
<div>Machtigingsnummer: { transaction["Machtigingsnummer"] }</div>
<div>Incassant ID: { transaction["Incassant ID"] }</div>
<div>Adres: { transaction["Adres"] }</div>
<div>Status: { transaction["status"] }</div>
<div>Created: { transaction["created"] }</div>
{tags}
</div>
<Link to="/"><button type="button" className="btn btn-default">Back</button></Link>
</div>
);
};
}
function mapStateToProps({ transactions }) {
// console.log('transactions_selectedTransaction: ' + transactions['selectedTransaction']);
return { transaction: transactions['selectedTransaction'] };
}
export default connect(mapStateToProps, { fetchTransaction })(TransactionsIndex);
I found this but it did not help me: Redux-form: Set form values from state
and this: How to get state / value from form component?

get checkbox values in real time with a reactive form Angular

I have a list of country objects, that I access and use with my reactive form. I create each one as a form control dynamically, because this list will be changing. Then I attempt to get the form and values in real time (not using a submit button), as the checkboxes get clicked, by using the ngOnChanges hook. this is obviously not working, what hook should I use? on another note, is this a bad way to accomplish this? what would be a better approach?
component
export class GeoDropComponent implements OnInit, OnChanges {
countries = [
{
name : 'USA',
continent : 'north america'
},
{
name : 'Canada',
continent: 'north america'
}
];
countriesForm: FormGroup;
constructor() { }
ngOnInit() {
// add checkbox for each country
this.countriesForm = new FormGroup({});
for (let i = 0; i < this.countries.length; i++) {
this.countriesForm.addControl(
this.countries[i].name, new FormControl(false)
)
}
}
ngOnChanges() {
console.log(this.countriesForm);
}
}
html
<div class="geo-list">
<div class="content-box container">
<form [formGroup]="countriesForm">
<div class="country" *ngFor="let country of countries">
<input
type="checkbox"
formControlName="{{country.name}}"
>
{{ country.name }} | {{ country.continent }}
</div>
</form>
</div>
</div>
you can try like this. when ever search checkbox is selected or selected change method will update selected items
pseudo code
<input
type="checkbox"
formControlName="{{country.name}}"
(change)="search(country, $event)
>
component file.
selectedItems : any [] = [];
search(country, event) {
var index = this.selectedItems.indexOf(country.name);
if (event.target.checked) {
if (index === -1) {
this.selectedItems.push(country.name);
}
} else {
if (index !== -1) {
this.selectedItems.splice(index, 1);
}
}
}
}