Breeze.js typed entities

断了今生、忘了曾经 提交于 2019-12-01 19:20:13
Jay Traband

We do have plans to do more with TypeScript, but haven't yet committed to a specific time frame. (Boris's work, mentioned in John's post, is certainly a great start).

UPDATE: As of v 0.84.4, Breeze provides full TypeScript support.

As far as automatically generating design time Javascript classes for each entity; we have had several internal discussions on this, but are really waiting to see what the community votes for. We will almost certainly get around to this at some point, but your votes on the
UserVoice can definitely expedite the process.

This gist contains a modified version of T4TS that contains some initial support for generating "design time Javascript classes for each entity" with support for Breeze.

https://gist.github.com/alexdresko/5393155

So far, it suits my needs. I'm pretty sure you need to have the DefinitelyTyped Breeze definition in your solution for this to work properly.

Maybe this is something that could be added to T4TS permanently? Maybe it's something for the Breeze team to consider? Or maybe it's just stupid and doesn't really work for anyone but me. :)

In my dataservice, I can do something like:

    createOrganization() : T4TS.Organization {
        return <T4TS.Organization>this.manager.createEntity("Organization");
    }

Then, in my VM, all of this code is nicely typesafe..

    organizationSubmit() {
        this.editingOrganization(false);
        var newOrganization = this.dataservice.createOrganization();

        newOrganization.Name(this.organizationNameInput());
        if (newOrganization.entityAspect.validateEntity()) {
            this.extendOrganization(newOrganization);
            this.organizations.push(newOrganization);
            this.dataservice.saveChanges();
            this.organizationNameInput("");
        } else {
            this.handleItemErrors(newOrganization);

        }
    };

I don't really know where to go with this from here. I tried forking T4TS, but didn't have time to figure out his build system. Hence the gist. Opinions are certainly welcome.

Below is a page you can drop in your site to generate typescript interface definitions. The page fetches the breeze metadata then iterates through all of the types and outputs a typescript interface declaration for each type. The output of this page can then be pasted in any typescript file (*.ts) or typescript definition file (*.d.ts). Enclose the results in a module declaration if you want to namespace the interfaces: declare module northwind { ... paste interfaces here... }.

Before using the page you'll need to make one edit: change the entity manager's controller url from "api/northwind" to whatever your breeze controller's url is.

The generated interfaces have a dependency on the Knockout.js typescript definitions which you can grab here: https://github.com/borisyankov/DefinitelyTyped/tree/master/knockout/

Using the northwind example from learn.breezejs.com, the output of this definitions generator page would be something like this:

export interface Employee extends breeze.Entity {
    FirstName: KnockoutObservable<string>;
    LastName: KnockoutObservable<string>;
}

you could then execute a query using breeze and cast the results to an array of employees like this:

var manager = new breeze.EntityManager('api/northwind');

var query = new breeze.EntityQuery()
    .from("Employees");

manager.executeQuery(query).then(data => {
    // ***cast the results to a strongly typed array of Employee***
    var employees = <Employee[]>data.results;
}).fail(e => {
    alert(e);  
});

below is the definitions generator page- add a new html file to your project named "definitions.html", run the project and navigate to the page.

<html>
<head>
    <title>Typescript Definition Generator</title>
    <style>
        code {
            white-space: pre;
        }
    </style>
    <script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.0.0/q.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/breezejs/1.4.4/breeze.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var entityManager = new breeze.EntityManager('api/northwind');
            entityManager.fetchMetadata()
                .then(function () {
                    var html = '',
                        types = entityManager.metadataStore.getEntityTypes(),
                        type,
                        i,
                        j,
                        property,
                        crlf = String.fromCharCode(13),
                        code = document.createElement('code'),
                        script = document.createElement('script');

                    function getJSType(metadataType) {
                        if (/(Int64)|(Int32)|(Int16)|(Byte)|(Decimal)|(Double)|(Single)|(number)/.test(metadataType))
                            return 'number';
                        else if (/(DateTime)|(DateTimeOffset)|(Time)|(Date)/.test(metadataType))
                            return 'Date';
                        else if (/(Boolean)/i.test(metadataType))
                            return 'boolean';
                        return 'string';
                    }

                    for (i = 0; i < types.length; i++) {
                        // type declaration
                        var type = types[i];
                        html += 'export interface ' + type.shortName;

                        // base type
                        html += ' extends ';
                        if (type.hasOwnProperty('baseEntityType')) {
                            html += type.baseEntityType.shortName;
                        } else {
                            html += 'breeze.Entity';
                        }
                        html += ' {' + crlf;

                        // data properties
                        for (j = 0; j < type.dataProperties.length; j++) {
                            property = type.dataProperties[j];
                            if (type.baseEntityType && type.baseEntityType.dataProperties.filter(function (p) { return p.name === property.name; }).length > 0)
                                continue;
                            html += '    ' + property.name;
                            //if (property.isNullable)
                            //    html += '?';
                            html += ': KnockoutObservable&lt;';
                            html += getJSType(property.dataType.name);
                            html += '&gt;; //' + property.dataType.name + crlf;
                        }

                        // navigation properties
                        for (j = 0; j < type.navigationProperties.length; j++) {
                            property = type.navigationProperties[j];
                            if (type.baseEntityType && type.baseEntityType.navigationProperties.filter(function (p) { return p.name === property.name; }).length > 0)
                                continue;
                            html += '    ' + property.name;
                            //if (property.isNullable)
                            //    html += '?';
                            if (property.isScalar)
                                html += ': KnockoutObservable&lt;';
                            else
                                html += ': KnockoutObservableArray&lt;';
                            html += property.entityType.shortName;
                            html += '&gt;;' + crlf;
                        }

                        html += '}' + crlf + crlf;
                    }

                    code.innerHTML = html;

                    $(code).addClass('prettyprint');

                    document.body.appendChild(code);

                    script.setAttribute('src', '//google-code-prettify.googlecode.com/svn/loader/run_prettify.js');
                    document.body.appendChild(script);
                })
                .fail(function (reason) {
                    alert(reason);
                });
        });
    </script>
</head>
<body>
</body>
</html>

For breeze TypeScript defiintions, use NuGet package breeze.TypeScript.DefinitelyTyped. To create definitions for your entities, you can use TypeLITE. It will create a .d.ts file with interfaces like this:

declare module MyProject.Models {
interface Customer {
  Id: number;
  AccountNumber: string;
  ...

While these interfaces fully describe your entities, they don't extend breeze.Entity. To play nicely with the Breeze API type definitions, derive your own interfaces from these, like this:

import Models = MyProject.Models;
interface Customer extends Models.Customer, breeze.Entity { }
...

This still involves some manual coding, but at least it's only per type, not per property.

Regarding Typescript, it is definitely on our radar. At a minimum we will create a TypeScript 'declare' file that will allow better intellisense when using Breeze.

You can vote it up over on UserVoice.

Until then you might want to use Boris Yankov's DefinitelyTyped which supports Breeze.

my two cents...

I have been using the definatelytyped definition file for breeze and it seems to work well.

It is fairly straightforward to create a T4 script to generate .d.ts files for all the entities in an .edmx and then you get the intellisense you desire. If Ward likes, I could post a sample T4 script somewhere.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!