angular - Ionic 3 - 存储只保留一次
问题描述
好的,所以这是一个奇怪的问题,我确信对此有一个很好的解释,但它让我无法理解。基础知识:
- 我有一个具有嵌套对象列表的提供程序。
- 每当我更新该列表中的对象时,我都会将更新的列表保存到
LogGoals
函数中的存储中。 - 安装后第一次运行应用程序(或第一次加载后
ionic serve
)列表成功保存。 - 如果我关闭并重新加载应用程序(或在服务的情况下刷新浏览器),我可以看到我的更改。但是,如果我进行更多更改,嵌套列表不会更新。
这是有问题的提供商:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
@Injectable()
export class GoalproviderProvider {
public all_goals:any = [];
public viewed_tutorial = false;
public style_options = [
{
style_name: "girl-and-dog",
style_color: "#5bc3cc",
style_icon: "md-paw",
style_icon_color: "#FFFFFF",
mood: "Joyous"
},
{
style_name: "pink-city",
style_color: "#f7ced9",
style_icon: "md-radio",
style_icon_color: "#FFFFFF",
mood: "Serene"
},
{
style_name: "snow-guy",
style_color: "#b4cbb9",
style_icon: "md-snow",
style_icon_color: "#FFFFFF",
mood: "Confident"
},
{
style_name: "fish-shop",
style_color: "#31c1e9",
style_icon: "md-rainy",
style_icon_color: "#FFFFFF",
mood: "Relaxed"
}
];
constructor(public http: HttpClient,
private storage: Storage) {
console.log('Loading Goals');
this.LoadGoals();
}
public ClearAllData() {
this.storage.clear();
}
public GetWatchedGoals() {
var watched_goals = [];
var i = 0;
while(i < this.all_goals.length) {
var k = 0;
while(k < this.all_goals[i].goal_list.length) {
var child_watched_goals = this.GetWatchedGoalsRecursively(this.all_goals[i].goal_list[k]);
watched_goals = watched_goals.concat(child_watched_goals);
k++;
}
i++;
}
return watched_goals;
}
public GetWatchedGoalsRecursively(goal) {
var watched_goals = [];
if(goal.goal_watch && !goal.goal_complete && !goal.goal_deleted)
watched_goals.push(goal);
var i = 0;
while(i < goal.goal_list.length) {
var child_watched_goals = this.GetWatchedGoalsRecursively(goal.goal_list[i]);
watched_goals = watched_goals.concat(child_watched_goals);
i++;
}
return watched_goals;
}
public AppendAndReturnNewGoal(goal_style, goal_title) {
var new_goal = {
goal_style: goal_style,
goal_title: goal_title,
goal_list: [],
goal_id: new Date().getUTCMilliseconds()
};
this.all_goals.push(new_goal);
this.LogGoals();
return new_goal;
}
public EditGoalTitle(goal, new_title) {
goal.goal_title = new_title;
this.LogGoals();
}
public AddGoalChild(goal, new_title) {
var new_goal = {
goal_title: new_title,
goal_watch: false,
goal_complete: false,
goal_deleted: false,
goal_list: [],
goal_id: new Date().getUTCMilliseconds()
}
goal.goal_list.push(new_goal);
this.LogGoals();
}
public AddGoalParent(goal, new_title) {
var new_goal = {
goal_title: goal.goal_title,
goal_watch: goal.goal_watch,
goal_complete: goal.goal_complete,
goal_deleted: goal.goal_deleted,
goal_list: goal.goal_list,
goal_id: goal.goal_id
}
goal.goal_title = new_title;
goal.goal_watch = false;
goal.goal_complete = false;
goal.goal_deleted = false;
goal.goal_list = [ new_goal ];
goal.goal_id = new Date().getUTCMilliseconds();
this.LogGoals();
}
public DeleteGoal(goal) {
goal.goal_deleted = true;
this.LogGoals();
}
public ToggleGoalComplete(goal) {
goal.goal_complete = !goal.goal_complete;
this.LogGoals();
}
public ToggleGoalWatch(goal) {
goal.goal_watch = !goal.goal_watch;
this.LogGoals();
}
public UpdateMasterGoal(goal_id, new_goal) {
}
LogGoals() {
// TODO : I THINK WE HAVE TO TIMEOUT THIS FUNCTION TO ALLOW "ALL_GOALS" TO CATCH UP.
console.log('GOAL LOG:', this.all_goals);
this.storage.set('all_goals', this.all_goals);
}
LoadGoals() {
this.all_goals = [];
this.storage.get('all_goals').then((val) => {
if(!val) {
val = [];
this.storage.set('all_goals', val);
}
console.log('GOALS LOADED FROM STORAGE:', val);
this.all_goals = val;
});
}
}
在这个片段中,我有一个变量all_goals
. 该变量连接到一系列递归组件,这些组件能够更新/操作各自的目标。
组件:
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { GoalproviderProvider } from '../../providers/goalprovider/goalprovider';
import { MonetizationProvider } from '../../providers/monetization/monetization';
@Component({
selector: 'goalem-list',
templateUrl: 'goalem.html'
})
export class GoalemComponent {
@Input() goal_list;
@Input() search_term;
@Input() nested_level;
constructor(public actionSheetCtrl: ActionSheetController,
public alertCtrl: AlertController,
public goal_provider: GoalproviderProvider,
public monetization_provider: MonetizationProvider) {
}
PresentActionSheet(goal) {
const actionSheet = this.actionSheetCtrl.create({
title: goal.goal_title,
buttons: [
{
text: 'Add Step',
icon: 'add-circle',
handler: () => {
console.log('Add Child Step');
this.PresentAddChildPopup(goal);
}
},
{
text: 'Add Parent Step',
icon: 'add-circle',
handler: () => {
console.log('Add Parent Step');
this.PresentAddParentPopup(goal);
}
},
{
text: goal.goal_complete ? 'Mark Incomplete' : 'Mark Complete',
icon: goal.goal_complete ? 'close-circle' : 'checkmark-circle',
handler: () => {
console.log('Check Off');
this.goal_provider.ToggleGoalComplete(goal);
}
},
{
text: 'Edit',
icon: 'build',
handler: () => {
console.log('Edit Goal');
this.PresentEditPopup(goal);
}
},
{
text: goal.goal_watch ? 'Stop Watching' : 'Watch',
icon: goal.goal_watch ? 'eye-off' : 'eye',
handler: () => {
console.log('Watch Goal');
this.goal_provider.ToggleGoalWatch(goal);
}
},
{
text: 'Delete',
icon: 'trash',
role: 'destructive',
handler: () => {
console.log('Delete Goal');
this.PromptConfirmDelete(goal);
}
},
{
text: 'Cancel',
icon: 'close',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
}
]
});
actionSheet.present();
}
PresentEditPopup(goal) {
const prompt = this.alertCtrl.create({
title: 'Edit Step',
message: "Enter a description for this step.",
inputs: [
{
name: 'title',
placeholder: 'Description...',
value: goal.goal_title
},
],
buttons: [
{
text: 'Cancel',
handler: data => {
console.log('Cancel clicked');
}
},
{
text: 'Submit',
handler: data => {
this.goal_provider.EditGoalTitle(goal, data.title);
}
}
]
});
prompt.present();
}
PresentAddChildPopup(goal) {
if(this.nested_level > 3) {
var limit_title = "It's goals all the way down, mate.";
var limit_description = "You've discovered a premium feature! To unlock your app's true potential, click the button below. Your donation helps cover our operating costs, allowing us to continue providing the fantastic service you deserve!";
if(this.monetization_provider.PromptUpgradeIfNotPremium(limit_title, limit_description))
return;
}
const prompt = this.alertCtrl.create({
title: 'Create Step',
message: "Enter a description for this step.",
inputs: [
{
name: 'title',
placeholder: 'Description...'
},
],
buttons: [
{
text: 'Cancel',
handler: data => {
console.log('Cancel clicked');
}
},
{
text: 'Submit',
handler: data => {
this.goal_provider.AddGoalChild(goal, data.title);
}
}
]
});
prompt.present();
}
PresentAddParentPopup(goal) {
const prompt = this.alertCtrl.create({
title: 'Create Step',
message: "Enter a description for this step.",
inputs: [
{
name: 'title',
placeholder: 'Description...'
},
],
buttons: [
{
text: 'Cancel',
handler: data => {
console.log('Cancel clicked');
}
},
{
text: 'Submit',
handler: data => {
this.goal_provider.AddGoalParent(goal, data.title);
}
}
]
});
prompt.present();
}
PromptConfirmDelete(goal) {
const confirm = this.alertCtrl.create({
title: 'Are You Sure?',
message: 'By deleting this step, you will also delete any children steps. This can not be undone. Continue?',
buttons: [
{
text: 'Cancel',
handler: () => {
console.log('Disagree clicked');
}
},
{
text: 'Okay',
handler: () => {
this.goal_provider.DeleteGoal(goal);
}
}
]
});
confirm.present();
}
}
<ul *ngIf="goal_list.length">
<li *ngFor="let goal of goal_list">
<ion-item class="goal-item" *ngIf="!goal.goal_deleted && (!search_term || goal.goal_title.toLowerCase().includes(search_term.toLowerCase()))">
<p [ngStyle]="{'text-decoration': goal.goal_complete ? 'line-through' : 'none'}">{{goal.goal_title}}</p>
<button ion-button clear item-end (tap)="PresentActionSheet(goal);">
<ion-icon name="md-menu"></ion-icon>
</button>
</ion-item>
<goalem-list [goal_list]="goal.goal_list" [search_term]="search_term" [nested_level]="nested_level + 1" *ngIf="goal.goal_list.length && !goal.goal_deleted"></goalem-list>
</li>
</ul>
可以看到组件调用了自己。这将创建一系列嵌套的组件,这些组件反映了来自提供者的嵌套目标列表。
从功能上讲,除了保存之外,一切正常。这是我到目前为止发现的:
- 应用程序第一次运行时,一切都会正确更新。
all_goals
总是反映目标树。每当我对目标树进行更新时,它都会“同步”回all_goals
提供者中的变量。 - 重新启动后,同步不起作用。我可以对组件进行操作并且它们可以工作,但它们不再绑定到
all_goals
. all_goals 从存储中加载,然后永远不会改变。因此,无论何时我将其保存到存储中,它都不会写入任何更新(因为变量没有更新)。 - 更新:进一步的发现。在服务期间,只要应用程序重建,保存到存储一次就可以工作,然后刷新会导致它再次停止工作。因此,以下操作会使存储停止工作:在移动设备上关闭和打开应用程序,或者在保存时刷新浏览器。以下操作会导致它工作,直到发生前面提到的操作之一:提供应用程序、重建应用程序(通过在提供服务时修改代码)或在移动设备上重新安装应用程序。
我有点疲惫,这已经接近我的技能边缘,所以不确定如何进行调试。作为参考,这里是调用根goal-list
组件的页面。
<ion-header>
<ion-navbar class="overlapping-navbar">
<button ion-button menuToggle icon-only>
<ion-icon name='menu'></ion-icon>
</button>
<ion-input [(ngModel)]="search_term" placeholder="Search..."></ion-input>
</ion-navbar>
</ion-header>
<ion-content scroll="false" [ngStyle]="{'background-color': goal_color}">
<div class="splash-bg">
<img [src]="goal_image" />
</div>
<div class="splash-info">
<!-- <div class="splash-logo"></div> -->
<div class="splash-goal">
<div>
{{goal_title}}
</div>
</div>
</div>
<div class="goal-list">
<goalem-list [goal_list]="goal_list" [search_term]="search_term" [nested_level]="1" *ngIf="goal_list"></goalem-list>
<button class="rpg-button" ion-button icon-end large (click)="PromptForNewGoal()"
style="margin:auto; display:block; margin-top:35px;">
Map It!
</button>
</div>
</ion-content>
如果我可以做些什么来提供更多信息,请告诉我:) 我认为问题源于嵌套,但让我感到困惑的是它第一次起作用。这么奇怪...
解决方案
推荐阅读
- python - 将许多参数传递给 scipy 优化
- mongodb - 在不影响锁定和性能的情况下删除大型 mongo 集合的所有元素的最佳方法?
- tensorflow - 从对象检测 API 模型中提取研磨锚
- elasticsearch - 弹性休息高级api RolloverRequest
- python - Python排序html表格
- javascript - 用于设置复选框 ref 和检查复选框状态的反应语法
- ms-access - Access 2016 中单个表单上的更改记录
- tfs - 在 TFS 2018 中是否有用于完成拉取请求的 ISubscriber 事件?
- laravel - Doctrine\DBAL\Driver\PDOException: 在我的单元测试中尝试使用工厂时找不到驱动程序
- xcode - 尝试使用 Xcode 与佳能的 EDSDK 合作