Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Am I doing it wrong? #7

Closed
timothylrobb opened this issue Oct 11, 2016 · 10 comments
Closed

Am I doing it wrong? #7

timothylrobb opened this issue Oct 11, 2016 · 10 comments

Comments

@timothylrobb
Copy link

I've been using the Javascript implementation of PureMVC and recently decided to try the Typescript implementation.

I've created a UserProxy and registered it with the facade. The UserProxy looks like this:

namespace myapp {
    export class UserProxy extends puremvc.Proxy {
        /* override */
        private _first_name:string = "";
        private _last_name:string = "";
        private _email:string = "";
        private _created_at:Date;
        private _logged_in:boolean = false;

        public get first_name():string {
            return this._first_name;
        }

        public get last_name():string {
            return this._last_name;
        }

        public get email():string {
            return this._email;
        }

        public get created_at():Date {
            return this._created_at;
        }

        public get logged_in():boolean {
            return this._logged_in;
        }

        public getUserInfo():Promise<any> {
            var self = this;
            var request = fetch("/user/profile", {credentials: 'same-origin'})
                    .then(function (response) {
                        if (response.status >= 200 && response.status < 300) {
                            return response;
                        } else {
                            // TODO
                            // Show the error dialog for a server-side error
                            // Attempt to report the error to the server
                        }
                    })
                    .then(function (response) {
                        return response.json();
                    })
                    .then(function (response) {
                        console.log(response);
                        if (response.msg === "OK") {
                            if (response.loggedin) {
                                self._email = response.user.email;
                                self._first_name = response.user.first_name;
                                self._last_name = response.user.last_name;
                                self._created_at = response.user.created_at;
                                self._logged_in = response.loggedin;
                            } else {
                                self._email = "";
                                self._first_name = "";
                                self._last_name = "";
                                self._created_at = null;
                                self._logged_in = false;
                            }
                            self.facade().sendNotification(myapp.UserProxy.USER_PROXY_UPDATED); // Report that it's been updated
                            return response;
                        } else {
                            // TODO
                            // Show the error dialog
                            // Report the error to the server
                        }
                    });
            return request;
        }

        public static USER_PROXY_UPDATED:string = "userProxyUpdated";
    }
}

But, when I retrieve the proxy from my controller:

var userProxy:UserProxy = this.facade().retrieveProxy(myapp.UserProxy.NAME);
var request = userProxy.getUserInfo();

I get the following type-checking error:

[ts] Type 'IProxy' is not assignable to type 'UserProxy'.
       Property '_first_name' is missing in type 'IProxy'.
(local var) userProxy: UserProxy

Am I not supposed to be tied down to the methods and properties in the interface? It seems very limiting for the model to be locked to these methods. I guess I can create a separate model class and use setData to instantiate it. Is that the intended approach?

@cliffhall
Copy link
Member

cliffhall commented Oct 11, 2016

It appears that TypeScript (as of 1.6) is unhappy with additional properties being added to a class.

An interface should ensure that certain members are always or optionally present, but the class definition should be able to add members. If you want to handle an object by interface, then you only get to access those members. If you do a widening cast to the class itself, then it should be able to type check the additional members.

This is how it worked in the original ActionScript, and usually works elsewhere.

Here is the issue thread on TypeScript project that discusses it:
microsoft/TypeScript#3755

So, it appears that two approaches could be taken here:

  1. Create a VO and use setData/getData to access it. Yes, this is how it was originally intended anyway, because VOs often get handed to view components for display and modification. View components are supposed to be agnostic to the framework, they just need to be fed data. So if you were intending to pass this proxy to a view component for display, this issue actually sets you on the right path. Use a VO.

  2. Even if you're using a VO to pass data around, you still should be able to have properties and methods that aren't defined on the interface in your concrete class. You could define an interface like IUserProxy that defines the extra properties, add those members to it, and make this UserProxy extend puremvc.Proxy AND implement IUserProxy.

@timothylrobb
Copy link
Author

Thank you for taking the time to look into this. That's disappointing. But since it is the way it is, I think I will go with the route that involves creating a VO and then using the onRegister override to instantiate it.

Best Regards,
Tim

@cliffhall
Copy link
Member

Timothy, I wonder, while you are working with the TS port, if you could clone the Employee Admin repo https://github.com/PureMVC/puremvc-typescript-demo-employeeadmin.git and see if it has such problems going on when you use the latest TS. Because the proxies there, although they do use VOs, definitely have non-interface methods defined. If you don't have the time, I totally understand and I'll look into it myself shortly.

@timothylrobb
Copy link
Author

I should have time this evening.

@timothylrobb
Copy link
Author

After examining the employee admin demo, I found that using type casting resolved the indifference between the interface IProxy and the concrete class:

var user:UserProxy = <UserProxy>this.facade().retrieveProxy(recypt.UserProxy.NAME);

I have found a number of errors, though, due to changes in the Typescript language overtime. For instance, declaring a boolean used to be something like this:

var result:bool = false;

Today it's like this:

var result:boolean = false;

I also see code like this:

var users:UserVO[] = new UserVO[]();

Which should be changed to this:

var users:UserVO[] = new Array<UserVO>();

There are also a number of issues with the Typescript Definitions files for jQuery, jQueryUI, and PureMVC-standard.

Finally, Typescript does not need to make use of ANT and JDK to compile. You can now use the command (tsc -init) to create a configuration file which can then be configured to identify which files get included into the resulting Javascript file. After that's setup you can simply type 'tsc' to compile the Javascript. It currently won't successfully compile.

Best Regards,
Tim

@timothylrobb
Copy link
Author

timothylrobb commented Oct 12, 2016

Note that the 1.1 version of the PureMVC Multicore Typescript Definitions doesn't have any errors in it with the current version of Typescript.

@cliffhall
Copy link
Member

cliffhall commented Oct 15, 2016

I've been spending a lot of time working with untyped JS. Fzzzz. Yes, the widening cast should generally be done when you fetch a Proxy or Mediator. The original actionscript for your fetch above was:

var userProxy:UserProxy = UserProxy( facade.retrieveProxy( UserProxy.NAME ) );

I see that in the existing typescript we're doing it like this:

var userProxy:UserProxy = <UserProxy> this.facade.retrieveProxy( ProxyNames.USER_PROXY );

@cliffhall
Copy link
Member

cliffhall commented Oct 15, 2016

And thanks for turning up that list of todos, which I'll condense here, along with my observations:

  1. Fix definitions file for Typescript Standard based on the working Multicore file.
  2. Change bool to boolean in this demo and Standard framework. It's been fixed in Multicore.
  3. Change the casting of arrays, e.g., new UserVO[](); becomes new Array<UserVO>();.
  4. Moved to compiling with tsc instead of Ant and JDK.

@timothylrobb
Copy link
Author

Thanks again. My application is coming together quite nicely.

@cliffhall
Copy link
Member

Great to heat it. Let us know when it's done. Maybe blog about your experience.

Cheers,
-=Cliff>

Sent from my iPhone

On Oct 15, 2016, at 12:23 PM, Timothy Robb notifications@github.com wrote:

Thanks again. My application is coming together quite nicely.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants