Ranking the images of ImageCollection in Google Earth Engine

I am writing a code in Google Earth Engine (GEE) where I get the monthly precipitation from the NASA/GPM_L3/IMERG_MONTHLY_V06 between 2000 and 2021. Then, I build an image collection of 12 images which contains the highest observed value of each month for each pixel.

What I am trying to do is to create a new image collection which will contain the rank index of each image’s value compared to the values of the other images.

So for example if the image collection gives this result:
Jan: 0.25,
Feb: 0.23
Mar: 0.45,

Dec: 0.1

to get a new image collection which will contain the values:
Jan: 2,
Feb: 3,
Mar: 1,

Dec: 12

What I have done until now is this but I am stack in ranking:

var startDate = '2000-06-01';
var endDate = '2021-09-01';

var im_col = 'NASA/GPM_L3/IMERG_MONTHLY_V06';
var band = 'precipitation';

var dataset = ee.ImageCollection(im_col)
                .filterDate(startDate, endDate)
                .select(band)
                .map(function(image) {
                  return image.set('system:time_start', image.get('system:time_start'));
                });

var startYear = ee.Date(startDate).get('year');
var endYear = ee.Date(endDate).get('year');
var totalYears = endYear.subtract(startYear);

var addAttributes = function(image) {
  var year = ee.Date(image.get('system:time_start')).get('year');
  return image.set({
    'year': year,
    'n': totalYears
  });
};

var datasetWithAttributes = dataset.map(addAttributes);
var yearRange = ee.List.sequence(startYear, endYear);


var monthlyMaxCollection = ee.Dictionary({});

for (var month = 1; month <= 12; month++) {
  var maxImage = dataset
    .filter(ee.Filter.calendarRange(month, month, 'month'))
    .max();
  
  monthlyMaxCollection = monthlyMaxCollection.set(ee.Number(month).format(), maxImage);
}

var monthlyMaxList = monthlyMaxCollection.values();

var monthlyMaxListCol = ee.ImageCollection.fromImages(monthlyMaxList);

print(monthlyMaxListCol);

Does anyone have any idea?

gRPC fails to serialize object with arbitrary properties

The context is that I am working on a configurable mock gRPC server which will run in a Docker instance and serve as a mock instance of backend services. I initialize each instance with a proto file and a configuration that tells it what functions to listen to and what data to respond with.

I am using @grpc/grpc-js and @grpc/proto-loader. I’ve already set it up to return arbitrary scalar values as well as arrays and objects with a known schema. Everything was working until I got to the part where I wanted to generate arbitrary objects (i.e. maps). For some reason, the gRPC server doesn’t recognize the object that I pass to it as an object and complains.

Error as reported from the client:

Error: 13 INTERNAL: Error serializing response: .Result.testObject: object expected
    at callErrorFromStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
    at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
    at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
    at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
    at ***/test/grpc/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
    at ServiceClientImpl.makeUnaryRequest (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client.js:160:32)
    at ServiceClientImpl.<anonymous> (***/test/grpc/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19)
    at file://***/test/grpc/index.mjs:14:10
    at file://***/test/grpc/index.mjs:18:3
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
    at async loadESM (node:internal/process/esm_loader:34:7)
    at async handleMainPromise (node:internal/modules/run_main:66:12) {
  code: 13,
  details: 'Error serializing response: .Result.testObject: object expected',
  metadata: Metadata {
    internalRepr: Map(2) { 'content-type' => [Array], 'date' => [Array] },
    options: {}
  }
}

This is my proto file (identical for both client and server):

syntax = "proto3";

service TestApi {
  rpc Execute(None) returns (Result);
}

message None {}

message Result {
  map<string, Value> testObject = 1;
}

message Value {
  oneof kind {
    bool boolValue = 1;
    int32 intValue = 2;
    double doubleValue = 3;
    string stringValue = 4;
  }
}

The server handler function:

export function handler(call, callback) {
  try {
    const response = generateValues('$', outputs); // Generate random response
    console.log(response);
    callback(null, response);
  } catch (e) {
    console.error(e);
    callback(e, null);
  }
}

The console.error never fires. An example of the console.log output:

{
  testObject: {
    VmZWD: false,
    VXiRwi: 28.087891844342792,
    cjCTDFD: false,
    inYZS: 6,
    AYKahk: ' xsyflf szsnwb ush',
    pgUT: 23.19160119367565
  }
}

Things I’ve tried which resulted in the same error:

  • Changing Value to google.protobuf.Any (with associated import)
  • Setting the json, object, and oneOf options of the proto-loader load function to true or false.

Note that changing map to another structure isn’t an option as the production protos that this image is meant to be able to mock already use map in their definitions.

Trying to submit two input fields in an array, not working

Here’s my javascript function, that populates input fields for child ages, depending on the number of children input field. Like if the number is children field has a value of 2, then two extra fields are pupolated for the ages. The problem is the only the last on gets submitted:

function handleChildrenNoChange() {
    const childrenNo = document.getElementById('childrenNo').value;
    const childAgeFieldsContainer = document.getElementById('childAgeFields');

    // Clear existing child age fields
    childAgeFieldsContainer.innerHTML = '';

    // Add label for child ages
    const label = document.createElement('label');
    label.innerText = 'Gyerekek életkora:';
    childAgeFieldsContainer.appendChild(label);

    // Create FormData object to collect form data
    const formData = new FormData();

    // Generate child age fields based on the selected number of children
    const childAges = [];

    for (let i = 1; i <= childrenNo; i++) {
        const ageField = document.createElement('input');
        ageField.type = 'number';
        ageField.placeholder = `Gyermek ${i} életkora`;
        ageField.name = 'chd_age'; // Use the same name for all child age fields
        ageField.required = true;

        // Append age field to the container
        childAgeFieldsContainer.appendChild(ageField);

        // Add event listener to capture the value when the user enters it
        ageField.addEventListener('change', function () {
            // Update the 'chd_age' field in the FormData object
            childAges[i - 1] = ageField.value;
            formData.set('chd_age', childAges.join(','));

            // Debugging: Log the age values after the user enters them
            console.log(`Child ${i} age: ${ageField.value}`);
            console.log('chd_age:', formData.get('chd_age'));
        });
    }
}

Then here’s the json output where there is only the last one shows:

{"client_name_last":"Teszt","client_name_first":"Elek","client_mail":"[email protected]","client_telephone":"6200532947","check_in":"2024-05-01","check_out":"2024-05-02","username":"Pannon","password":"wwere4436jv9RFMPs5vkztx","property_id":"701043","client_lang":"","adults_no":"2","chd_no":"2","chd_age":"21","client_comment":""}

And this is what the chd_age part should look like: “chd_age”:”13,21″

Any help would be much appreciated.
Thanks.

AlpineJS passing number coming up as NaN

I have a link in alpineJS

$count_posts = get_posts( $args );
$total_posts_child = count($count_posts);


  ?>
          <li :class="category == <?php echo $category->term_id; ?> ? 'child' : ''" ;>
                     <a class="child post_no" data-value="<?php echo $total_posts_child ?>"  @click="total += parseInt($el.dataset.value);  filterPosts(<?= $category->term_id; ?>)"><?= esc_html( $category->name ); echo " ". "(" .$total_posts_child . ")"; ?></a>
                </li> <?php
                

I am trying to populate my JS function total with the post count.

