본문 바로가기
Spring

[Spring] Spring MVC + handlebars + Helper 추가

by Real Iron 2019. 2. 8.
  1. Spring에서 Handlebars를 쓴다면?


    1. 의존성 설정

    Maven
    <dependency>
       <groupId>com.github.jknack</groupId>
       <artifactId>handlebars-springmvc</artifactId>
       <version>${handlebars-version}</version>
    </dependency>

    https://github.com/jknack/handlebars.java/tree/master/handlebars-springmvc  

     

    2. View Resolver 설정

    xml base
    <bean class="com.github.jknack.handlebars.springmvc.HandlebarsViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".hbs"/>
    </bean>

     

    java configration
    @Bean
    public HandlebarsViewResolver handlebarsViewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();
        viewResolver.setOrder(1);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".hbs");
        return viewResolver;
    }

     

    3. 사용

    java controller
    @Controller
    public class HelloController {
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public ModelAndView hello() {
            ModelAndView mav = new ModelAndView("index");
            mav.addObject("hello""Hello world!");
            return mav;
        }
    }
    index.hbs
    <html>
    <body>
    <h1>{{hello}}</h1>
    </body>
    </html>
    html
    <html>
    <body>
    <h1>Hello world!</h1>
    </body>
    </html>


  2. 4. Helper 추가

    java configration
    @Bean
    public HandlebarsViewResolver handlebarsViewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();
        
        Helper<Object> helper = new Helper<Object>() {
            @Override
            public Object apply(final Object context, final Options options) throws IOException {
        return "Spring Helper";
            }
        };

        viewResolver.registerHelper("spring", helper);
        viewResolver.setHelperSources(Arrays.asList(HandlebarsApp.class));
        Map<String, Helper<?>> helpers = new HashMap<String, Helper<?>>();
        helpers.put("setHelper", helper);
        viewResolver.setHelpers(helpers);
        // no cache for testing
        viewResolver.setCache(false);

        viewResolver.setBindI18nToMessageSource(true);

        return viewResolver;
    }


  1. 5. Handlebars + Spring boot

    java controller
    @Controller
    @SpringBootApplication
    public static class Application {

        @RequestMapping("/")
        public String index(Model model) {
            model.addAttribute("foo", "Hello Handlebars!");
            return"index";
        }

        public static void main(String[] args) {
          SpringApplication.run(Application.class, args);
        }
    }

