2016-08-03 23 views
0

Ich baue eine ziemlich fortgeschrittene App mit dem Aurelia-Framework, das Modelle, Dienste und benutzerdefinierte Elemente verwendet.Verweis auf Objekt ist irgendwie verloren

Ich habe ein Benutzermodell mit Benutzername, E-Mail usw. sowie eine Bool isLoggedIn.

Ich habe einen Benutzer-Dienst, der den Benutzer in localstorage speichert, können Sie die Benutzerdaten aktualisieren, etc etc.

ich einige benutzerdefinierte Elemente schließlich haben, die den Benutzer-Dienst verwenden den aktuellen Benutzer zu holen und eine Benutzeroberfläche angezeigt werden Das hängt davon ab, ob der Benutzer angemeldet ist oder nicht.

Das Problem, mit dem ich konfrontiert bin, ist, dass, nachdem ich den Benutzer mithilfe des Benutzerdienstes abgerufen und dann das Benutzermodell von einem meiner benutzerdefinierten Elemente (im Benutzerservice gespeichert) aktualisiert die Referenz eines anderen benutzerdefinierten Elements auf diesen Benutzer Modell wird nicht aktualisiert.

Ich habe versucht, das Problem in einem JSFiddle zu replizieren (in plain JS - ich schreibe dies in ES6), aber es versäumt, dies zu tun, in der Geige funktioniert alles gut.

Es gibt ziemlich viel Code, aber ich würde gerne von Ihnen hören, was ich vielleicht falsch gemacht habe, damit das überhaupt passiert?

Hier ist der Kern von dem, was ich habe:

user.model.js

export class User { 
    username = ''; 
    isLoggedIn = false; 

    constructor (data) { 
     Object.assign(this, data); 
    } 
} 

user.service.js

import { User } from 'user.model'; 

export class UserService { 
    constructor() { 
     this.user = null; 
    } 

    getUser() { 
     // User is cached 
     if (this.user) { 
      return Promise.resolve(this.user); 
     } 

     // Check if we have localStorage 
     var user = window.localStorage.getItem('user'); 

     if (user) { 
      this.user = new User(JSON.parse(user)); 

      return Promise.resolve(this.user); 
     } 

     // No user - create new 
     this.user = new User(); 

     return Promise.resolve(this.user); 
    } 

    saveUser() { 
     this.getUser().then(() => { 
      window.localStorage.setItem('user', JSON.stringify(this.user)); 
     }); 
    } 

    updateUser (user) { 
     this.getUser().then(() => { 
      Object.assign(this.user, user); 

      this.saveUser(); 
     }); 
    } 

    login() { 
     this.updateUser({ 
      isLoggedIn: true 
     }); 

     return Promise.resolve(true); 
    } 
} 

custom-element.js

import { inject } from 'aurelia-framework'; 

import { UserService } from 'user.service'; 

@inject (UserService) 
export class CustomElement { 
    constructor (userService) { 
     this.userService = userService; 
    } 

    attached() { 
     this.userService.getUser().then(user => { 
      this.user = user; 

      console.log('Fetched user:'); 
      console.dir(this.user); 

      console.log('Same?'); 
      console.dir(this.user === user); // this is true 
     }); 

     // At some point another custom element fires the UserService.login() method 
     // Here we check that our reference to the user also updates 

     setInterval(() => { 
      console.log('Is user logged in?'); 
      console.dir(this.user.isLoggedIn); // This is always false - even after UserService.login() has been called and UserService.user is updated (if I console.dir this.user inside UserService it is indeed updated) 

      // Grab a new version of UserService.user 
      this.userService.getUser().then(user => { 
       console.log('Fetched new user, is new user and our user same?'); 
       console.dir(this.user === user); // false :/ the new user fetched here actually has isLoggedIn === true but our this.user does not... 
      }); 
     }, 2000); 
    } 
} 

Als ein anderes benutzerdefiniertes Element, das UserService.user.isLoggedIn zu true ändert läuft UserService.login() an einem Punkt in den Kommentaren darauf hingewiesen (dies im UserService reflektiert wird, wenn ich seine this.userconsole.dir) this.user aber die anderen CustomElement ‚s tut nicht Updates.

