breezejs: overriding displayname
Asked Answered
P

7

7

I'm in the process of customizing validation messages. It's working fine using the messageTemplates property. However it uses %displayName% to render the name of the property and I can't find out how to override this value ? Is there anyway to do that ?

Parotitis answered 24/5, 2013 at 10:48 Comment(0)
S
7

This is not YET well documented but you can simply set the 'displayName' property on any dataProperty and this will override the autogenerated display name and will be used for all validation messages for this property. So

 var custType = myEntityManager.metadataStore.getEntityType("Customer");
 var dp = custType.getProperty("companyName");
 dp.displayName = "My custom display name";

Also, please see "Customize the message templates" at the bottom of this page: Breeze Validation

Sociability answered 24/5, 2013 at 16:56 Comment(2)
like I said, this only able me to customize the message, which I already do. But how do I override the property display name ? If I have a property called ProductTitle, I would like %displayName% to show 'product title' for instance.Parotitis
My mistake, I should have read your post more carefully. I have updated the answer above.Sociability
O
14

I was wanting to do this also but I wanted to use the [DisplayName] attribute from my EF model. I couldn't find anyone that had an example of doing this so after I found a way I thought I would share.

First, I extended the metadata returned from my BreezeController:

[HttpGet]
public string Metadata()
{
    // Extend metadata with extra attributes
    JObject metadata = JObject.Parse(contextProvider.Metadata());
    string nameSpace = metadata["schema"]["namespace"].ToString();
    foreach (var entityType in metadata["schema"]["entityType"])
    {
        string typeName = entityType["name"].ToString();
        Type t = Type.GetType(nameSpace + "." + typeName);
        foreach (var prop in t.GetProperties())
        {
            foreach (var attr in prop.CustomAttributes)
            {
                string name = attr.GetType().Name;
                foreach (var p in entityType["property"])
                {
                    if (prop.Name == p["name"].ToString()) {
                        if (attr.AttributeType.Name == "DisplayNameAttribute") {
                            DisplayNameAttribute a = (DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute));
                            p["displayName"] = a.DisplayName;
                            break;
                        }
                    }
                }
            }
        }
    }

    return metadata.ToString();
}

Then I added a little javascript after the metadata load to poke the display names from the augmented metadata where Breeze wants to find them.

manager.fetchMetadata().then(function (md) {
    angular.forEach(md.schema.entityType, function (et) {
        var etype = manager.metadataStore.getEntityType(et.name);
        angular.forEach(et.property, function (p) {
            var prop = etype.getProperty(p.name);
            prop.displayName = p.displayName;
        });
    });

    console.log("starting app");
    angular.bootstrap($("#app"), ["app"]);
});

I'm using angular so if you aren't you can ignore the angular stuff and probably get the idea. This seems to work rather nicely. It should be pretty easy to extend this to other model attributes as well like the RegularExpression validation attribute. I'll probably work on that next.

FYI, some of this code is not optimized and could probably be refactored, prettied up a bit but I just got it working and thought I would share. If anyone has any suggestions of a better way let me know. Hopefully Breeze will allow extending the metadata in a more supported way in the future. This does seem like a bit of a hack.

Oversee answered 14/6, 2013 at 4:56 Comment(0)
S
7

This is not YET well documented but you can simply set the 'displayName' property on any dataProperty and this will override the autogenerated display name and will be used for all validation messages for this property. So

 var custType = myEntityManager.metadataStore.getEntityType("Customer");
 var dp = custType.getProperty("companyName");
 dp.displayName = "My custom display name";

Also, please see "Customize the message templates" at the bottom of this page: Breeze Validation

Sociability answered 24/5, 2013 at 16:56 Comment(2)
like I said, this only able me to customize the message, which I already do. But how do I override the property display name ? If I have a property called ProductTitle, I would like %displayName% to show 'product title' for instance.Parotitis
My mistake, I should have read your post more carefully. I have updated the answer above.Sociability
S
3

Following jpcoder request for suggestions, here goes my slightly improved server portion:

JObject metadata = JObject.Parse(contextProvider.Metadata());
string nameSpace = metadata["schema"]["namespace"].ToString();
foreach (var entityType in metadata["schema"]["entityType"])
{
    string typeName = entityType["name"].ToString();
    Type t = Type.GetType(nameSpace + "." + typeName);

    IEnumerable<JToken> metaProps = null;
    if (entityType["property"].Type == JTokenType.Object)
        metaProps = new[] { entityType["property"] };
    else
        metaProps = entityType["property"].AsEnumerable();

    var props = from p in metaProps
                let pname = p["name"].ToString()
                let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                where prop != null
                from attr in prop.CustomAttributes
                where attr.AttributeType.Name == "DisplayNameAttribute"
                select new
                {
                    Prop = p,
                    DisplayName = ((DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute))).DisplayName
                };
    foreach (var p in props)
        p.Prop["displayName"] = p.DisplayName;
}
Stencil answered 16/10, 2013 at 15:50 Comment(0)
B
0

Looking at http://www.breezejs.com/sites/all/apidocs/files/a40_entityMetadata.js.html#l1452,

could this be improved by renaming the existing "name" value to nameOnServer (to satisfy the getDataProperty call) and inserting the DisplayNameAttribute value as "name"?

Bajaj answered 16/7, 2013 at 1:45 Comment(0)
C
0

A necessary change to the server code is that, if your model classes are in different assemblies, you cannot use