index.hbs
<html>
<body>
<h1>{{foo}}</h1>
</body>
</html>


  1. 6. Registering custom helpers

    하나의 helper 등록할 경우
    handlebars.registerHelper("blog", new Helper<Blog>() {
      public CharSequence apply(Blog blog, Options options) {
        return options.fn(blog);
      }
    });


  1. 여러개의 helper 를 한번에 등록할 경우
    public class HelperSource {
      public String blog(Blog blog, Options options) {
        return options.fn(blog);
      }

      public static String now() {
        return new Date().toString();
      }

      public String render(Blog context,  String param0,  int param1,
         boolean param2, Options options) {
        return ...
      }
    }
  1. instance 등록하는 방법
    handlebars.registerHelpers(new HelperSource());


  1. static 등록하는 방법
    handlebars.registerHelpers(HelperSource.class);


  1. 7. Helper options

    하나의 helper 등록할 경우
    handlebars.registerHelper("blog", new Helper<Blog>() {
      public CharSequence apply(Blog blog, Options options) {
        return options.fn(blog);
      }
    });



  1. 8. HandlebarsApp.java

    HandlebarsApp.java
    package com.github.jknack.handlebars.springmvc;

    import java.io.IOException;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;

    import com.github.jknack.handlebars.Handlebars;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.MessageSource;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ResourceBundleMessageSource;

    import com.github.jknack.handlebars.Helper;
    import com.github.jknack.handlebars.Options;

    @Configuration
    public class HandlebarsApp {

      @Autowired
      ApplicationContext applicationContext;

      @Bean
      public HandlebarsViewResolver viewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();

        Helper<Object> helper = new Helper<Object>() {
          @Override
          public Object apply(final Object context, final Options options) throws IOException {
            return "Spring Helper";
          }
        };

        viewResolver.registerHelper("spring", helper);
        viewResolver.setHelperSources(Arrays.asList(HandlebarsApp.class));
        Map<String, Helper<?>> helpers = new HashMap<String, Helper<?>>();
        helpers.put("setHelper", helper);
        viewResolver.setHelpers(helpers);
        // no cache for testing
        viewResolver.setCache(false);

        viewResolver.setBindI18nToMessageSource(true);

        return viewResolver;
      }

      @Bean
      public HandlebarsViewResolver parameterizedHandlebarsViewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver(new Handlebars(
            new SpringTemplateLoader(applicationContext)));

        // no cache for testing
        viewResolver.setCache(false);

        return viewResolver;
      }

      @Bean
      public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        return messageSource;
      }

      @Bean
      public HandlebarsViewResolver viewResolverWithoutMessageHelper() {
        return new HandlebarsViewResolver().withoutMessageHelper();
      }

      public static CharSequence helperSource() {
        return "helper source!";
      }
    }


  1. 9. Helper 추가(Create a Dynamic Select)

    java configration
    @Bean
    public HandlebarsViewResolver handlebarsViewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();
        
        Helper<Object> helper = new Helper<Object>() {
            public CharSequence apply(List<CmnCdVO> list, Options options)
    {
        String lsSltId = options.param( 0 ); // SELECT ID
        String lsMode = options.param( 1 ); // SELECT MODE(S:선택 , A:전체)
        String lsEtc = options.param( 2 ); // SELECT REQUIRED
        StringBuffer sb = new StringBuffer(); // SELECT HTML

        sb.append( "<select id='" ).append ( lsSltId ).append ( "' name='" ).append ( lsSltId ).append ( "' " ).append ( lsEtc ).append( " >" );

        if ( "A".equals ( lsMode ) )
        {
            sb.append( "<option value=\"\">전체</option>" );
        }
        else
        {
            sb.append( "<option value=\"\">선택</option>" );
        }

        for (CmnCdVO cmnCdVO: list)
        {
            sb.append( "<option value='" ).append ( cmnCdVO.getCd() ).append ( "'>" ).append ( cmnCdVO.getNmCdKo() ).append ( "</option>" );
        }

        sb.append( "</select>" );

        return new Handlebars.SafeString( sb.toString() );
    }
        };

        viewResolver.registerHelper("select", helper);
        viewResolver.setHelperSources(Arrays.asList(HandlebarsApp.class));
        Map<String, Helper<?>> helpers = new HashMap<String, Helper<?>>();
        helpers.put("setHelper", helper);
        viewResolver.setHelpers(helpers);
        // no cache for testing
        viewResolver.setCache(false);

        viewResolver.setBindI18nToMessageSource(true);

        return viewResolver;
    }



  1. 10. Custom Helper 분리 구성

    java configration
    @Bean
    public HandlebarsViewResolver handlebarsViewResolver() {
        HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();
        viewResolver.registerHelper("select", new SelectHelper());
        viewResolver.registerHelper("math", new MathHelper());
        viewResolver.registerHelper("url", new UrlHelper());        
        viewResolver.registerHelpers(ConditionalHelpers.class);    // handlebars 자체 확장Helper 추가
        viewResolver.setOrder(0);
        viewResolver.setViewClass(HandlebarsView.class);
        viewResolver.setContentType(DEFAULT_CONTENT_TYPE);
        viewResolver.setPrefix("/WEB-INF/views/hbs/");
        viewResolver.setSuffix(".hbs");
        viewResolver.setCache(false);
        viewResolver.setFailOnMissingFile(false);
        return viewResolver;
    }


  1. Select Custom Helper
    import java.util.List;

    import com.github.jknack.handlebars.Handlebars;
    import com.github.jknack.handlebars.Helper;
    import com.github.jknack.handlebars.Options;
    import com.gsc.component.share.vo.ViewCmnCdVO;

    /**
     * Handlebars Select Helper
     *
     */
    public class SelectHelper implements Helper<List<ViewCmnCdVO>>
    {

        public CharSequence apply(List<ViewCmnCdVO> list, Options options)
        {
            String lsSltId    = options.param( 0 );        // SELECT ID
            String lsMode    = options.param( 1 );        // SELECT MODE(S:선택 , A:전체)
            String lsEtc    = options.param( 2 );        // SELECT REQUIRED
            StringBuffer sb = new StringBuffer();        // SELECT HTML

            sb.append( "<select id='" ).append ( lsSltId ).append ( "' name='" ).append ( lsSltId ).append ( "' " ).append ( lsEtc ).append( " >" );

            if ( "A".equals ( lsMode ) )
            {
                sb.append( "<option value=\"\">전체</option>" );
            }
            else
            {
                sb.append( "<option value=\"\">선택</option>" );
            }

            for (ViewCmnCdVO cmnCdVO: list)
            {
                sb.append( "<option value='" ).append ( cmnCdVO.getCd() ).append ( "'>" ).append ( cmnCdVO.getNmCdKo() ).append ( "</option>" );
            }

            sb.append( "</select>" );

            return new Handlebars.SafeString( sb.toString() );
        }
    }


  1. Math Custom Helper - https://github.com/mrhanlon/handlebars-math-helper
    import java.io.IOException;
    import java.math.BigDecimal;
    import java.math.MathContext;
    import java.math.RoundingMode;

    import com.github.jknack.handlebars.Helper;
    import com.github.jknack.handlebars.Options;

    /**
     * Handlebars Math Helper
     *
     * <p>
     * Perform math operations in handlebars. Inspired by: http://jsfiddle.net/mpetrovich/wMmHS/
     * Operands are treated as java.math.BigDecimal and operations are performed with the
     * java.math.MathContext.DECIMAL64 MathContext, yielding results with 16 bits of precision
     * and rounding to the nearest EVEN digit, according to IEEE 754R. You can force rounding
     * decimal results using the extra parameter <code>scale</code>, which corresponds to calling
     * <code>BigDecimal.setScale(int scale, RoundingMode.HALF_UP)</code>.
     * </p>
     *
     * <p>addition</p>
     *
     * <pre>{{math arg0 "+" arg1}} // arg0 + arg1</pre>
     *
     * <p>subtraction</p>
     *
     * <pre>{{math arg0 "-" arg1}} // arg0 - arg1</pre>
     *
     * <p>multiplication</p>
     *
     * <pre>{{math arg0 "*" arg1}} // arg0 * arg1</pre>
     *
     * <p>division</p>
     *
     * <pre>{{math arg0 "/" arg1}} // arg0 / arg1</pre>
     *
     * <p>modulus</p>
     *
     * <pre>{{math arg0 "%" arg1}} // arg0 % arg1</pre>
     *
     * @author mrhanlon
     * @see java.math.BigDecimal
     * @see java.math.MathContext
     */
    public class MathHelper implements Helper<Object> {

        public enum Operator
        {
            add("+"),
            subtract("-"),
            multiply("*"),
            divide("/"),
            mod("%");

            private Operator(String symbol)
            {
                this.symbol = symbol;
            }

            private String symbol;

            public static Operator fromSymbol(String symbol)
            {
                Operator op = null;

                if (symbol.equals("+"))
                {
                    op = add;
                } else if (symbol.equals("-"))
                {
                    op = subtract;
                } else if (symbol.equals("*"))
                {
                    op = multiply;
                } else if (symbol.equals("/"))
                {
                    op = divide;
                } else if (symbol.equals("%"))
                {
                    op = mod;
                }

                return op;
            }
        }

        @Override
        public CharSequence apply(final Object value, Options options) throws IOException, IllegalArgumentException
        {
            if (options.params.length >= 2)
            {
                Operator op = Operator.fromSymbol(options.param(0).toString());
                if (op == null)
                {
                    throw new IllegalArgumentException("Unknown operation '" + options.param(0) + "'");
                }

                Integer scale = options.hash("scale");

                MathContext mc = new MathContext(16, RoundingMode.HALF_UP);

                if (value == null || options.param(1) == null)
                {
                    throw new IllegalArgumentException("Cannot perform operations on null values");
                }

                BigDecimal t0 = new BigDecimal(value.toString());
                BigDecimal t1 = new BigDecimal(options.param(1).toString());
                BigDecimal result;
                switch (op)
                {
                    case add        : result = t0.add(t1, mc);
                    break;
                    case subtract    : result = t0.subtract(t1, mc);
                    break;
                    case multiply    : result = t0.multiply(t1, mc);
                    break;
                    case divide        : result = t0.divide(t1, mc);
                    break;
                    case mod        : result = t0.remainder(t1, mc);
                    break;
                    default            : throw new IllegalArgumentException("Unknown operation '" + options.param(0) + "'");
                }

                if (scale != null)
                {
                    result = result.setScale(scale, BigDecimal.ROUND_HALF_UP);
                }

                return result.toString();
            }
            else
            {
                throw new IOException("MathHelper requires three parameters");
            }
        }
    }


  1. Url Custom Helper - html 태그 url decocding
    import com.github.jknack.handlebars.Handlebars;
    import com.github.jknack.handlebars.Helper;
    import com.github.jknack.handlebars.Options;

    /**
     * Handlebars Url Helper
     *
     */
    public class UrlHelper implements Helper<String>
    {

    public CharSequence apply( String value, Options options)
    {
    return new Handlebars.SafeString( value );
    }
    }




참조

https://jknack.github.io/handlebars.java/helpers.html (Handlebars.java)

https://github.com/jknack/handlebars.java/blob/master/handlebars-springmvc/src/test/java/com/github/jknack/handlebars/springmvc/HandlebarsApp.java (github)

http://allegro.tech/2015/04/spring-boot-starter-handlebars.html (Spring Boot Handlebars)

https://slipp.net/wiki/display/SLS/mustache#mustache-잉여내용:Spring에서Handlebars를쓴다면?