GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.37k stars 4.05k forks source link

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

Closed brentonkelly1982 closed 5 years ago

brentonkelly1982 commented 5 years ago

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

brentonkelly1982 commented 5 years ago

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
            });

        },

        // ...

    };
})();