Type t = Type.GetType(nameSpace + "." + typeName);

You need the namespace per type (which is in the metadata), and (I think) to use BuildManager to locate the appropriate types in different assemblies. The mapping from cSpaceOSpaceMapping might be achieved more elegantly, but I didn't have time to research the different json formatting options.

JObject metadata = JObject.Parse(UnitOfWork.Metadata());
string EFNameSpace = metadata["schema"]["namespace"].ToString();
string typeNameSpaces = metadata["schema"]["cSpaceOSpaceMapping"].ToString();
typeNameSpaces = "{" + typeNameSpaces.Replace("],[", "]|[").Replace("[", "").Replace("]", "").Replace(",", ":").Replace("|", ",") + "}";
        JObject jTypeNameSpaces = JObject.Parse(typeNameSpaces);

        foreach (var entityType in metadata["schema"]["entityType"])
        {
            string typeName = entityType["name"].ToString();
            string defaultTypeNameSpace = EFNameSpace + "." + typeName;
            string entityTypeNameSpace = jTypeNameSpaces[defaultTypeNameSpace].ToString();
            Type t = BuildManager.GetType(entityTypeNameSpace, false);

            IEnumerable<JToken> metaProps = null;
            if (entityType["property"].Type == JTokenType.Object)
                metaProps = new[] { entityType["property"] };
            else
                metaProps = entityType["property"].AsEnumerable();

            var props = from p in metaProps
                        let pname = p["name"].ToString()
                        let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                        where prop != null
                        from attr in prop.CustomAttributes
                        where attr.AttributeType.Name == "DisplayNameAttribute"
                        select new
                        {
                            Prop = p,
                            DisplayName = ((DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute))).DisplayName
                        };
            foreach (var p in props)
            {
                p.Prop["displayName"] = p.DisplayName;
            }
        }
Coussoule answered 16/1, 2014 at 12:54 Comment(0)
B
0
        JObject metadata = JObject.Parse(this._context.Metadata());
        string EFNameSpace = metadata["schema"]["namespace"].ToString();
        string typeNameSpaces = metadata["schema"]["cSpaceOSpaceMapping"].ToString();
        typeNameSpaces = "{" + typeNameSpaces.Replace("],[", "]|[").Replace("[", "").Replace("]", "").Replace(",", ":").Replace("|", ",") + "}";
        JObject jTypeNameSpaces = JObject.Parse(typeNameSpaces);

        foreach (var entityType in metadata["schema"]["entityType"])
        {
            string typeName = entityType["name"].ToString();
            string defaultTypeNameSpace = EFNameSpace + "." + typeName;
            string entityTypeNameSpace = jTypeNameSpaces[defaultTypeNameSpace].ToString();
            Type t = BuildManager.GetType(entityTypeNameSpace, false);

            IEnumerable<JToken> metaProps = null;
            if (entityType["property"].Type == JTokenType.Object)
                metaProps = new[] { entityType["property"] };
            else
                metaProps = entityType["property"].AsEnumerable();

            var props = from p in metaProps
                        let pname = p["name"].ToString()
                        let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                        where prop != null
                        from attr in prop.CustomAttributes
                        where attr.AttributeType.Name == "DisplayAttribute"
                        select new
                        {
                            Prop = p,
                            DisplayName = ((DisplayAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayAttribute))).Name
                        };
            foreach (var p in props)
            {
                p.Prop["displayName"] = p.DisplayName;
            }
        }

        return metadata.ToString();
Bosh answered 9/5, 2016 at 18:34 Comment(1)
While this code may answer the question, it would be better to include some context, explaining how it works and when to use it. Code-only answers are not useful in the long run.Gallon
S
0

Improving jpcoder's answer ...

For me most of my DisplayName changes were to replace "PascalCaseFieldName" or "camelCaseFieldName" with "Upper Case Field Name". So rather than set every property DisplayName on the server, I applied a default function to set displayName.

End result was much less EF annotation required. My TypeScript is:

    manager.metadataStore.getEntityTypes().forEach(function (storeEntityType) {
        if (!(storeEntityType instanceof breeze.EntityType)) {
            throw new Error("loadExtendedMetadata found '" + storeEntityType
                + "' StructuralType that is not an EntityType (e.g. a ComplexType)");
        }

        var extEntityType = extendedMetadata.entitiesExtended.find((extendedEntityType) => {
            return extendedEntityType.shortName + ":#" + extendedEntityType.nameSpace === storeEntityType.name;
        });


        (storeEntityType as breeze.EntityType).getProperties().forEach((storeProperty) => {
            //Both NavigationProperty & DataProperty have displayName & nameOnServer properties
            var storeDataProperty = <breeze.DataProperty>storeProperty;

            var extProperty;
            if (extEntityType) {
                extProperty = extEntityType.propertiesExtented.find((extendedProperty) => {
                    return extendedProperty.name === storeDataProperty.nameOnServer;
                });
            }

            //Smart default: nameOnServer "PascalCaseFieldName" or "camelCaseFieldName" converted to "Upper Case Field Name"
            storeDataProperty.displayName = (extProperty && extProperty.displayName)
                || storeDataProperty.nameOnServer.replace(/^./, function (str) {
                    // first ensure the first character is uppercase
                    return str.toUpperCase();
                    // insert a space before all caps, remove first character (added space)
                }).replace(/([A-Z])/g, " $1").substring(1);
        });
    });
Stalkinghorse answered 1/7, 2016 at 0:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.