All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.flow.data.renderer.BasicRenderer Maven / Gradle / Ivy

There is a newer version: 24.5.5
Show newest version
/*
 * Copyright 2000-2024 Vaadin Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.vaadin.flow.data.renderer;

import java.util.Objects;
import java.util.Optional;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.data.provider.AbstractComponentDataGenerator;
import com.vaadin.flow.data.provider.DataGenerator;
import com.vaadin.flow.data.provider.DataKeyMapper;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.dom.ElementFactory;
import com.vaadin.flow.function.ValueProvider;

import elemental.json.JsonObject;

/**
 *
 * Abstract renderer used as the base implementation for renderers that outputs
 * a simple value in the UI, such as {@link NumberRenderer} and
 * {@link LocalDateRenderer}.
 *
 * @author Vaadin Ltd
 *
 * @param 
 *            the type of the item used inside the renderer
 * @param 
 *            the type of the output object, such as Number or LocalDate
 */
public abstract class BasicRenderer
        extends ComponentRenderer {

    private final ValueProvider valueProvider;

    /**
     * Builds a new template renderer using the value provider as the source of
     * values to be rendered.
     *
     * @param valueProvider
     *            the callback to provide a objects to the renderer, not
     *            null
     */
    protected BasicRenderer(ValueProvider valueProvider) {
        if (valueProvider == null) {
            throw new IllegalArgumentException("valueProvider may not be null");
        }

        this.valueProvider = valueProvider;
    }

    protected ValueProvider getValueProvider() {
        return valueProvider;
    }

    @Override
    public Rendering render(Element container,
            DataKeyMapper keyMapper, Element contentTemplate) {
        SimpleValueRendering rendering = new SimpleValueRendering(
                keyMapper == null ? null : keyMapper::key);
        rendering.setTemplateElement(contentTemplate);
        setupTemplate(container, rendering, keyMapper);

        return rendering;
    }

    private void setupTemplate(Element owner, SimpleValueRendering rendering,
            DataKeyMapper keyMapper) {
        /*
         * setupTemplateWhenAttached does some setup that will be needed by
         * generateData. To ensure the setup has completed before it is needed,
         * we forego the general convention of using beforeClientResponse to
         * guard the action against duplicate invocation. This is not a big
         * problem in this case since setupTemplateWhenAttached only sets
         * properties but doesn't execute any JS.
         */
        owner.getNode().runWhenAttached(
                ui -> setupTemplateWhenAttached(owner, rendering, keyMapper));
    }

    private void setupTemplateWhenAttached(Element owner,
            SimpleValueRendering rendering, DataKeyMapper keyMapper) {

        Element templateElement = rendering.getTemplateElement();
        owner.appendChild(templateElement);

        if (keyMapper != null) {
            String propertyName = getTemplatePropertyName(rendering);

            templateElement.setProperty("innerHTML", getTemplateForProperty(
                    "[[item." + propertyName + "]]", rendering));
            rendering.setPropertyName(propertyName);

            RendererUtil.registerEventHandlers(this, templateElement, owner,
                    keyMapper::get);
        } else {
            String value = getFormattedValue(null);
            templateElement.setProperty("innerHTML",
                    getTemplateForProperty(value, rendering));
            rendering.setContainer(owner);
        }
    }

    /**
     * Gets the name of the property to be transmitted and used inside the
     * template. By default, it generates a unique name by using the class name
     * of the renderer and the node id of the template element.
     * 

* This method is only called when * {@link #render(Element, DataKeyMapper, Element)} is invoked. * * @param context * the rendering context * @return the property name to be used in template data bindings * * @see Rendering#getTemplateElement() */ protected String getTemplatePropertyName(Rendering context) { Objects.requireNonNull(context, "The context should not be null"); Element templateElement = context.getTemplateElement(); if (templateElement == null) { throw new IllegalArgumentException( "The provided rendering doesn't contain a template element"); } return "_" + getClass().getSimpleName() + "_" + templateElement.getNode().getId(); } /** * Gets the template String for a given property. *

* This method is only called when * {@link #render(Element, DataKeyMapper, Element)} is invoked. * * @param property * the property to be used inside the template * @param context * the rendering context * @return the template string to be used inside a {@code