[SalesForce] How to use progress ring with lightning component

In recent release salesforce has released progress ring part of SLDS. https://www.lightningdesignsystem.com/components/progress-ring/#content
I am little confused on how to use this with lightning component. My requirement is to show some kind of progress depending on Goal and actual values. Below is the code to start with. I can see the circle with green border. I had to add code into renderer to remove cDATA tags as lightning is not allowing to put the svg directly in the component. (I know there is a separate svg helper file but I am not sure how to reference the icon from the helper file into component so keeping directly into component.)

I think i need to be able to update the d in the path dynamically for this to work.

Any suggestions are welcome!

<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome" access="global">
    <div class="slds-progress-ring">
        <div class="slds-progress-ring__progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="58">
            <div aura:id="svg_content">
                <![CDATA[
                    <svg viewBox="-1 -1 2 2">
                        <path class="slds-progress-ring__path" id="slds-progress-ring-path" d="M 1 0 A 1 1 0 1 1 0.7289686274214112 -0.684547105928689 L 0 0" />
                    </svg>
                ]]>
            </div>
        </div>
        <div class="slds-progress-ring__content"></div>
    </div>

</aura:component>

Renderer

({
    // Your renderer method overrides go here

    afterRender: function(component, helper) {
        var svg = component.find("svg_content");
        var value = svg.getElement().innerText;
        value = value.replace("<![CDATA[", "").replace("]]>", "");
        svg.getElement().innerHTML = value;        
    }

})

Best Answer

You need to use custom rendering. This is not a complete example (it doesn't show any icons/variants), but this will get you started:


Component

<aura:component >
    <aura:attribute name="value" type="Integer" default="0" />
    <aura:handler name="init" value="{!this}" action="{!c.updateView}" />
    <aura:handler name="change" value="{!v.value}" action="{!c.updateView}" />
    <div class="slds-progress-ring">
        <div id="progressContainer" class="slds-progress-ring__progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="{!v.value}">

        </div>
        <div class="slds-progress-ring__content">
        </div>
    </div>
</aura:component>

Controller

({
    updateView: function(component, event, helper) {
        // Does nothing *now*, but would be responsible for
        // choosing the correct css class for the progress area, etc.
    }
})

Renderer

({
    // Create SVG, path, populate with default values from controller
    render: function(component, helper) {
        var result = this.superRender(),
            xmlns = "http://www.w3.org/2000/svg",
            updateContainer = result[0].querySelector("#progressContainer"),
            value = component.get("v.value"),
            dValue = "M 1 0 A 1 1 0 "+Math.floor(value / 50)+" 1 "+
                Math.cos(2 * Math.PI * value / 100)+" "+
                Math.sin(2 * Math.PI * value / 100)+" L 0 0",
            svg = document.createElementNS(xmlns,"svg"),
            path = document.createElementNS(xmlns,"path");
        svg.setAttributeNS(null,"viewBox", "-1 -1 2 2");
        path.setAttributeNS(null, "class", "slds-progress-ring__path");
        path.setAttributeNS(null, "d", dValue);
        svg.appendChild(path);
        updateContainer.appendChild(svg);
        return result;
    },
    // Update the progress bar on a rerender event
    rerender: function(component, helper) {
        var value = component.get("v.value"),
            dValue = "M 1 0 A 1 1 0 "+Math.floor(value / 50)+" 1 "+
                Math.cos(2 * Math.PI * value / 100)+" "+
                Math.sin(2 * Math.PI * value / 100)+" L 0 0",
            svg = component.getElement().querySelector("svg"),
            path = svg.childNodes[0];
        this.superRerender();
        path.setAttributeNS(null, "d", dValue);
    }
})

Note that this implementation uses an Integer value from 0-100. If you want to use a different scale (e.g. 0-1 floating point), adjust accordingly.