none
Getting custom field to display as html RRS feed

  • Question

  • I am converting an SP2007 field to SP2013. My problem at the moment is that the html produced by the field is displayed as text.

    Here is the fldtypes code

    <?xml version="1.0" encoding="utf-8" ?>
    <FieldTypes>
      <FieldType>
        <Field Name="TypeName">PriorityIndicator2</Field>
        <Field Name="ParentType">Text</Field>
        <Field Name="TypeDisplayName">Priority Indicator</Field>
        <Field Name="TypeShortDescription">Priority Indicator </Field>
        <Field Name="UserCreatable">TRUE</Field>
        <Field Name="Sortable">TRUE</Field>
        <Field Name="AllowBaseTypeRendering">TRUE</Field>
        <Field Name="Filterable">TRUE</Field>
        <Field Name="FieldTypeClass">MDA.WebParts.PriorityIndicator2, MDA.WebParts.PriorityIndicator2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ec96bdcbce786400</Field>
        <Field Name="FieldEditorUserControl">/_controltemplates/15/PriorityIndicator2FieldEditor.ascx</Field>
        <Field Name="CAMLRendering">TRUE</Field>
        <PropertySchema>
          <Fields>
            <Field Hidden="TRUE" Name="MyCustomProperty"
            DisplayName="My Custom Property"
            Type="Text">
            </Field>
            <Field Hidden="TRUE" Name="Priorities"
            DisplayName="Priorities"
            Type="Text">
            </Field>
            <Field Hidden="TRUE" Name="Colors"
              DisplayName="Colors"
              Type="Text">
            </Field>
            
          </Fields>
          <Fields></Fields>
        </PropertySchema>
        <RenderPattern Name="DisplayPattern">
          <Html>
            <Column HTMLEncode="FALSE" />
          </Html>
         </RenderPattern>
      </FieldType>
    </FieldTypes>

    Here's the code that produces the output

    public override string GetValidatedString(object value)
            {
                if (value != null)
                {
                    return "<div style=\"line-height:24px;\"><div style='font-weight:bold; float: left; padding-right:5px;font-size:24px; color:" + GetColor(value.ToString()) + " ;'>&bull;</div>" + value.ToString() + "</div>";
                }
                else
                {
                    return "";
                }
            }

    Any help would be greatly appreciated.


    Luis R. Lebron

    Wednesday, July 24, 2013 1:11 PM