Alpine.data("filterPosts", (adminURL) => ({
    posts: "",
    limit: 6,
    category: 0,
    post_type_js: post_id,
    showDefault: true,
    showFiltered: false,
    offset: 0,
    total: 0, // want to get the value from the varible he
    
    
    filterPosts(id) {
        this.showDefault = false;
        this.showFiltered = true;
        this.category = id;
        this.offset = 0; // <-- reset offset to zero
        this.fetchPosts();
        this.total = 0;
    },

When i check the output i get NaN, Anyone know how to fix this ?

Make the Reviews Tab Active on Elementor Product Data Tabs

we are using Elementor Pro and theme, and trying to style the single product page template.

The thing is, Elementor only has a standalone Product Rating element, that shows the overall rating.

There is nothing that shows the actual reviews, other than the “Product Data Tabs”, where the Reviews are held in the 3rd tab.

We don’t want the other tabs as we’ve already added that information individually, so I hid them with CSS, but the problem is, on page load, the “Description” tab is still active, instead of the Reviews tab.

Example page where issue occurs: https://www.maxwellmelia.co.uk/shop/in-person-courses/hair-extensions-complete-training/

Is there any custom JS I can add to the child theme so that by default on product pages the Reviews tab is active rather than the description tab?

You can see in the screenshot that the description tab is still acive even though it’s hidden.

I’ve searched articles on this and other platforms, and contacted Elementor support directly, and can’t get anywhere with it.

I’m sure it’s very simple for any one with any experience in PHP/JS but I’m clueless…

Thank you so much in advance!

Sophie

(https://i.stack.imgur.com/Nww9Q.png)](https://i.stack.imgur.com/Nww9Q.png)

I have tried adding a few different different codes into the child theme functions.php based on what I’ve found online and code snippets I’ve used for other purposes but with no joy.

This was the closest I got, from this URL, which was to simply manually add dynamic reviews rather than trying to edit the Product Tabs Element, but it displayed other unrelated and private info (such as SUMO payment plans), and was formatted horribly:

https://www.businessbloomer.com/woocommerce-display-product-reviews-custom-page-shortcode/#:~:text=PHP%20Snippet%3A%20WooCommerce%20Product%20Reviews%20Shortcode&text=php%2C%20simply%20use%20shortcode%20%5Bproduct_reviews,want%20to%20output%20customer%20reviews


/**
 * @snippet       WooCommerce Product Reviews Shortcode
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @testedwith    WooCommerce 3.9
 * @community     https://businessbloomer.com/club/
 */
 
add_shortcode( 'product_reviews', 'bbloomer_product_reviews_shortcode' );
 
function bbloomer_product_reviews_shortcode( $atts ) {
    
   if ( empty( $atts ) ) return '';
 
   if ( ! isset( $atts['id'] ) ) return '';
       
   $comments = get_comments( 'post_id=' . $atts['id'] );
    
   if ( ! $comments ) return '';
    
   $html .= '<div class="woocommerce-tabs"><div id="reviews"><ol class="commentlist">';
    
   foreach ( $comments as $comment ) { 
      $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) );
      $html .= '<li class="review">';
      $html .= get_avatar( $comment, '60' );
      $html .= '<div class="comment-text">';
      if ( $rating ) $html .= wc_get_rating_html( $rating );
      $html .= '<p class="meta"><strong class="woocommerce-review__author">';
      $html .= get_comment_author( $comment );
      $html .= '</strong></p>';
      $html .= '<div class="description">';
      $html .= $comment->comment_content;
      $html .= '</div></div>';
      $html .= '</li>';
   }
    
   $html .= '</ol></div></div>';
    
   return $html;
}
  • I then used dynamic field to add the post ID into this shortcode – [[product_reviews id=”123″]] — but I put a dynamic field where 123 is.

I also tried along the lines of (please don’t laugh at me for this!):

$(document).ready(function(){

    if ($('#hdnActiveTab').length > 0) {
        var tabID = "#" + $("#hdnActiveTab").val();
        $(tabID).addClass("active");
    }              

});
  • and on the actual template product page I added custom code:
<input id="hdnActiveTab" type="hidden" val="reviews_tab" />


This looks very close to what I need if removing the tabs with JS would automatically make the remaining tab active:

https://stackoverflow.com/questions/58587944/remove-tabs-from-product-data-in-woocommerce

My edited version:

function product ()
{
remove_tab($tabs){
unset($tabs['tab-title-description']); // it is to remove description tab
unset($tabs['tab-title-additional_information']); // it is to remove additional info tab
return($tabs);
}
add_filter('woocommerce_product_data_tabs', 'remove_tab', 10, 1);
}

But I assume I need something more in there to make it know that it’s for woocommerce product pages?

Why does my WebGL program won’t compile using link?

const canvas = document.querySelector('#canvas');
const gl = canvas.getContext('webgl')
const vertexData = [
    0,1,0,
    1,-1,0
    -1,-1,0,
];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader,  `
    attribute vec3 position;
    void main() {
        gl_Position = vec4(position,1);
    }
`)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader,  `
    void main() {
        gl_FragColor = vec4(1,0,0,1);
    }
`)
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)


