Issue #2279Opened September 19, 2019by brentonkelly19826 reactions

[QUESTION]: How to update the model for a custom component when a trait is changed.

Question

Hi! I have read and re-read the GrapesJS Docs and API Reference on how to create a custom component and I just can't seem to figure it out. I initially started trying to extend the existing video component but then decided to make my own custom video component. I'm using the existing video component as a guide. My component can be dragged onto the canvas and the component settings (traits) can be toggled but nothing updates in the canvas. My component tagName is a div because I need a wrapper to make the inner iframe responsive via CSS. That in itself could be an issue in how I'm adding the iframe but I can't get far enough with this to tell for sure.

I really appreciate any insight that you can provide!

Code snippet update and moved to closing comment

Answers (1)

brentonkelly1982September 20, 20196 reactions

I have finally figured this out on my own and want to share the result for anyone else that is struggling with this the way I did. This code snipped creates a custom, responsive video component that supports YouTube, Vimeo, MyVRSpot, and a custom iframe src. I was able to get the model to update in the canvas when a component setting (trait) is changed. I hope someone else finds this helpful.

(function() {
    var PageBuilder = {
        
	// ...

        "BuildGrapesEditor": function() {
            // INITIALLY LOAD THE SAVED APP CONTENT INTO THE EDITOR
            var savedComponents = JSON.parse($("#grapes-components").val());

            // DEFINE CUSTOM COMPONENTS *BEFORE* GRAPES INITIALIZATION (VERY IMPORTANT) - https://grapesjs.com/docs/modules/Components.html#define-custom-component-type
            // ALSO ADD PLUGIN NAMES IN THIS METHODS RETURNED ARRAY
            var plugins = this.GatherCustomComponents();

            // INITIALIZE THE GRAPES EDITOR
            this.GrapesEditor = grapesjs.init({
                container: "#cs-grapes-editor",
                height: "700px",
                width: "auto",
                showOffsets: true,
                allowScripts: true,
                components: savedComponents,
                style: $("#grapes-css").val(),
                storageManager: {
                    type: null,
                    autosave: false,
                },
                avoidInlineStyle: true,
                plugins: plugins
            });

            this.GrapesCommands = this.GrapesEditor.Commands;
            this.GrapesPanels = this.GrapesEditor.Panels;
            this.GrapesBlocks = this.GrapesEditor.BlockManager;
        },

        "GatherCustomComponents": function() {
            var _this = this;

            // CUSTOM VIDEO COMPONENT
            var customVideo = function(editor) {

                editor.DomComponents.addType("csVideo", {

                    // FRAMEWORK METHOD - https://grapesjs.com/docs/modules/Components.html#model
                    "model": {

                        defaults: {
                            // FRAMEWORK DEFAULTS
                            tagName: "div",
                            draggable: true,
                            droppable: false,
                            classes: ["cs-gjs-video"],
                            content: '<iframe class="gjs-no-pointer" src="https://www.youtube.com/embed/mxD7QEt85ms" frameborder="0" allow="autoplay; fullscreen" scrolling="no"></iframe>' +
                                '<style>' +
                                '.cs-gjs-video {' +
                                    'position: relative;' +
                                    'margin: 5px 0px;' +
                                '}' +
                                '.cs-gjs-video:before {' +
                                    'content: "";' +
                                    'display: block;' +
                                    'padding-top: 56.25%;' +
                                '}' +
                                '.cs-gjs-video iframe {' +
                                    'width: 100%;' +
                                    'height: 100%;' +
                                    'position: absolute;' +
                                    'top: 0px;' +
                                    'left: 0px;' +
                                '}' +
                                '</style>',
                            editable: true,

                            // CUSTOM DEFAULTS
                            provider: "youtube",
                            videoId: "mxD7QEt85ms",
                            videoWidth: 560,
                            videoHeight: 315,
                            youtubeUrl: "https://www.youtube.com/embed/",
                            youtubeDefaultId: "mxD7QEt85ms",
                            vimeoUrl: "https://player.vimeo.com/video/",
                            vimeoDefaultId: "22439234",
                            myvrspotUrl: "https://live.myvrspot.com/iframe",
                            myvrspotDefaultId: "vff46db9afa6d",
                            iframeDefaultId: 'https://www.youtube.com/embed/mxD7QEt85ms',
                            loop: 0,
                            autoplay: 0,
                            controls: 1,
                            color: "",
                            rel: 1,
                            modestbranding: 0,
                            title: 1,
                            portrait: 1,
                            byline: 1
                        },

                        // FRAMEWORK METHOD
                        "init": function(properties, options) {

                            // SET TRAITS
                            var traits = this.getTraits();
                            this.set("traits", traits);

                            // MAKE COMPONENTS PANEL ACTIVE
                            if (_this.GrapesEditor.Panels !== undefined) {
                                _this.GrapesEditor.Panels.getButton("views", "open-tm").set("active", true);
                            }

                            // EVENT LISTENER FOR TRAIT CHANGES 
                            this.listenTo(this, "change:provider", this.updateTraits);
                            this.listenTo(this, "change:videoId change:provider change:videoWidth change:videoHeight change:autoplay change:loop change:controls change:rel change:modestbranding change:color change:portrait change:title change:byline", this.updateModel);
                        },

                        // CUSTOM METHOD TO UPDATE THE COMPONENT MODEL - https://grapesjs.com/docs/modules/Components.html#define-custom-component-type
                        "updateModel": function() {
                            this.updateSrc();

                            var attributes = this.attributes;
                            var modelComponent = _this.GrapesEditor.getSelected();

                            modelComponent.addAttributes({
                                "data-cs-gjs-id": modelComponent.ccid
                            });
                            modelComponent.set("content", '' +
                                '<iframe class="gjs-no-pointer" src="' + attributes.src + '" frameborder="0" allow="autoplay; fullscreen" scrolling="no"></iframe>' +
                                '<style>' +
			           '[data-cs-gjs-id="' + modelComponent.ccid + '"] {' +
			           'position: relative;' +
				   'margin: 5px 0px;' +
                                '}' +
                                '[data-cs-gjs-id="' + modelComponent.ccid + '"]:before {' +
			           'content: "";' +
				   'display: block;' +
				   'padding-top: ' + ((attributes.videoHeight / attributes.videoWidth) * 100) + '%;' +
                                '}' +
                                '[data-cs-gjs-id="' + modelComponent.ccid + '"] iframe {' +
				   'width: 100%;' +
				   'height: 100%;' +
				   'position: absolute;' +
				   'top: 0px;' +
				   'left: 0px;' +
                                '}' +
                                '</style>' +
                            '');
                        },

                        // CUSTOM METHOD TO RETURN PROVIDER DROPDOWN TRAIT
                        "getProviderTrait": function() {
                            return {
                                type: "select",
                                label: "Provider",
                                name: "provider",
                                changeProp: 1,
                                options: [{
                                        value: "youtube",
                                        name: "YouTube"
                                    },
                                    {
                                        value: "vimeo",
                                        name: "Vimeo"
                                    },
                                    {
                                        value: "myvrspot",
                                        name: "MyVRSpot"
                                    },
                                    {
                                        value: "iframe",
                                        name: "Custom iframe"
                                    }
                                ],
                            };
                        },

                        // CUSTOM METHOD TO RETURN YOUTUBE TRAITS
                        "getYouTubeTraits": function() {
                            return [
                                this.getProviderTrait(),
                                {
                                    type: "text",
                                    label: "Video ID",
                                    name: "videoId",
                                    placeholder: "eg. mxD7QEt85ms",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Width",
                                    name: "videoWidth",
                                    placeholder: "eg. 560",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Height",
                                    name: "videoHeight",
                                    placeholder: "eg. 315",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Autoplay",
                                    name: "autoplay",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Loop",
                                    name: "loop",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Controls",
                                    name: "controls",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Related",
                                    name: "rel",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Modest Branding",
                                    name: "modestbranding",
                                    changeProp: 1
                                }
                            ];
                        },

                        // CUSTOM METHOD TO RETURN VIMEO TRAITS
                        "getVimeoTraits": function() {
                            return [
                                this.getProviderTrait(),
                                {
                                    type: "text",
                                    label: "Video ID",
                                    name: "videoId",
                                    placeholder: "eg. 22439234",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Width",
                                    name: "videoWidth",
                                    placeholder: "eg. 560",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Height",
                                    name: "videoHeight",
                                    placeholder: "eg. 315",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Color",
                                    name: "color",
                                    placeholder: "eg. #00adef",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Autoplay",
                                    name: "autoplay",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Loop",
                                    name: "loop",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Portrait",
                                    name: "portrait",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Title",
                                    name: "title",
                                    changeProp: 1
                                },
                                {
                                    type: "checkbox",
                                    label: "Byline",
                                    name: "byline",
                                    changeProp: 1
                                }
                            ];
                        },

                        // CUSTOM METHOD TO RETURN MYVRSPOT TRAITS
                        "getMyVRSpotTraits": function() {
                            return [
                                this.getProviderTrait(),
                                {
                                    type: "text",
                                    label: "Video ID",
                                    name: "videoId",
                                    placeholder: "eg. vff46db9afa6d",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Width",
                                    name: "videoWidth",
                                    placeholder: "eg. 560",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Height",
                                    name: "videoHeight",
                                    placeholder: "eg. 315",
                                    changeProp: 1
                                }
                            ];
                        },

                        // CUSTOM METHOD TO RETURN CUSTOM IFRAME TRAITS
                        "getIframeTraits": function() {
                            return [
                                this.getProviderTrait(),
                                {
                                    type: "text",
                                    label: "iframe Src Url",
                                    name: "videoId",
                                    placeholder: 'eg. https://player.vimeo.com/video/22439234?autoplay=1&loop=1&title=0&byline=0&portrait=0',
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Width",
                                    name: "videoWidth",
                                    placeholder: "eg. 560",
                                    changeProp: 1
                                },
                                {
                                    type: "text",
                                    label: "Video Height",
                                    name: "videoHeight",
                                    placeholder: "eg. 315",
                                    changeProp: 1
                                }
                            ];
                        },

                        // CUSTOM HELPER METHOD TO GET THE TRAITS FOR THE SELECTED PROVIDER
                        "getTraits": function() {
                            var provider = this.get("provider");
                            var traits = "";

                            switch (provider) {

                                case "youtube":
                                    traits = this.getYouTubeTraits();
                                    break;

                                case "vimeo":
                                    traits = this.getVimeoTraits();
                                    break;

                                case "myvrspot":
                                    traits = this.getMyVRSpotTraits();
                                    break;

                                case "iframe":
                                    traits = this.getIframeTraits();
                                    break;

                                default:
                                    traits = this.getYouTubeTraits();
                                    break;
                            }

                            return traits;
                        },

                        // CUSTOM METHOD TO UPDATE TRAITS ON PROVIDER DROPDOWN CHANGE
                        "updateTraits": function() {

                            this.loadTraits(this.getTraits());

                            var provider = this.get("provider");

                            switch (provider) {

                                case "youtube":
                                    traits = this.getYouTubeTraits();
                                    break;

                                case "vimeo":
                                    traits = this.getVimeoTraits();
                                    break;

                                case "myvrspot":
                                    traits = this.getMyVRSpotTraits();
                                    break;

                                case "iframe":
                                    traits = this.getIframeTraits();
                                    break;

                                default:
                                    traits = this.getYouTubeTraits();
                                    break;
                            }

                            return traits;
                        },

                        // CUSTOM HELPER METHOD TO UPDATE THE IFRAME SRC WHEN TRAITS CHANGE
                        "updateSrc": function() {
                            var provider = this.get("provider");

                            switch (provider) {

                                case "youtube":
                                    this.set("src", this.getYouTubeSrc());
                                    break;

                                case "vimeo":
                                    this.set("src", this.getVimeoSrc());
                                    break;

                                case "myvrspot":
                                    this.set("src", this.getMyVRSpotSrc());
                                    break;

                                case "iframe":
                                    this.set("src", this.getIframeSrc());
                                    break;
                            }
                        },

                        // CUSTOM METHOD TO BUILD YOUTUBE IFRAME SRC
                        "getYouTubeSrc": function() {
                            var videoId = this.get("videoId");
                            var url = this.get("youtubeUrl");

                            url += videoId + "?";
                            url += this.get("autoplay") ? "&autoplay=1" : "";
                            url += this.get("controls") ? "" : "&controls=0&showinfo=0";
                            url += this.get("loop") ? "&loop=1&playlist=" + videoId : "";
                            url += this.get("rel") ? "" : "&rel=0";
                            url += this.get("modestbranding") ? "&modestbranding=1" : "";

                            return url;
                        },

                        // CUSTOM METHOD TO BUILD VIMEO IFRAME SRC
                        "getVimeoSrc": function() {
                            var videoId = this.get("videoId");
                            var url = this.get("vimeoUrl");

                            url += videoId + "?";
                            url += this.get("autoplay") ? "&autoplay=1" : "";
                            url += this.get("loop") ? "&loop=1" : "";
                            url += this.get("portrait") ? "&portrait=0" : "";
                            url += this.get("title") ? "&title=0" : "";
                            url += this.get("byline") ? "" : "&byline=0";
                            url += this.get("color") ? "&color=" + this.get("color") : "";

                            return url;
                        },

                        // CUSTOM METHOD TO BUILD MYVRSPOT IFRAME SRC
                        "getMyVRSpotSrc": function() {
                            var videoId = this.get("videoId");
                            var url = this.get("myvrspotUrl");

                            url += "?v=" + videoId;

                            return url;
                        },

                        // CUSTOM METHOD TO BUILD CUSTOM IFRAME SRC
                        "getIframeSrc": function() {
                            var videoId = this.get("videoId");

                            return videoId;
                        }

                    }

                });
            }

            // RETURN AN ARRAY OF DEFINED COMPONENTS/PLUGINS
            return [customVideo];
        },

        // ...

        "ExtendGrapesBlocks": function() {
            var _this = this;

            // VIDEO BLOCK
            this.GrapesBlocks.add("csVideo", {
                label: 'Video',
                content: {
                    type: "csVideo",
                    name: "Video",
                    attributes: {
                        "data-element-type": "csVideo"
                    }
                },
                category: 'Multimedia',
                attributes: {
                    "class": "fa fa-youtube-play"
                },
                select: true
            });

        },

        // ...

    };
})();

Related Questions and Answers

Continue research with similar issue discussions.

Paid Plugins That Match This Issue

Curated by issue keywords and label relevance to help you ship faster.

View all plugins

Loading paid plugin recommendations...

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.