LWC1079 Ожидается, что тег root будет шаблоном, найдено мета - PullRequest
2 голосов
/ 05 августа 2020

Нашел этот код в Интернете как открытый исходный код, и я пытался преобразовать его в LW C. Код находится в HTML, CSS и JS. Я работаю над этим в визуальной студии и использую пакет расширений salesforce, который не принимает HTML, ему нужны теги, но я никогда раньше не использовал теги шаблонов. Это также дает мне ошибку: метатег не разрешен. Я понятия не имею, что это за ошибка. Кто-нибудь может помочь? Ошибка в строке 3

<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />

        <title>Maths Game</title>

        <link rel="stylesheet" href="mathGame.css" />
    </head>

    <body>
        <main>
            <div id="container">
                <p id="message" class="structure-elements"></p>
                <aside id="score" class="structure-elements">Score: <span>00</span></aside>

                <div id="calculation">
                    <section id="question" class="structure-elements"></section>
                    <p id="instruction" class="structure-elements">Click on the correct answer</p>
                    <ul id="choices" class="structure-elements">
                        <li></li>
                        <li></li>
                        <li></li>
                        <li></li>
                    </ul>
                </div>

                <button id="start-reset" class="structure-elements">Start Game</button>

                <aside id="time-remaining" class="structure-elements">Time remaining: <span>60</span> sec</aside>
            </div>

            <div id="game-over" class="structure-elements">
                <p>Game over!</p>
                <p>Your score is <span>00</span>.</p>
            </div>
        </main>

        <script src="mathGame.js"></script>
    </body>
</html>

** Это Javascript часть кода

var Counter = {
    PlayingState: null,
    IsStoped: true,
    Score: 0,
    TimeRemaining: 0,
    FirstNumber: 0,
    SecondNumber: 0,
    CorrectAnswer: 0,
    CorrectPosition: 0,
    WrongAnswer: 0,
    AddContentToElement: function(selector, content)
    {
        document.querySelector(selector).innerHTML = content;
    },
    ChangeStyle: function(selector, property, value)
    {
        document.querySelector(selector).setAttribute(property, value);
    },
    Initialize: function(timeRemaining)
    {
        this.TimeRemaining = timeRemaining;
    },
    GenerateRandomNumber: function(multiplier)
    {
        return Math.round( Math.random() * multiplier ) + 1;
    },
    Refresh: function(selector, data)
    {
        document.querySelector(selector).innerText = (data < 10 ? "0" : "") + data;
    },
    LoopThroughElements: function()
    {
        var answers = [this.CorrectAnswer];

        for (var index = 1; index < 5; index++)
        {
            this.ChangeStyle("ul#choices > li:nth-of-type(" + index + ")", "style", "height:auto;");

            if (index !== this.CorrectPosition)
            {
                do
                {
                    this.WrongAnswer = this.GenerateRandomNumber(9) * this.GenerateRandomNumber(9);
                } while ( answers.indexOf(this.WrongAnswer) > -1 );

                this.AddContentToElement( "ul#choices > li:nth-of-type(" + index + ")", this.WrongAnswer );
                answers.push(this.WrongAnswer);
            }
        }
    },
    Launch: function()
    {
        this.IsStoped = false;
        this.Action();
        this.ChangeStyle("aside#time-remaining", "style", "visibility:visible;");
        this.ChangeStyle("#game-over", "style", "display:none;");
        this.ChangeStyle("ul#choices", "style", "pointer-events:initial; opacity:1;");
        this.ChangeStyle("button#start-reset", "style", "visibility:hidden;");
        this.AddContentToElement("button#start-reset", "Reset Game");
        this.Refresh("aside#time-remaining > span", this.TimeRemaining);
        this.GenerateQuestionAndAnswers();
    },
    GenerateQuestionAndAnswers: function()
    {
        this.FirstNumber = this.GenerateRandomNumber(9);
        this.SecondNumber = this.GenerateRandomNumber(9);
        this.CorrectAnswer = this.FirstNumber * this.SecondNumber;
        this.CorrectPosition = this.GenerateRandomNumber(3);
        this.ChangeStyle("section#question", "style", "height:auto;");
        this.AddContentToElement("section#question", this.FirstNumber + "x" + this.SecondNumber);
        this.AddContentToElement( "ul#choices > li:nth-of-type(" + this.CorrectPosition + ")", this.CorrectAnswer );
        this.LoopThroughElements();
    },
    Action: function()
    {
        Counter.PlayingState = setInterval( function()
        {
            Counter.TimeRemaining--;
            
            if (Counter.TimeRemaining <= 50)
            {
                Counter.ChangeStyle("button#start-reset", "style", "visibility:visible;");
            }

            if (Counter.TimeRemaining < 1)
            {
                Counter.Stop();
            }
            else
            {
                Counter.Refresh("aside#time-remaining > span", Counter.TimeRemaining);
            }
        }, 1000 );
    },
    EventListener: function(event)
    {
        if ( Number(event.currentTarget.innerText) === Number(Counter.CorrectAnswer) )
        {
            Counter.Score++;
            Counter.Refresh("aside#score > span", Counter.Score);
            Counter.GenerateQuestionAndAnswers();
            Counter.ChangeStyle("p#message", "style", "visibility:visible; background-color:#23A230;");
            Counter.AddContentToElement("p#message", "Correct");
        }
        else
        {
            if (Counter.Score >= 1)
            {
                Counter.Score -= 0.5;
                Counter.Refresh("aside#score > span", Counter.Score);
            }
            
            Counter.ChangeStyle("p#message", "style", "visibility:visible; background-color:#DE401A;");
            Counter.AddContentToElement("p#message", "Try again");
        }

        setTimeout( function()
        {
            Counter.ChangeStyle("p#message", "style", "visibility:hidden;");
        }, 1000 );
    },
    CheckClickOnRightAnswer: function()
    {
        for (var index = 1; index < 5; index++)
        {
            document.querySelector("ul#choices > li:nth-of-type(" + index + ")").removeEventListener("click", this.EventListener, false);
            document.querySelector("ul#choices > li:nth-of-type(" + index + ")").addEventListener("click", this.EventListener);
        }
    },
    Stop: function()
    {
        this.IsStoped = true;
        clearInterval(this.PlayingState);
        this.ChangeStyle("ul#choices", "style", "pointer-events:none; opacity:0.4;");
        this.ChangeStyle("aside#time-remaining", "style", "visibility:hidden;");
        this.ChangeStyle("div#game-over", "style", "display:block;");
        this.AddContentToElement("button#start-reset", "Start Game");
        this.AddContentToElement( "div#game-over > p:last-of-type > span", (this.Score !== "00" && this.Score < 10 ? "0" : "") + this.Score );
        this.AddContentToElement("aside#score > span", this.Score = "00");
    }
};