gl.linkProgram(program);

const positionLocation = gl.getAttribLocation(program, `position`);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);

I tried this tutorial https://www.youtube.com/watch?v=kju9OgYrUmU&list=PL2935W76vRNHFpPUuqmLoGCzwx_8eq5yK&index=3 and i expected that it works as expected but I have those errors

WebGL: INVALID_OPERATION: getAttribLocation: program not linked
(anonymous) @ main.js:33

main.js:37 WebGL: INVALID_OPERATION: useProgram: program not valid
(anonymous) @ main.js:37

main.js:38 WebGL: INVALID_OPERATION: drawArrays: no valid shader program in use

How to call an instance method by passing its name to a base class instance method, with correctly-typed arguments?

We’ve defined a class with several instance methods. We want to define an exec instance method that accepts the name and arguments of another instance method, and then calls it.

However, we’ve run into an issue with the typing of the other instance method’s arguments. Here’s a snippet that demonstrates the error:

class MyClass { 
    constructor() {
        this.exec(`sayMessage`, `Hello`); // Error: Argument of type '["Hello"]' is not assignable to parameter of type 'Parameters<this["sayMessage"]>'.
    }

    exec<
        Self extends Record<MethodKey, (...args: any) => void>,
        MethodKey extends keyof Self,
        Method extends Self[MethodKey],
    >(
        this: Self,
        methodKey: MethodKey,
        ...args: Parameters<Method>
    ) {}

    sayMessage(message: string) {
        console.log(message);
    }
}

TS Playground link

Why doesn’t this work, and how do we correctly type the exec method so that it accepts the other method’s arguments?

Javascript doesn’t fetch progress data from python

I’ve been trying to implement a progress bar for a user to track how far along my code is. I’ve been doing it like this:

global_progress = 0.0

def update_progress(progress):
    global global_progress
    global_progress = progress

@app.route('/progress')
def progress():
    return jsonify(progress=global_progress)

Then I’d place update_progress(0.25) but appropriately modified at various points of my other function. And the javascript is supposed to fetch the progress data to update the bar.

function updateProgressBar(progress) {
    var progressBar = document.getElementById('progress-bar');
    
    progressBar.style.setProperty('--width', progress * 100 + '%');
}

function checkProgress() {
    // Fetch progress from the server
    fetch('http://127.0.0.1:5000/progress')
      .then(response => response.json())
      .then(data => {
        updateProgressBar(data.progress);
        
        setTimeout(checkProgress, 1000);
      })
      .catch(error => console.error('Error fetching progress:', error));
}

checkProgress();

But the bar doesn’t change when pass an image into the code. I’m wondering what I’m doing wrong?

Last time I was wondering if the problem was that I didn’t set up my Flask folder properly, but I’ve checked it since so now I’m not sure. I’ve made sure that the progress data does indeed get passed on and changes value, so I think that problem is that my javascript code can’t fetch it?

How to have submit and field validation at the same time in formik?

