Copying PropertyGroup values
Example using custom node template.



Test code, put together with the custom nodes template for 2.8. Recommend that scripting now in 2.8 will save time going forward

Using Set up methods in the property group have a method to draw, and set and get from another object that shares the same pointer.

The draw method simply iterates and draws a basic prop layout for each property in way of example.  I've added a list of chosen props. Begs for a nicely formatted method.

The operator requires context.node  this has been added to the context at where the operator is displayed in custom node UI, the context member is set to the node, which is self in the node code.  For the N panel draw, the context active node is used instead.

    def draw_buttons(self, context, layout):
        box = layout.box()
        box.context_pointer_set("node", self)
        op = box.operator("node.configure")  # XXX


The config properties are drawn using. box is any layout element defined, could be self.layout or self.layout.column() etc.

        self.config.draw(box, context)


Note could flag to use self.node instead of self above  would display the node values directly.  Contemplating using this but inverting the operator to revert if cancelled, and make props live so one can dynamically dick around and only save when happy.

The node values are loaded on invoke using CustomNodeConfig.getfrom and set on execute with CustomNodeConfig.setfrom

Note It appears the config pointer will not register on the operator if it has an ID type pointer as a member.

from nodeitems_utils import NodeCategory, NodeItem
import nodeitems_utils
import bpy
from bpy.types import NodeTree, Node, NodeSocket


class CustomNodeConfig(bpy.types.PropertyGroup):
    __props = ["x", "my_enum_prop"]
    x: bpy.props.FloatProperty(name="X factor")
    my_items = (
        ('DOWN', "Down", "Where your feet are"),
        ('UP', "Up", "Where your head should be"),
        ('LEFT', "Left", "Not right"),
        ('RIGHT', "Right", "Not left"),
    )

    my_enum_prop: bpy.props.EnumProperty(
        name="Direction",
        description="Just an example",
        items=my_items,
        default='UP',
    )

    def getfrom(self, node):
        for p in node.__props:
            setattr(self, p, getattr(node, p))

    def setfrom(self, node):
        for p in self.__props:
            setattr(node, p, getattr(self, p))

    def draw(self, layout, context):
        layout.label(text="Settings...")
        for p in self.__props:
            layout.prop(self, p)


class NODE_OT_configure(bpy.types.Operator):
    bl_label = "Configure"
    bl_idname = "node.configure"
    node = None
    config: bpy.props.PointerProperty(type=CustomNodeConfig)

    def invoke(self, context, event):
        self.node = context.node
        if self.node:
            self.config.getfrom(self.node.config)
        return context.window_manager.invoke_props_dialog(self)

    def execute(self, context):

        if self.node:

            self.config.setfrom(self.node.config)
        # Copy config -&gt; node_ref.conf
        return {'FINISHED'}

    def draw(self, context):
        if self.node:
            self.layout.label(text="%s" % (repr(self.node.id_data)))
            self.layout.label(text="%s" % (self.node.name))
        self.config.draw(self.layout, context)


class MyCustomTree(NodeTree):
    '''A custom node tree type that will show up in the node editor header'''
    bl_idname = 'CustomTreeType'
    bl_label = "Custom Node Tree"
    bl_icon = 'NODETREE'


class MyCustomTreeNode:
    @classmethod
    def poll(cls, ntree):
        return ntree.bl_idname == 'CustomTreeType'


class MyCustomNode(Node, MyCustomTreeNode):
    '''A custom node'''
    bl_idname = 'CustomNodeType'
    bl_label = "Custom Node"
    bl_icon = 'SOUND'

    my_string_prop: bpy.props.StringProperty()
    my_float_prop: bpy.props.FloatProperty(default=3.1415926)
    config: bpy.props.PointerProperty(type=CustomNodeConfig)

    def init(self, context):
        self.inputs.new('NodeSocketFloat', "World")
        self.outputs.new('NodeSocketFloat', "How")

    def copy(self, node):
        print("Copying from node ", node)

    def free(self):
        print("Removing node ", self, ", Goodbye!")

    def draw_buttons(self, context, layout):
        box = layout.box()
        box.context_pointer_set("node", self)
        op = box.operator("node.configure")  # XXX
        self.config.draw(box, context)
        layout.label(text="Node settings")
        layout.prop(self, "my_float_prop")

    def draw_buttons_ext(self, context, layout):
        box = layout.box()
        box.context_pointer_set("node", context.active_node)
        op = box.operator("node.configure")
        self.config.draw(box, context)
        layout.prop(self, "my_float_prop")
        layout.prop(self, "my_string_prop")

    def draw_label(self):
        return "I am a custom node"


class MyNodeCategory(NodeCategory):
    @classmethod
    def poll(cls, context):
        return context.space_data.tree_type == 'CustomTreeType'


# all categories in a list
node_categories = [
    # identifier, label, items list
    MyNodeCategory(
        'SOMENODES',
        "Some Nodes",
        items=[
            # our basic node
            NodeItem("CustomNodeType"),
        ]),
]

classes = (
    CustomNodeConfig,
    NODE_OT_configure,
    MyCustomTree,
    MyCustomNode,
)


def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

    nodeitems_utils.register_node_categories('CUSTOM_NODES', node_categories)


def unregister():
    nodeitems_utils.unregister_node_categories('CUSTOM_NODES')

    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)


if __name__ == "__main__":
    register()