Answers

  • Hi,

    Well, that is harder than I expected it to be so it took awhile to respond.  I was hoping the CurrentFieldSchema property of the ctx variable passed to the field render function would have included the custom properties, but it does not.  So then I went through the exercise of trying to get it from the client object model, but became rather complex because that functionality is not yet loaded when this function is run.  It turns out the best way to get the custom properties is using the REST API.  I also found at this point, that I preferred to load JQuery in order to call the REST URL to get the field schema xml and parse it.  So it looks like you can do this:

    1. Make sure your Master Page has a script reference to the Jquery library (version 1.7.1 or 1.7.2)

    2. In the render function for your Priority field, you call out to get the Schema XML and use that to set the div color.  Note that I need to create a unique ID for each bullet div so that the async call to get the REST data updates just that div with the appropriate color.

    // The priorityIndicatorViewTemplate provides the rendering logic
    // the custom field type when it is displayed in the view form.
    
    // Counter of the number of priority fields on the page 
    // so that have a unique ID to set the color
    var divCount = 0;
    
    function priorityIndicatorViewTemplate(ctx) {
        var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
        var listID = ctx.listName;
        var divID = "priority" + divCount.toString();
    
        // Get rid of parentheses from list ID
        listID = listID.substring(1, listID.length - 1);
    
        $.ajax({
            url: "/_api/Web/Lists(guid'" + listID + "')/Fields(guid'" + ctx.CurrentFieldSchema.ID + "')/SchemaXml",
            type: "GET",
            headers: { "ACCEPT": "application/json;odata=verbose" },
            success: function (data) {
                var xmlDoc = $.parseXML(data.d.SchemaXml);
                var xml = $(xmlDoc);
                var priorities, colors;
                xml.find("Property").each(function () {
                    if ($("Name", this)[0].textContent == "Priorities") {
                        priorities = $("Value", this)[0].textContent;
                    }
                    if ($("Name", this)[0].textContent == "Colors") {
                        colors = $("Value", this)[0].textContent;
                    }
                });
                if (priorities != undefined && colors != undefined) {
                    var priorityArray = priorities.split(";");
                    var colorArray = colors.split(";");
                    for (var i = 0; i < priorityArray.length; i++) {
                        if (priorityArray[i] == value) {
                            var div = "#" + divID;
                            $(div).css("color", colorArray[i]);
                            break;
                        }
                    }
                }
            },
            error: function (data) {
                // handle error here as you like
            }
        });
        return "<div style=\"line-height:24px;\"><div style=\"float:left;font-weight:bold;padding-right:5px;font-size:2;\" id='" + divID + "'>&bull;</div>" + value + "</div>";
    }


    • Edited by mfharvey Monday, July 29, 2013 4:45 PM Removed extra "test" line
    • Marked as answer by Emir Liu Wednesday, July 31, 2013 1:20 AM
    Monday, July 29, 2013 4:44 PM
  • Okay, after a bit of testing, I have to slightly change the approach to the div ID, and I should have done this in the first place, but I learned something in the process.

    Turns out that my "divCount" variable is not defined when a page is reloaded with "Minimal Download Strategy" activated.  So if you click on another page and back to the list display, the "toString" reference throws an error.

    So it is better to just use the Item ID:

    var divID = "priority" + ctx.CurrentItem.ID.toString();

    Cheers,

    -mfharvey

    • Marked as answer by Emir Liu Wednesday, July 31, 2013 1:20 AM
    Monday, July 29, 2013 5:29 PM