i want to validate form on submit with formik, while also have field level validation.For example, validate all fields when submitting, and validate a field on change.

 return (
    <div className='add-member'>
      <h1>Register a new member</h1>
      <Formik
        validationSchema={validationSchema}
        validateOnBlur={false}
        validateOnChange={false}
        initialValues={{ name: ''}}
        onSubmit={() => {
          console.log('something')
        }}
      >
        {({errors}) => (
          <Form>
            <div className='member-info'>
              <div className='title'><p>Info</p></div>
        
              <div className='input' id='name-input'>
                <label>Name</label>
                <Field name='name' placeholder={errors.name ? '*Required' : null} className={errors.name ?      'input-error' : ''} />
              </div>
      </Formik>
    </div>
  )
 

I have disabled validateOnChange in the Formik component so that submit validation works, but dont know how to validate a field on change.

trying to delete existing tabs which were dynamically added

Here i am in a situtation where i am trying to add the dynamic tabs to the existing tabs but also if that link or popup is agai opened, i am trying to make sure it does not duplicate the tabs, my current code is doing as such as of now

Here is my html and my jquery code

function loadbtns(dataAttributes) {
  $.ajax({
    url: "x.cfm?section=11",
    type: 'POST',
    data: dataAttributes,
    dataType: 'json', 
    
    success: function(response) {
      console.log(response);
      if(Object.keys(response).length > 0) {

        $('#tabs').find("li.addedDynamic").each(function() {
          $(this).remove();
        });
        var tabTitle1 = response.title1; // replace with how you access your title in data
        var tabContent1 = response.msg1 + "n" + response.label1 + "n" + response.textData1 + "n" + response.content1; // replace with how you access your content in data
        var tabTitle2 = response.title2;// replace with how you access your title in data
        var tabContent2 = response.msg2 + "n" + response.label2 + "n" + response.textData2 + "n" + response.content2; // replace with how you access your content in data

        var tabTemplate = "<li><a class='addedDynamic' href='#{href}'>#{label}</a></li>";
        var id1 = "tabs-" + ( $("#tabs li").length + 1 );
        var id2 = "tabs-" + ( $("#tabs li").length + 2 );

        var li1 = $( tabTemplate.replace( /#{href}/g, "#" + id1 ).replace( /#{label}/g, tabTitle1 ) );
        var li2 = $( tabTemplate.replace( /#{href}/g, "#" + id2 ).replace( /#{label}/g, tabTitle2 ) );

        $("#tabs").find( ".ui-tabs-nav" ).append( li1 );
        $("#tabs").append( "<div id='" + id1 + "'>" + tabContent1 + "</div>" );

        $("#tabs").find( ".ui-tabs-nav" ).append( li2 );
        $("#tabs").append( "<div id='" + id2 + "'>" + tabContent2 + "</div>" );

        $("#tabs").tabs( "refresh" );
      }
    }
  });
}

now the html code i have is:

<div id="tabs">
            <ul>
                <li id="Tabsheet"><a href="#newMgrAprvlScreen" class="tName"></a></li>
                <li id="Tabcs"><a href="#commentsSectionData" class="cName"></a></li>
            </ul>
            <div id="Screen">
                <div class="modal_datatable">
                    <table class="table2 display" id="ModalTblData">
                    </table>
                    <table id="ModalTblData2" class="table2 display">
                    </table>
                </div>
            </div>
            <div id="SectionData"></div>
        </div>

the above will stay as is and will never be changed and deleted, the new tabs will be created by the jquery code and the jquery response is ike this

{
    "textData1": "<div><textarea required minlength="5" name="adjustment1" id="adjustment1" class="textareaWidth width100" cols="50" wrap="Virtual"></textarea></div>",
    "msg1": "<div id="divIDMsg_1" style="text-align:center;display:none;"> accepted and email sent successfully!</div>",
    "textData2": "<div><textarea required minlength="5" name="adjustment2" id="adjustment2" class="textareaWidth width100" cols="50" wrap="Virtual"></textarea></div>",
    "msg2": "<div id="divIDMsg_2" style="text-align:center;display:none;">request has been sent!</div>",
    "title2": "Request",
    "title1": "Accepted",
    "content2": "<div class="pad5 padleft0"><input type="button" data-reqAdj="Request" data-accAdj="Accepted " data-emailSend="Email with this request has been sent!" data-accepted=" accepted and email sent successfully!" data-details="Please enter details." data-invalid="Error! Your input is invalid!" class="ui-button ui-corner-all ui-widget openSection2" onclick="submitRequest2(this)" data-reviewstatus="R" data-tmcardid="221" data-weekid="02/24/24" data-stype="REG" data-stime="NuMDA=" data-instance="Vf=" data-minvalue="40" data-email="ICAA=" data-empname="MOsa" data-wkend="02/24/24" data-emplyid="76" data-action="2" data-approver="NO" value="Request Adjustment" name="adjusted" id="adjusted"></div>",
    "content1": "<div class="pad5 padleft0"><input type="button" data-reqAdj="Request" data-accAdj="Accepted " data-emailSend="Email with this  request has been sent!" data-accepted=" accepted and email sent successfully!" data-details="Please enter details." data-invalid="Error! Your input is invalid!" class="ui-button ui-corner-all ui-widget openSection" onclick="submitRequest(this)" data-reviewstatus="Y" data-tmcardid="221" data-weekid="02/24/24" data-stype="REG" data-stime="NDAuMDA=" data-instance="TkE=" data-minvalue="40" data-email="RAgICAgICA=" data-empname="MOsa" data-wkend="02/24/24" data-emplyid="7151" data-action="1" data-approver="NO" value="Accepted" name="accepted" id="accepted"></div>",
    "label2": "<div class="pad5">Request</div>",
    "label1": "<div class="pad5">Accepted </div>"
}

but it keeps on adding new tabs instead of clearing existing if any, what could be wrong here

Raspberry Pi – Doorbell using server side javascript

I’m trying to create a server side doorbell that triggers from an https call. I have this running on a Raspberry Pi B+ running Raspberry Pi OS lite. I’ve tested aplay and it works, and all the files are in their appropriate places.

Here is the Javascript I’m using:

const express = require('express');
const bodyParser = require('body-parser');
const { exec } = require('child_process');

const app = express();
const port = 3000;

app.use(bodyParser.json());

app.post('/play-sound', (req, res) => {
  // Execute a command to play a sound (you can replace this with your own sound file and player command)
  exec('aplay /usr/share/sounds/alsa/doorbell.wav', (error, stdout, stderr) => {
    if (error) {
      console.error(`exec error: ${error}`);
      return res.status(500).send('Error playing sound');
    }
    console.log(`stdout: ${stdout}`);
    console.error(`stderr: ${stderr}`);
    res.send('Sound played successfully');
  });
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

and HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Play Sound</title>
</head>
<body>
  <button id="playSound">Play Sound</button>

  <script>
    const playButton = document.getElementById('playSound');

    playButton.addEventListener('click', async () => {
      try {
        const response = await fetch('https://10.8.8.109:3000/play-sound', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          }
        });

        if (response.ok) {
          alert('Sound played successfully');
        } else {
          throw new Error('Failed to play sound');
        }
      } catch (error) {
        console.error(error);
        alert('An error occurred while playing the sound');
      }
    });
  </script>
</body>
</html>

When I go to the page and press the button I get “An error occurred while playing the sound”

So the script is running, I just can’t seem to figure out why it’s not executing the command.

How to display product details on a single web page using HTML, CSS, and JavaScript

I am building a website using html, css, and js. I created an html file containing all the products and divided them into a grid system. I have many products and I want to create a function. When the user clicks Event, it opens a page containing more details about the product. Should I create a file? html for each product so that I can open a page that contains the details of this product

I tried to use AJAX when I click on a specific product that displays the product details on the same page. I stored the product details in obj js and converted it to json.
I also used that when I click on a specific product, only the product information appears on a new page, and I call up the product details using data – id methods, its value is the product name, but I created an html file for each product, and this affects performance greatly, and the situation is also very cumbersome, and it is not best practice