Btw: Der Grund für die Promise.resolve() in UserService.getUser() ist, dass in Zukunft Serveraufrufe drin sein werden.

Tbh Ich bin ziemlich neu in dieser Art von Programmierung in JS, komme eher aus einer jQuery-Welt, und obwohl ich im Grunde in Aurelia verliebt bin, verwirrt mich das immer noch sehr und hofft auf einen Einblick:)

Antwort

2

Mit Blick auf den Code, den Sie angegeben haben, sehe ich nicht, was das beschriebene Verhalten verursachen würde.

One suggestion- Ihren Code ein wenig leichter zu verwalten, weniger async ... was ist, wenn Sie es wie folgt strukturiert:

user.js

export class User { 
    loggedIn = false; 
    name = 'Anonymous'; 
} 

Benutzer-store .js

@inject(User) 
export class UserStore { 
    constructor(user) { 
    this.user = user; 
    } 

    load() { 
    const serializedUser = localStorage.getItem('user'); 
    if (!info) { 
     return; 
    } 
    const storageUser = JSON.parse(serializedUser); 
    this.user.loggedIn = storageUser.loggedIn; 
    this.user.name = storageUser.name; 
    } 

    save() { 
    const serializedUser = JSON.stringify(this.user); 
    localStorage.setItem('user', serializedUser); 
    } 
} 

Auth-service.js

@inject(User, UserStore) 
export class AuthService { 
    constructor(user, store) { 
    this.user = user; 
    this.store = store; 
    } 

    login(username, password) { 
    return fetch('https://api.megacorp.com/login', { method: 'POST' ... }) 
     .then(result => { 
     this.user.loggedIn = true; 
     this.user.name = result.name; 
     this.store.save(); 
     }); 
    } 
} 

custom-element.js

@inject(User) 
export class CustomElement { 
    constructor(user) { 
    this.user = user; 
    } 

    ... 
} 

Der Vorteil nie Ihre individuelle Wesen Elemente eine dep auf nehmen müssen etwas Asynchrones. Sie erhalten nur die User-Instanz der Anwendung, deren Eigenschaften sich ändern können, aber immer dieselbe Instanz bleiben wird.

+0

Hmm, ich bin mir nicht sicher, ob ich folge ... Was Sie anders machen, ist, dass 'UserStore.user' ein' new User() 'ist, also tatsächlich das_ Benutzermodell? Würde mich das nicht davon abhalten, das Benutzermodell woanders zu verwenden? Würden Sie auch einen separaten 'AuthService' empfehlen? Ich habe das ganze System im Grunde genommen selbst gemacht, ohne zu viel darüber zu lesen, wie man sowas strukturieren sollte. Wenn Sie einen Link zu einem Tutorial oder Artikel über OOP-Authentifizierung/Benutzerbehandlung in JS haben, würde ich es gerne lesen. – powerbuoy

+0

Sie könnten den UserStore und den AuthStore kombinieren, es gibt keine harte Regel, tun Sie, was am besten für Sie funktioniert. Die Hauptsache, die ich demonstrieren wollte, ist, dass die Dinge, die vom Benutzer abhängen, keine asynchrone API benötigen. Sie können auf die Benutzerinstanz zugreifen, indem sie eine Abhängigkeit von ihr nehmen (mit '@ inject'). Die Dinge, die * auf den Benutzer * wirken (indem sie ihn von einem anonymen Zustand in einen eingeloggten Zustand versetzen) können asynchron sein (der AuthService ist asynchron, der UserStore jedoch nicht). –

+0

Ok, ich werde etwas damit experimentieren müssen, um zu sehen, ob ich alles verstehe. Aber ist es richtig, dass der Hauptunterschied in Ihrem Code darin besteht, dass Sie 'UserService.user' auf die eigentliche' User' Klasse setzen, im Gegensatz zu einer Instanz der 'User' Klasse (mit' new User() ')? Ist das der Grund, warum ich, wenn ich später dieselbe "User" -Klasse in meine benutzerdefinierten Elemente injiziere, dieselben sein werden? – powerbuoy