/*************************************************************************************************/
/* ************************************** CODE PRINCIPAL *************************************** */
/*************************************************************************************************/
document.addEventListener('DOMContentLoaded', function()
{
    document.getElementById("start-reset").addEventListener("click", function()
    {
        Counter.Initialize(60);
        Counter.IsStoped ? Counter.Launch() : Counter.Stop();
        Counter.CheckClickOnRightAnswer();
    });
});

1 Ответ

0 голосов
/ 06 августа 2020

С LW C вы не пишете полностраничные приложения, нет <html>, <head>, <body>. Вы пишете небольшие повторно используемые компоненты с тегом <template>, и их можно размещать на разных страницах. И в большинстве случаев вы не манипулируете HTML напрямую. Вы устанавливаете значения для JS переменных, и фреймворк выполняет рендеринг соответствующих частей. Упрощает разделение презентации и logi c и делает logi c тестируемым (да, могут быть модульные тесты для LW C).

Эти самостоятельные тренинги могут быть полезны: https://trailhead.salesforce.com/content/learn/modules/modern-javascript-development, https://trailhead.salesforce.com/en/content/learn/modules/lightning-web-components-basics

Итак ... Если вы просто хотите заставить его работать в Salesforce, вы всегда можете сделать из него страницу Visualforce, это ближе всего к полностраничному приложению. Или загрузите свой материал как ресурс stati c, а затем используйте компонент ligthning:container Aura, чтобы загрузить его. Он будет загружаться как iframe, но стили не будут sh, для переноса приложения требуется минимум JS знаний, иногда это так. Немного более «за» было бы попытаться переписать его как можно реже. Эта штука сильно манипулирует необработанным HTML, и это не совсем способ LW C, но это возможно с lwc:dom="manual"

