How can I get my variable to update when its reference does?

时间:2016-07-11 20:13:39

标签: typescript angular pass-by-reference observable

I have an Angular2 component with a variable set to another variable in another service. I can't for the life of me get it to update without re-setting the variable.

In my component inside ngOnInit I do:

ngOnInit(){
   this.rooms = this.globals.filteredRooms;
}

In my @injectable called AppGlobals I'm always doing stuff like this:

applyFilters() {
    this.filteredRooms = this.rooms.filter(r => this.campFilters.includes(r.camp));
}

When the filteredRooms variable in appGlobals changes, I want the rooms variable in my component to change with it. Instead I need to set this.rooms = this.globals.filteredRooms again and again.

I've tried a million things such as attempting (probably failing) to create an Observable and subscribe to it, attempting (and failing) to import my component to appGlobals so I can tell the component to update its reference when needed (throws non-descript errors until import is removed), and seeing if I need to do some kind of byRef specification so I'm creating a reference to globals.filteredRooms instead of perhaps copying it?

If what I need is to change it to a reference, how is that done? How would I create an Observable and subscribe to it if that's what's needed? Or... What is needed?

2 个答案:

答案 0 :(得分:1)

You can't do that, and here's what's happening:

let a = [1, 2];
console.log(a); // [1, 2]

let b = a;
console.log(b); // [1, 2]

a = [1, 2, 3];

console.log(a); // [1, 2, 3]
console.log(b); // [1, 2]

As you can see, when you do a = b you point from a to the same place as b does (in this case [1, 2]).
But when you change b you only change what b now points to, a still points to the same value.

In order to avoid this you can have a container for the value, something like:

interface Reference {
    value: number[];
}

let a = { value: [1, 2] };
console.log(a.value); // [1, 2]

let b = a;
console.log(b.value); // [1, 2]

a.value = [1, 2, 3];

console.log(a.value); // [1, 2, 3]
console.log(b.value); // [1, 2, 3]

You haven't posted the part of your code where you declare the filteredRooms member so I can't write an exact solution for you, but it's deducible from my example.

You can have a helper class if you need to do something like this more than once:

class Reference<T> {
    private value: T;

    public set(value: T): void {
        this.value = value;
    }

    public get(): T {
        return this.value;
    }
}

答案 1 :(得分:0)

So just after posting this question I managed to create the Observable. Ironic because I spent days on this issue with no progress. Just in case it helps someone else:

Inside my AppGlobals:

@Injectable()
export class AppGlobals {

    private _filteredRooms = new BehaviorSubject(this.rooms);

    public filteredRooms = this._filteredRooms.asObservable();

    public rooms = [];


    constructor(private roomSvc: RoomSvc) {
        this.getRooms();
    }

    applyFilters() {
        this._filteredRooms.next(this.rooms.filter(r => this.campFilters.includes(r.camp)));
    }

    getRooms() {
        this.roomSvc.getRooms()
            .subscribe(r => {
                    this.rooms = r;
                    this._filteredRooms.next(this.rooms);
                }
            );
    }
}

Note: You need to import BehaviorSubject from 'rxjs/BehaviorSubject'

And inside my component:

export class TableComp {
   roomSubs: Subscription;
   rooms: Array<any>;
constructor(private globals: AppGlobals) { }

    ngOnInit() {

        this.roomSubs = this.globals.filteredRooms
            .subscribe(r =>
                this.rooms = r
            );
    }
}

Note: you need to import Subscription from 'rxjs/Subscription'