I am trying to update object array inside observable subscribe method. But it is not updating as expecting.
My implementation is as follows.
Compoent.ts
constructor(private shopHomeService: ShopHomeService) {
this.shopHomeService.shopItemChanged.subscribe(
(item) => {
if (item) {
this.items = shopHomeService.getShopItems();
if (this.items.length > 0) {
const findItem = this.items.find(shopItem => shopItem.Id === item.Id);
if (findItem) {
findItem.OrderQuantity = findItem.OrderQuantity + 1;
} else {
this.items.push(item);
}
} else {
this.items.push(item);
}
this.items = this.items.slice();
shopHomeService.ShopItems = this.items;
this.shopItems = [];
this.shopItems = this.items;
}
}
);
}
Service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ShopHomeService {
private shopItems = [];
private shopItem: any;
shopItemChanged = new BehaviorSubject<any>(this.shopItem);
constructor() { }
getShopItems() {
console.log(this.shopItems);
return this.shopItems;
}
set ShopItems(shopItems) {
this.shopItems = shopItems;
console.log(this.shopItems);
}
addShopItem(item) {
item.OrderQuantity = 1;
item.Discount = 0;
item.DiscountPercentage = 0;
this.shopItemChanged.next(item);
}
removeShopItem(item) {
this.shopItems = this.shopItems.filter(element => element.Id !== item.Id);
this.shopItemChanged.next(this.shopItems);
}
}
Initially OrderQuantity property set to 1 in every objects of the array items.
When same type of object adding to shopItems it will increase value of OrderQuantity by 1. Logic has implemented above code. But when shop items change it always gives object array with OrderQuantity = 1. (shopHomeService.getShopItems() returns array list with OrderQuantity = 1, but when the shopHomeService.ShopItems call it sent rderQuantity = 2)
Related
I have the below class and its properties initialized within the constructor.
I have an onchange function which sets property 'currentFilter' and i want this value to remain in the object However when the set function is triggered it resets the object and the value of 'currentFilter' is lost. How can i retain the values of the properties and prevent it from getting initialized when they have values.
class Filters
{
constructor()
{
this.isShow = false;
this.currentFilter = '';
}
async loadFilters()
{
const query = `?$select=*&$filter=_new_table_value eq ${window.parent.Xrm.Page.data.entity.getId().replace(/[{}]/g, "")}&$orderby=ice_name asc`;
this.Filters = await window.parent.Xrm.WebApi.retrieveMultipleRecords('ice_filterstable', query);
return this;
}
}
<script>
export default {
data() {
return {
filters : {};
};
},
methods: {,
set () {
const filters = new Filters();
this.filters = await filters.loadFilters();
},
changeFilter: (itemId, itemText) =>
{
this.filters.isShow = true;
this.filters.currentFilter = itemId;
},
}
My goal is to create a portfolio page where the user can add arworks by filling a form (titel, technique, image, yaer etc.). Submited data will be added to an array and presented in a form of a list.
I'm able to upload the array to the firebase but I don't know how to upload it when the site starts. I want it to update and refresh the list on the page each time I submit an item. I spent hours looking for the right way to do it.
//SERVER.SERVICE
export class ServerService {
url:string = 'https://basic-31cd5.firebaseio.com';
constructor(
private httpClient: HttpClient,
private artService:ArtService
) { }
storeArtworks() {
return this.httpClient.put( this.url +"/artworks.json",
this.artService.getArtworks());
}
getArtworks() {
return this.httpClient.get<HttpResponse<Art[]>>
(this.url+"/artworks.json")};
}
//ART.SERVIVCE
import { Art } from "../modules/art.module"
import { Subject } from 'rxjs';
export class ArtService{
listChanged = new Subject<Art[]>();
public list: Art[] = [];
// new Art ("The Young Ladies of Avignon", "painting", 1907),
// new Art ("Kiss", "painting", 1908),
// new Art ("Dance", "painting", 1911)
subject = new Subject<string>();
setArt(artworks:Art[]=[]){
this.list = artworks;
this.listChanged.next(this.list);
}
getArtworks() {
return this.list;
}
addArt(f){
this.list.push( new Art( f.value.titel, f.value.technique, f.value.year));
}
remove(titel){
// this.list.splice(i, 1)
for (let i = 0; i < this.list.length; i++) {
if( this.list[i].titel === titel )
{ this.list.splice( i,1 ) }
}
}
}
//DashboardComponent
list;
constructor(
private serverService:ServerService, private artService:ArtService )
{}
ngOnInit() {
this.list = this.artService.getArtworks();
}
submit(form:NgForm){
this.artService.addArt(form);
}
removeItem(titel){
this.artService.remove(titel)
}
onSave(){
this.serverService.storeArtworks()
.subscribe( (res:Response) => console.log(res));
}
}
Try this code:
getList() {
let listDB = this.db.database.ref('/tasks').child(this.uid);
listDB.on('value', (snapshot) => {
const items = snapshot.val();
if(items) {
this.list = Object.keys(items).map(i => items[i]);
console.log('this.list', this.list)`enter code here`;
}
})
}
I would like to update a Firestore model containing a profile name and a list of hashtags with Angular 6. The "name" is stored as the value of a document field and the "hashtags" are stored as the keys of an object. When I try to update the database entry, my program adds a new document field called "data" every time I call the update function instead of updating the existing fields.
How can I fix this?
This is how my firestore looks like before the update.
My update function adds a new "data" field instead of updating everytime I call it.
My Firestore Service:
export class MembersService {
membersCollection: AngularFirestoreCollection<Member>;
members$: Observable<Member[]>;
memberDoc: AngularFirestoreDocument<Member>;
constructor(public afs: AngularFirestore) {
this.membersCollection = afs.collection<Member>('Members');
this.members$ = this.membersCollection.snapshotChanges().pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data() as Member;
const id = a.payload.doc.id;
return { data, id };
}))
);
}
getMembers(): Observable<Member[]> {
return this.members$;
}
updateMember(member: Member) {
this.memberDoc = this.afs.doc(`Members/${member.id}`);
this.memberDoc.update(member);
}
}
My input component.ts:
export class MembersComponent implements OnInit {
members: Member[];
editState: boolean;
membertoEdit: Member;
constructor(private membersService: MembersService) {
this.editState = false;
}
ngOnInit() {
this.membersService.getMembers().subscribe(members => {
this.members = members;
});
}
editMember(member: Member) {
this.editState = true;
this.membertoEdit = member;
}
clearState() {
this.editState = false;
this.membertoEdit = null;
}
submit(member: Member, editName: string, editHashtag: string) {
if ( editName !== '' && editHashtag !== '') {
this.membertoEdit.name = editName;
const key = editHashtag;
const object = {};
object[key] = true;
this.membertoEdit.hashtag = object;
this.membersService.updateMember(this.membertoEdit);
}
this.clearState();
}
}
My component.html for the user Input:
<button *ngIf="editState == false" (click)="editMember(member)">edit</button>
<div *ngIf="editState && membertoEdit.id == member.id">
<form>
<input type="text" #editName>
<input type="text" #editHashtag>
<button (click)="submit(member, editName.value, editHashtag.value);
editName.value=''">Submit</button>
</form>>
</div>
Found a solution: Even I don´t think it is elegant. It´s possible to pass every input on its own
updateMember(member: Member, editName: string, editHashtag: object) {
this.memberDoc = this.afs.doc(`Members/${member.id}`);
console.log(this.memberDoc);
this.memberDoc.update({
name: editName,
hashtag: editHashtag
});
}
submit(member: Member, editName: string, editHashtag: string) {
if ( editName !== '' && editHashtag !== '') {
const key = editHashtag;
const object = {};
object[key] = true;
this.membersService.updateMember(member, editName, object);
}
this.clearState();
}
I'm stuck with return a value using Array.map in Angular 2
So what am I missing here?
export class TabsPage {
#ViewChild(SuperTabs) superTabs: SuperTabs;
public rootTab: string = 'ProductListPage';
public categories: Array<any>;
public collection_id: number;
public selectedindex: any;
private getArrayIndex(source, target) {
source.map((element, index) => {
if (element.attrs.collection_id === target) {
// Returns the Value i need
console.log('i: ', index);
return index;
}
});
}
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public shopifyClientProvider: ShopifyClientProvider,
private superTabsCtrl: SuperTabsController,
) {
this.categories = navParams.get('collections');
this.collection_id = navParams.get('collection_id');
this.selectedindex = this.getArrayIndex(this.categories, navParams.get('collection_id'));
// Returns undefined
console.log('Index ', this.selectedindex);
}
}
I know this question is already answered but I have one solution for this same.
.ts file code
private getArrayIndex(source, target) {
let indx = -1;
source.map((element, index) => {
if (element.attrs.collection_id === target) {
// Returns the Value i need
console.log('i: ', index);
indx = index;
}
});
return indx;
}
You can use findIndex() to do this in pretty short order:
I don't know exactly what your data looks like, but given an array:
const target = 2;
const source = [
{
attrs: {
collection_id: 1
}
},
{
attrs: {
collection_id: 2
}
},
{
attrs: {
collection_id: 3
}
},
];
const index = source.findIndex(element => element.attrs.collection_id === target);
would return 1 for the index. If the index isn't found, -1 will be returned.
Plunkr: https://plnkr.co/edit/5B0gnREzyz6IJ3W3
Hope that helps you out.
Looks like Typescript breaks the return. With this modification i get the desired Value:
private getArrayIndex(source, target) {
let found: number;
source.map((element, index) => {
if (element.attrs.collection_id === target) {
console.log('i: ', index);
found = index;
}
});
return found;
}
I'm trying to make my react app as dry as possible, for common things like consuming a rest api, I've created classes that act as stores with predefined actions to make it easy to modify it.
Behold, big code:
import {autorun, action, observable} from 'mobx'
export function getResourceMethods(name) {
let lname = name.toLowerCase()
let obj = {
methods: {
plural: (lname + 's'),
add: ('add' + name),
addPlural: ('add' + name + 's'),
rename: ('rename' + name),
},
refMethods: {
add: ('add' + name + 'ByRef'),
addPlural: ('add' + name + 'sByRef'),
rename: ('rename' + name + 'ByRef'),
setRef: ('set' + name + 'Ref'),
},
fetchMethods: {
pending: (lname + 'Pending'),
fulfilled: (lname + 'Fulfilled'),
rejected: (lname + 'Rejected'),
}
}
return obj
}
class ResourceItem {
#observable data;
#observable fetched = false;
#observable stats = 'pending';
#observable error = null;
constructor(data) {
this.data = data;
}
}
class ResourceList {
#observable items = [];
#observable fetched = false;
#observable status = 'pending';
constructor(name) {
this['add' + name + 's'] = action((items) => {
items.forEach((item, iterator) => {
this.items.push(item.id)
})
})
}
}
class ResourceStore {
constructor(name, resourceItem, middleware) {
let {methods} = getResourceMethods(name)
this.middleware = middleware || []
let items = methods.plural.toLowerCase()
this[items] = observable({}) // <--------------- THIS DOES NOT WORK!
// Add resource item
this[methods.add] = action((id, resource) => {
let item = this[items][id], data;
if (item && item.fetched) {
data = item.data
} else {
data = resource || {}
}
this[items][id] = new resourceItem(data)
this.runMiddleware(this[items][id])
})
// Add several resource items
this[methods.addPlural] = action((resources) => {
resources.forEach((resource, iterator) => {
this[methods.add](resource.id, resource)
})
})
// Rename resource item
this[methods.rename] = action((oldId, newId) => {
let item = this[items][oldId]
this[items][newId] = item
if (oldId !== newId) {
delete this[items][oldId]
}
})
// Constructor ends here
}
runMiddleware(item) {
let result = item;
this.middleware.map(fn => {
result = fn(item)
})
return result
}
}
class ReferencedResourceStore extends ResourceStore {
#observable references = {}
constructor(name, resource, middleware) {
super(name, resource, middleware)
let {methods, refMethods, fetchMethods} = getResourceMethods(name)
let getReference = (reference) => {
return this.references[reference] || reference
}
this[refMethods.setRef] = action((ref, id) => {
this.references[ref] = id
})
this[refMethods.add] = action((ref, data) => {
this[methods.add](getReference(ref), data)
this[refMethods.setRef](ref, getReference(ref))
})
this[refMethods.rename] = action((ref, id) => {
this[methods.rename](getReference(ref), id)
this[refMethods.setRef](ref, id)
})
// *** Fetch *** //
// Resource pending
this[fetchMethods.pending] = action((ref) => {
this[refMethods.add](ref)
})
// Resource fulfilled
this[fetchMethods.fulfilled] = action((ref, data) => {
this[refMethods.add](ref, data)
this[refMethods.rename](ref, data.id)
let item = this[methods.plural][data.id];
item.fetched = true
item.status = 'fulfilled'
})
}
}
export {ResourceItem, ResourceList, ResourceStore, ReferencedResourceStore}
Now I'm just creating a simple user store:
class UserResource extends ResourceItem {
constructor(data) {
super(data)
}
#observable posts = new ResourceList('Posts')
#observable comments = new ResourceList('Comment')
}
// Create store
class UserStore extends ReferencedResourceStore {}
let store = new UserStore('User', UserResource)
And mobx-react connects just fine to the store, can read it as well. BUT, whenever I do any changes to the items (users in this case, the name of the property is dynamic) property, there are no reactions. I also noticed that in chrome, the object property does not have a "invoke property getter" in the tree view:
Didn't read the entire gist, but if you want to declare a new observable property on an existing object, use extendObservable, observable creates just a boxed observable, so you have an observable value now, but not yet an observable property. In other words:
this[items] = observable({}) // <--------------- THIS DOES NOT WORK!
should be:
extendObservable(this, {
[items] : {}
})
N.b. if you can't use the above ES6 syntax, it desugars to:
const newProps = {}
newProps[items] = {}
extendObservable(this, newProps)
to grok this: https://mobxjs.github.io/mobx/best/react.html
Edit: oops misread, you already did that, it is not hacky but the correct solution, just make sure the extend is done before the property is ever used :)
I found a hacky solution:
First off, use extendObservable instead (this is the correct solution) and then use a fresh version of the object and set it as the property.
let items = methods.plural.toLowerCase()
extendObservable(this, {
[items]: {}
})
// Add resource item
this[methods.add] = action((id, resource) => {
let item = this[items][id], data;
if (item && item.fetched) {
data = item.data
} else {
data = resource || {}
}
this[items][id] = new resourceItem(data)
this.runMiddleware(this[items][id])
this[items] = {...this[items]}
})
This works, not sure if there's a better solution.
Your options are using extendObservable or using an observable map.
For reference see the documentation of observable and specifically:
To create dynamically keyed objects use the asMap modifier! Only initially existing properties on an object will be made observable, although new ones can be added using extendObservable.