Если вы любите приключения - перепишите его на LW C. Это далеко от совершенства и не является полным переписыванием, но должно дать вам некоторые идеи.

html

<template>
    <div>
        <lightning-layout multiple-rows="true">
            <lightning-layout-item size="6"><span class={messageStyle}>{message}</span></lightning-layout-item>
            <lightning-layout-item size="6">
                Score: 
                <lightning-formatted-number value={score} minimum-integer-digits="2"></lightning-formatted-number>
            </lightning-layout-item>

            <template if:false={isStopped}>
                <lightning-layout-item size="12"><span class="question">{question}</span></lightning-layout-item>
                <lightning-layout-item size="12">Click on the correct answer</lightning-layout-item>

                <template for:each={answers} for:item="a">
                    <lightning-layout-item key={a.value} size="3">
                        <lightning-button label={a.value} value={a.value} variant="neutral" onclick={handleAnswerClick}></lightning-button>
                    </lightning-layout-item>
                </template>
            </template>

            <lightning-layout-item size="6">
                <template if:true={isButtonVisible}>
                    <lightning-button label={buttonLabel} variant={buttonVariant} onclick={handleButtonClick}></lightning-button>
                </template>
            </lightning-layout-item>
            <lightning-layout-item size="6">
                Time remaining: 
                <lightning-formatted-number value={timeRemaining} minimum-integer-digits="2"></lightning-formatted-number>
            </lightning-layout-item>
        </lightning-layout>
    </div>
</template>

js

import { track, LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent'

export default class Stack63257378 extends LightningElement {
    buttonLabel = 'Start Game';
    buttonVariant = 'success';
    isButtonVisible = true;

    message;
    messageStyle;

    firstNumber;
    secondNumber;
    @track answers = [];

    timeRemaining;
    score;
    isStopped = true;

    handleButtonClick(event){
        this.isStopped ? this.launch() : this.stop();
    }

    launch(){
        this.timeRemaining = 15; // 60
        this.isStopped = this.isButtonVisible = false;
        this.score = 0;
        let interval = setInterval(function (){
            --this.timeRemaining;
            if(this.timeRemaining <= 10){ // 50
                this.isButtonVisible = true;
                this.buttonLabel = 'Stop Game';
                this.buttonVariant = 'destructive';
            }
            if(this.timeRemaining < 1){
                clearInterval(interval);
                this.stop();
            }
        }.bind(this),1000);

        this.generateQuestion();
    }

    handleAnswerClick(event){
        if(this.correctAnswer === event.target.value){
            ++this.score;
            this.generateQuestion();
            this.message = 'Correct';
            this.messageStyle = 'good';
        } else {
            if(this.score >= 1) {
                this.score -= 0.5;
            }
            this.message = 'Try again';
            this.messageStyle = 'bad';
        }
    }

    stop(){
        this.answers = [];
        this.isStopped = true;
        this.buttonLabel = 'Start Game';
        this.buttonVariant = 'success';
        this.message = this.messageStyle = null;
        
        const event = new ShowToastEvent({
            title: 'Game over!',
            message: `Your score is ${this.score}`,
        });
        this.dispatchEvent(event);
    }

    generateQuestion(){
        this.firstNumber = this.getRandomNumber(9);
        this.secondNumber = this.getRandomNumber(9);
        this.correctAnswer = this.firstNumber * this.secondNumber;
        this.answers = [];
        let correctPosition = this.getRandomNumber(3);
        for(let i = 0; i < 4; ++i){
            let obj = {"i" : i, "value" : i === correctPosition ? this.correctAnswer : this.getRandomNumber(9) * this.getRandomNumber(9)};
            this.answers.push(obj);
        }
    }

    getRandomNumber(range){
        return Math.round( Math.random() * range ) + 1
    }

    get question(){
        return this.isStopped ? '' : `${this.firstNumber} x ${this.secondNumber}`;
    }
}

css

.good {
    background-color:#23A230;
}
.bad {
    background-color:#DE401A;
}
.question {
    font-size: 24px;
}
button {
    width: 100%;
}

"meta- xml" (решает, где вы можете встроить этот компонент в SF)

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Math game</masterLabel>
    <description>https://stackoverflow.com/q/63257378/313628</description>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>
...