All replies

  • I suggest that you switch to using the new Client-Side Rendering (CSR) approach of SharePoint 2013.  See http://msdn.microsoft.com/en-us/library/jj220061.aspx for the most straightforward instructions on how to do this.  Essentially, you will want to move your "GetValidatedString" logic into the javascript file.

    Note that when you use Client Side Rendering, you will run into issues with the "Minimal Download Strategy" feature that is enabled by default in Team Sites.  You have two options to deal with that. 

    1. Deactivate that feature in the site.
    2. Add RegisterModuleInit to your javascript file by changing the global function into a normal function declaration, calling it, and registering it.  In the example referenced above, the changes would be these:

    function FavoriteColorField_Init() {
        var favoriteColorContext = {};    
    
        // You can provide templates for:
        // View, DisplayForm, EditForm and NewForm
        favoriteColorContext.Templates = {};
        favoriteColorContext.Templates.Fields = {
            "FavoriteColorField": {
                "View": favoriteColorViewTemplate
            }
        };
        SPClientTemplates.TemplateManager.RegisterTemplateOverrides(
            favoriteColorContext
            );
    }
    FavoriteColorField_Init();
    RegisterModuleInit("CSRAssets/CSRFieldType.js", FavoriteColorField_Init);
    
    ...
    

    - mfharvey
    Friday, July 26, 2013 1:43 PM
  • The problem I see is that I have two properties associated with this field: Priorities and Colors. When the user adds the field to a list they enter a set of priorities and the colors that go with them like this

    "Priorities"

    High
    Medium
    Low

    "Colors"

    Red
    Yellow
    Green

    The field uses the color in the html and the priority as the value

    div style=\"line-height:24px;\"><div style='font-weight:bold; float: left; padding-right:5px;font-size:24px; color:" + GetColor(value.ToString()) + " ;'>&bull;</div>" + value.ToString() + "</div>";

    The GetColor function returns the color that matches the priority (i.e. High would return Red, Medium returns Yellow, etc)

    Here's the code

     public string Priorities
            {
                get { return this.GetCustomProperty("Priorities") + ""; }
                set { this.SetCustomProperty("Priorities", value); }
            }

            public string Colors
            {
                get { return this.GetCustomProperty("Colors") + ""; }
                set { this.SetCustomProperty("Colors", value); }
            }

    private string GetColor(string Priority) { string itemColor = ""; string[] MyPriorities = Priorities.Split(System.Environment.NewLine.ToCharArray()); string[] MyColors = Colors.Split(System.Environment.NewLine.ToCharArray()); int key = Array.IndexOf(MyPriorities, Priority); itemColor = MyColors[key]; return itemColor; }

    I do not see how I can get the custom properties via javascript.

    Luis


    Luis R. Lebron

    Friday, July 26, 2013 3:11 PM
  • Hi,

    Well, that is harder than I expected it to be so it took awhile to respond.  I was hoping the CurrentFieldSchema property of the ctx variable passed to the field render function would have included the custom properties, but it does not.  So then I went through the exercise of trying to get it from the client object model, but became rather complex because that functionality is not yet loaded when this function is run.  It turns out the best way to get the custom properties is using the REST API.  I also found at this point, that I preferred to load JQuery in order to call the REST URL to get the field schema xml and parse it.  So it looks like you can do this:

    1. Make sure your Master Page has a script reference to the Jquery library (version 1.7.1 or 1.7.2)

    2. In the render function for your Priority field, you call out to get the Schema XML and use that to set the div color.  Note that I need to create a unique ID for each bullet div so that the async call to get the REST data updates just that div with the appropriate color.

    // The priorityIndicatorViewTemplate provides the rendering logic
    // the custom field type when it is displayed in the view form.
    
    // Counter of the number of priority fields on the page 
    // so that have a unique ID to set the color
    var divCount = 0;
    
    function priorityIndicatorViewTemplate(ctx) {
        var value = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
        var listID = ctx.listName;
        var divID = "priority" + divCount.toString();
    
        // Get rid of parentheses from list ID
        listID = listID.substring(1, listID.length - 1);
    
        $.ajax({
            url: "/_api/Web/Lists(guid'" + listID + "')/Fields(guid'" + ctx.CurrentFieldSchema.ID + "')/SchemaXml",
            type: "GET",
            headers: { "ACCEPT": "application/json;odata=verbose" },
            success: function (data) {
                var xmlDoc = $.parseXML(data.d.SchemaXml);
                var xml = $(xmlDoc);
                var priorities, colors;
                xml.find("Property").each(function () {
                    if ($("Name", this)[0].textContent == "Priorities") {
                        priorities = $("Value", this)[0].textContent;
                    }
                    if ($("Name", this)[0].textContent == "Colors") {
                        colors = $("Value", this)[0].textContent;
                    }
                });
                if (priorities != undefined && colors != undefined) {
                    var priorityArray = priorities.split(";");
                    var colorArray = colors.split(";");
                    for (var i = 0; i < priorityArray.length; i++) {
                        if (priorityArray[i] == value) {
                            var div = "#" + divID;
                            $(div).css("color", colorArray[i]);
                            break;
                        }
                    }
                }
            },
            error: function (data) {
                // handle error here as you like
            }
        });
        return "<div style=\"line-height:24px;\"><div style=\"float:left;font-weight:bold;padding-right:5px;font-size:2;\" id='" + divID + "'>&bull;</div>" + value + "</div>";
    }


    • Edited by mfharvey Monday, July 29, 2013 4:45 PM Removed extra "test" line
    • Marked as answer by Emir Liu Wednesday, July 31, 2013 1:20 AM
    Monday, July 29, 2013 4:44 PM
  • Okay, after a bit of testing, I have to slightly change the approach to the div ID, and I should have done this in the first place, but I learned something in the process.

    Turns out that my "divCount" variable is not defined when a page is reloaded with "Minimal Download Strategy" activated.  So if you click on another page and back to the list display, the "toString" reference throws an error.

    So it is better to just use the Item ID:

    var divID = "priority" + ctx.CurrentItem.ID.toString();

    Cheers,

    -mfharvey

    • Marked as answer by Emir Liu Wednesday, July 31, 2013 1:20 AM
    Monday, July 29, 2013 5:29 PM