Good evening all,
I'm learning Thymeleaf and web applications in general right now, and for starters, I'm trying to implement a web service with a page where you can view all registered users and filter them.
Since I want some pagination, I have two forms on this page:
a group of buttons linking to the first, previous, next, and last page
a form with various options for filtering, e.g. "username contains" or "min / max age"
My controller looks like this:
#RequestMapping("/users/all")
String showSearchPage(#RequestParam(value="page", required=false, defaultValue = "0") int page,
#CurrentSecurityContext(expression="authentication?.name") String username,
Model model) {
Page<User> userPage = userService.filterUsers(username, "", 0, 100, PageRequest.of(page, 10));
model.addAttribute("userPage", userPage);
model.addAttribute("pageNr", page);
return "users.html";
}
As you can see, I only implemented the buttons yet and always filter for some default values. (The username parameter makes sure that the currently logged in user isn't finding themself in the list.) My button form looks like that:
<form class="button" th:action="#{/users/all}" method="POST">
<button th:disabled="${pageNr == 0}" type="submit"
class="btn btn-primary"
name="page" th:value="0"><<</button>
<button th:disabled="${pageNr == 0}" type="submit"
class="btn btn-primary"
name="page" th:value="${pageNr - 1}"><</button>
<button th:disabled="${pageNr == userPage.getTotalPages - 1}" type="submit"
class="btn btn-primary"
name="page" th:value="${pageNr + 1}">></button>
<button th:disabled="${pageNr == userPage.getTotalPages - 1}" type="submit"
class="btn btn-primary"
name="page" th:value="${userPage.getTotalPages - 1}">>></button>
</form>
So I'm using the request parameter page to only show the requested page.
Now that I'm about to implement the filtering, my first approach would be adding the form to my HTML, and adding some #ModelAttribute FilterForm filterForm to my controller to be able to get the submitted filter values and use them to retrieve the filtered user list. However, when thinking about it, I found the problem that both forms would only submit their own content, and the controller would only get one of both. Therefore, after filtering users, I would inadvertedly revert back to the full user list when changing pages.
What would be the best approach here to make sure that both functions, filtering and pagination, work together properly?
Thanks in advance!
I'd go with HTTP GET method instead of POST. Why so? Reading users is an idempotent and safe operation. The applied filter and page number can be easily bookmarked in that case.
For filters, you can just add more params. Nothing bad about that.
Make it a bit more RESTful. "/users/all" is unnecessary. "/users" should be enough.
#GetMapping("/users")
String showSearchPage(#RequestParam(value="page", required=false, defaultValue = "0") int page,
#RequestParam("minAge") Optional<Integer> minAge,
#RequestParam("maxAge") Optional<Integer> maxAge,
#CurrentSecurityContext(expression="authentication?.name") String username,
Model model) {
// Apply filters too...
Page<User> userPage = userService.filterUsers(username, "", 0, 100, PageRequest.of(page, 10));
model.addAttribute("userPage", userPage);
model.addAttribute("pageNr", page);
model.addAttribute("nextPage", getPageWithFilterUrl(page + 1, minAge, maxAge));
model.addAttribute("previousPage", getPageWithFilterUrl(page - 1, minAge, maxAge));
return "users.html";
}
To preserve filter while moving back and forth:
private String getPageWithFilterUrl(int page, Optional<Integer> minAge, Optional<Integer> maxAge) {
String defaultNextPageUrl = "/users?page=" + page;
String withMinAge = minAge.map(ma -> defaultNextPageUrl + "&minAge=" + ma).orElse(defaultNextPageUrl);
String withMaxAge = maxAge.map(ma -> withMinAge + "&maxAge=" + ma).orElse(withMinAge);
return withMaxAge;
}
I think the answer is not given here yet. I have the same problem now. I am trying in my project with redirectAttributes.addFlashAttribute("searchProductItemDTO", searchProductItemDTO);
But the problem comes when clicking on the Next/Previous buttons - clicking them does not take into account the search criteria.
Here is my total solution:
1) Pay attention here to the JpaSpecificationExecutor<ItemEntity>
#Repository
public interface AllItemsRepository extends
PagingAndSortingRepository<ItemEntity, Long>, JpaSpecificationExecutor<ItemEntity>{
}
2) Pay attention here to the CriteriaBuilder
public class ProductItemSpecification implements Specification<ItemEntity> {
private final SearchProductItemDTO searchProductItemDTO;
private final String type;
public ProductItemSpecification(SearchProductItemDTO searchProductItemDTO, String type) {
this.searchProductItemDTO = searchProductItemDTO;
this.type = type;
}
#Override
public Predicate toPredicate(Root<ItemEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder cb) {
Predicate predicate = cb.conjunction();
predicate.getExpressions().add(cb.equal(root.get("type"), type));
if (searchProductItemDTO.getModel() != null && !searchProductItemDTO.getModel().isBlank()) {
Path<Object> model = root.get("model");
predicate.getExpressions().add(
//!!!!! when we have two relationally connected tables
// cb.and(cb.equal(root.join("model").get("name"), searchProductItemDTO.getModel()));
//when all fields are from the same table ItemEntity:::: the like works case insensitive
cb.and(cb.like(root.get("model").as(String.class), "%" + searchProductItemDTO.getModel() + "%"))
);
}
if (searchProductItemDTO.getMinPrice() != null) {
predicate.getExpressions().add(
cb.and(cb.greaterThanOrEqualTo(root.get("sellingPrice"),
searchProductItemDTO.getMinPrice()))
);
}
if (searchProductItemDTO.getMaxPrice() != null) {
predicate.getExpressions().add(
cb.and(cb.lessThanOrEqualTo(root.get("sellingPrice"),
searchProductItemDTO.getMaxPrice()))
);
}
return predicate;
}
}
3) Pay attention here to the this.allItemsRepository.findAll default usage
//Complicated use
public Page<ComputerViewGeneralModel> getAllComputersPageableAndSearched(
Pageable pageable, SearchProductItemDTO searchProductItemDTO, String type) {
Page<ComputerViewGeneralModel> allComputers = this.allItemsRepository
.findAll(new ProductItemSpecification(searchProductItemDTO, type), pageable)
.map(comp -> this.structMapper
.computerEntityToComputerSalesViewGeneralModel((ComputerEntity) comp));
return allComputers;
}
4) Pay attention here to the redirectAttributes.addFlashAttribute
#Controller
#RequestMapping("/items/all")
public class ViewItemsController {
private final ComputerService computerService;
#GetMapping("/computer")
public String viewAllComputers(Model model,
#Valid SearchProductItemDTO searchProductItemDTO,
#PageableDefault(page = 0,
size = 3,
sort = "sellingPrice",
direction = Sort.Direction.ASC) Pageable pageable,
RedirectAttributes redirectAttributes) {
if (!model.containsAttribute("searchProductItemDTO")) {
model.addAttribute("searchProductItemDTO", searchProductItemDTO);
}
Page<ComputerViewGeneralModel> computers = this.computerService
.getAllComputersPageableAndSearched(pageable, searchProductItemDTO, "computer");
model.addAttribute("computers", computers);
redirectAttributes.addFlashAttribute("searchProductItemDTO", searchProductItemDTO);
return "/viewItems/all-computers";
}
5) Pay attention here to all the search params that we add in the html file, in the 4 sections where pagination navigation
<main>
<div class="container-fluid">
<div class="container">
<h2 class="text-center text-white">Search for offers</h2>
<form
th:method="GET"
th:action="#{/items/all/computer}"
th:object="${searchProductItemDTO}"
class="form-inline"
style="justify-content: center; margin-top: 50px;"
>
<div style="position: relative">
<input
th:field="*{model}"
th:errorclass="is-invalid"
class="form-control mr-sm-2"
style="width: 280px;"
type="search"
placeholder="Model name case Insensitive..."
aria-label="Search"
id="model"
/>
<input
th:field="*{minPrice}"
th:errorclass="is-invalid"
class="form-control mr-sm-2"
style="width: 280px;"
type="search"
placeholder="Min price..."
aria-label="Search"
id="minPrice"
/>
<input
th:field="*{maxPrice}"
th:errorclass="is-invalid"
class="form-control mr-sm-2"
style="width: 280px;"
type="search"
placeholder="Max price..."
aria-label="Search"
id="maxPrice"
/>
</div>
<button class="btn btn-outline-info my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
<h2 class="text-center text-white mt-5 greybg" th:text="#{view_all_computers}">.........All
Computers.......</h2>
<div class="offers row mx-auto d-flex flex-row justify-content-center .row-cols-auto">
<div
th:each="c : ${computers}" th:object="${c}"
class="offer card col-sm-2 col-md-3 col-lg-3 m-2 p-0">
<div class="card-img-top-wrapper" style="height: 20rem">
<img
class="card-img-top"
alt="Computer image"
th:src="*{photoUrl}">
</div>
<div class="card-body pb-1">
<h5 class="card-title"
th:text="' Model: ' + *{model}">
Model name</h5>
</div>
<ul class="offer-details list-group list-group-flush">
<li class="list-group-item">
<div class="card-text"><span th:text="'* ' + *{processor}">Processor</span></div>
<div class="card-text"><span th:text="'* ' + *{videoCard}">Video card</span></div>
<div class="card-text"><span th:text="'* ' + *{ram}">Ram</span></div>
<div class="card-text"><span th:text="'* ' + *{disk}">Disk</span></div>
<div th:if="*{!ssd.isBlank()}" class="card-text"><span th:text="'* ' + *{ssd}">SSD</span></div>
<div th:if="*{!moreInfo.isBlank()}" class="card-text"><span th:text="'* ' + *{moreInfo}">More info</span>
</div>
<div class="card-text"><span th:text="'We sell at: ' + *{sellingPrice} + ' лв'"
style="font-weight: bold">Selling price</span></div>
</li>
</ul>
<div class="card-body">
<div class="row">
<a class="btn btn-link"
th:href="#{/items/all/computer/details/{id} (id=*{itemId})}">Details</a>
<th:block sec:authorize="hasRole('ADMIN') || hasRole('EMPLOYEE_PURCHASES')">
<a class="btn btn-link alert-danger"
th:href="#{/pages/purchases/computers/{id}/edit (id=*{itemId})}">Update</a>
<form th:action="#{/pages/purchases/computers/delete/{id} (id=*{itemId})}"
th:method="delete">
<input type="submit" class="btn btn-link alert-danger" value="Delete"></input>
</form>
</th:block>
</div>
</div>
</div>
</div>
<div class="container-fluid row justify-content-center">
<nav>
<ul class="pagination">
<li class="page-item" th:classappend="${computers.isFirst()} ? 'disabled' : ''">
<a th:unless="${computers.isFirst()}"
class="page-link"
th:href="#{/items/all/computer(size=${computers.getSize()},page=0,model=${searchProductItemDTO.getModel()}, minPrice=${searchProductItemDTO.getMinPrice()},maxPrice=${searchProductItemDTO.getMaxPrice()})}">First</a>
</li>
</ul>
</nav>
<nav>
<ul class="pagination">
<li class="page-item" th:classappend="${computers.hasPrevious() ? '' : 'disabled'}">
<a th:if="${computers.hasPrevious()}"
class="page-link"
th:href="#{/items/all/computer(size=${computers.getSize()},page=${computers.getNumber() - 1},model=${searchProductItemDTO.getModel()}, minPrice=${searchProductItemDTO.getMinPrice()},maxPrice=${searchProductItemDTO.getMaxPrice()})}">Previous</a>
</li>
</ul>
</nav>
<nav>
<ul class="pagination">
<li class="page-item" th:classappend="${computers.hasNext() ? '' : 'disabled'}">
<a th:if="${computers.hasNext()}"
class="page-link"
th:href="#{/items/all/computer(size=${computers.getSize()},page=${computers.getNumber() + 1},model=${searchProductItemDTO.getModel()}, minPrice=${searchProductItemDTO.getMinPrice()},maxPrice=${searchProductItemDTO.getMaxPrice()})}">Next</a>
</li>
</ul>
</nav>
<nav>
<ul class="pagination">
<li class="page-item" th:classappend="${computers.isLast()} ? 'disabled' : ''">
<a th:unless="${computers.isLast()}"
class="page-link"
th:href="#{/items/all/computer(size=${computers.getSize()},page=${computers.getTotalPages()-1},model=${searchProductItemDTO.getModel()},minPrice=${searchProductItemDTO.getMinPrice()},maxPrice=${searchProductItemDTO.getMaxPrice()})}">Last</a>
</li>
</ul>
</nav>`enter code here`
</div>
</div>
</main>
I have loaded a sidebar over ajax however this html uses knockoutJS to render completely. I am wondering how to execute the KnockoutJs portions of this code.
The content below is loaded via jQuery ajax function and contains a number of knockout elements as well as some X Magento Init type scripts:
<div class=\"block filter\" id=\"layered-filter-block\" data-mage-init='{\"collapsible\":{\"openedState\": \"active\", \"collapsible\": true, \"active\": false, \"collateral\": { \"openedState\": \"filter-active\", \"element\": \"body\" } }}'>
<div class=\"block-title filter-title\" data-count=\"0\">
<strong data-role=\"title\">Shop By<\/strong>
<\/div>
<div class=\"block-content filter-content\">
<strong role=\"heading\" aria-level=\"2\" class=\"block-subtitle filter-subtitle\">Shopping Options<\/strong>
<div class=\"filter-options\" id=\"narrow-by-list\" data-role=\"content\" data-mage-init='{\"accordion\":{\"openedState\": \"active\", \"collapsible\": true, \"active\": [0,1,2], \"multipleCollapsible\": true}}'>
<div data-role=\"collapsible\" class=\"filter-options-item\">
<div data-role=\"title\" class=\"filter-options-title\">Category<\/div>
<div data-role=\"content\" class=\"filter-options-content\">\n<ol class=\"items\">
<li class=\"item\">
<a href=\"http:\/\/www.domain.com\/catalogsearch\/result\/index\/?ajax=1&cat=143&q=ice+machine\">Front of House
<span class=\"count\">2<span class=\"filter-count-label\">items<\/span><\/span><\/a>
<\/li>
<li class=\"item\">
<a href=\"http:\/\/www.domain.com\/catalogsearch\/result\/index\/?ajax=1&cat=182&q=ice+machine\">Bar Supplies
<span class=\"count\">4<span class=\"filter-count-label\">items<\/span><\/span><\/a>
<\/li>
<li class=\"item\">
<a href=\"http:\/\/www.domain.com\/catalogsearch\/result\/index\/?ajax=1&cat=257&q=ice+machine\">Catering Equipment<span class=\"count\">111<span class=\"filter-count-label\">\n
items <\/span><\/span>\n
<\/a>\n <\/li>\n
<li class=\"item\">\n
<a href=\"http:\/\/www.domain.com\/catalogsearch\/result\/index\/?ajax=1&cat=342&q=ice+machine\">\n
Warewashing <span class=\"count\">\n
3 <span class=\"filter-count-label\">\n
items <\/span><\/span>\n
<\/a>\n <\/li>\n <li class=\"item\">\n
<a href=\"http:\/\/www.domain.com\/catalogsearch\/result\/index\/?ajax=1&cat=521&q=ice+machine\">\n
Catering Equipment Offers <span class=\"count\">\n 1
<span class=\"filter-count-label\">\n item <\/span><\/span>\n
<\/a>\n <\/li>\
<\/ol>
<\/div>\n
<\/div>\n
<div data-role=\"collapsible\" class=\"filter-options-item\">
<div data-role=\"title\" class=\"filter-options-title\">Brand<\/div>\n
<div data-role=\"content\" class=\"filter-options-content\">
<div data-bind=\"scope: 'brandFilter'\">
<!-- ko template: getTemplate() --> <!-- \/ko -->
<\/div>
<script type=\"text\/x-magento-init\">
{\"*\" : {\"Magento_Ui\/js\/core\/app\": {\"components\": {\"brandFilter\": {\"component\":\"Smile_ElasticsuiteCatalog\\\/js\\\/attribute-filter\",\"maxSize\":10,\"displayProductCount\":true,\"hasMoreItems\":true,\"ajaxLoadUrl\":\"http:\\\/\\\/www.domain.com\\\/catalog\\\/navigation_filter\\\/ajax\\\/?ajax=1&filterName=brand&q=ice+machine\",\"items\":[{\"label\":\"Scotsman\",\"count\":41,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Scotsman&q=ice+machine\",\"is_selected\":false},{\"label\":\"Hoshizaki\",\"count\":15,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Hoshizaki&q=ice+machine\",\"is_selected\":false},{\"label\":\"Ice-o-matic\",\"count\":12,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Ice-o-matic&q=ice+machine\",\"is_selected\":false},{\"label\":\"Blue Ice\",\"count\":7,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Blue+Ice&q=ice+machine\",\"is_selected\":false},{\"label\":\"Graupel\",\"count\":7,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Graupel&q=ice+machine\",\"is_selected\":false},{\"label\":\"Nemox\",\"count\":7,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Nemox&q=ice+machine\",\"is_selected\":false},{\"label\":\"Manitowoc\",\"count\":6,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Manitowoc&q=ice+machine\",\"is_selected\":false},{\"label\":\"Polar Refrigeration\",\"count\":5,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Polar+Refrigeration&q=ice+machine\",\"is_selected\":false},{\"label\":\"Longo & Co\",\"count\":4,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Longo+%26+Co&q=ice+machine\",\"is_selected\":false},{\"label\":\"Beaumont\",\"count\":3,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&brand=Beaumont&q=ice+machine\",\"is_selected\":false}]}}}}}\n<\/script>\n\n<\/div>\n <\/div>\n <div data-role=\"collapsible\" class=\"filter-options-item\">\n <div data-role=\"title\" class=\"filter-options-title\">Power<\/div>\n <div data-role=\"content\" class=\"filter-options-content\"><div data-bind=\"scope: 'power_ddFilter'\">\n <!-- ko template: getTemplate() --> <!-- \/ko -->\n<\/div>\n\n<script type=\"text\/x-magento-init\">\n {\"*\" : {\"Magento_Ui\/js\/core\/app\": {\"components\": {\"power_ddFilter\": {\"component\":\"Smile_ElasticsuiteCatalog\\\/js\\\/attribute-filter\",\"maxSize\":10,\"displayProductCount\":true,\"hasMoreItems\":false,\"ajaxLoadUrl\":\"http:\\\/\\\/www.domain.com\\\/catalog\\\/navigation_filter\\\/ajax\\\/?ajax=1&filterName=power_dd&q=ice+machine\",\"items\":[{\"label\":\"13 Amp (Plug)\",\"count\":111,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&power_dd=13+Amp+%28Plug%29&q=ice+machine\",\"is_selected\":false},{\"label\":\"1 Phase (Hard Wired)\",\"count\":2,\"url\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&power_dd=1+Phase+%28Hard+Wired%29&q=ice+machine\",\"is_selected\":false}]}}}}}\n<\/script>\n\n<\/div>\n <\/div>\n <div data-role=\"collapsible\" class=\"filter-options-item\">\n <div data-role=\"title\" class=\"filter-options-title\">Price<\/div>\n <div data-role=\"content\" class=\"filter-options-content\"><div class=\"smile-es-range-slider\" data-role=\"range-price-slider-price\">\n <div data-role=\"from-label\"><\/div>\n <div data-role=\"to-label\"><\/div>\n <div data-role=\"slider-bar\"><\/div>\n <div class=\"actions-toolbar\">\n <div data-role=\"message-box\"><\/div>\n <div class=\"actions-primary\">\n <a class=\"action primary small\" data-role=\"apply-range\">\n <span>OK<\/span>\n <\/a>\n <\/div>\n <\/div>\n<\/div>\n\n<script type=\"text\/x-magento-init\">\n { \"[data-role=range-price-slider-price]\" : { \"rangeSlider\" : {\"minValue\":1,\"maxValue\":6091,\"currentValue\":{\"from\":1,\"to\":6091},\"fieldFormat\":{\"pattern\":\"\\u00a3%s\",\"precision\":2,\"requiredPrecision\":2,\"decimalSymbol\":\".\",\"groupSymbol\":\",\",\"groupLength\":3,\"integerRequired\":false},\"intervals\":[{\"value\":1,\"count\":1},{\"value\":2,\"count\":1},{\"value\":3,\"count\":1},{\"value\":40,\"count\":1},{\"value\":60,\"count\":1},{\"value\":64,\"count\":1},{\"value\":150,\"count\":1},{\"value\":179,\"count\":1},{\"value\":190,\"count\":1},{\"value\":242,\"count\":1},{\"value\":291,\"count\":1},{\"value\":325,\"count\":1},{\"value\":355,\"count\":2},{\"value\":395,\"count\":1},{\"value\":465,\"count\":1},{\"value\":472,\"count\":1},{\"value\":515,\"count\":1},{\"value\":520,\"count\":1},{\"value\":535,\"count\":1},{\"value\":555,\"count\":1},{\"value\":577,\"count\":1},{\"value\":585,\"count\":1},{\"value\":599,\"count\":1},{\"value\":605,\"count\":2},{\"value\":615,\"count\":1},{\"value\":640,\"count\":1},{\"value\":658,\"count\":1},{\"value\":685,\"count\":1},{\"value\":705,\"count\":1},{\"value\":730,\"count\":1},{\"value\":745,\"count\":2},{\"value\":785,\"count\":1},{\"value\":805,\"count\":1},{\"value\":830,\"count\":1},{\"value\":895,\"count\":2},{\"value\":925,\"count\":1},{\"value\":965,\"count\":1},{\"value\":970,\"count\":1},{\"value\":990,\"count\":2},{\"value\":1030,\"count\":1},{\"value\":1065,\"count\":1},{\"value\":1080,\"count\":1},{\"value\":1085,\"count\":1},{\"value\":1095,\"count\":1},{\"value\":1105,\"count\":1},{\"value\":1130,\"count\":1},{\"value\":1155,\"count\":1},{\"value\":1225,\"count\":1},{\"value\":1235,\"count\":1},{\"value\":1240,\"count\":1},{\"value\":1259,\"count\":1},{\"value\":1310,\"count\":1},{\"value\":1360,\"count\":1},{\"value\":1365,\"count\":1},{\"value\":1450,\"count\":1},{\"value\":1485,\"count\":1},{\"value\":1495,\"count\":1},{\"value\":1510,\"count\":1},{\"value\":1580,\"count\":2},{\"value\":1605,\"count\":2},{\"value\":1685,\"count\":1},{\"value\":1710,\"count\":1},{\"value\":1779,\"count\":1},{\"value\":1785,\"count\":1},{\"value\":1865,\"count\":1},{\"value\":1870,\"count\":1},{\"value\":1885,\"count\":1},{\"value\":1890,\"count\":1},{\"value\":1970,\"count\":1},{\"value\":1995,\"count\":1},{\"value\":2000,\"count\":1},{\"value\":2050,\"count\":1},{\"value\":2130,\"count\":1},{\"value\":2199,\"count\":1},{\"value\":2220,\"count\":1},{\"value\":2345,\"count\":1},{\"value\":2350,\"count\":1},{\"value\":2360,\"count\":1},{\"value\":2405,\"count\":1},{\"value\":2415,\"count\":1},{\"value\":2445,\"count\":1},{\"value\":2450,\"count\":2},{\"value\":2480,\"count\":1},{\"value\":2500,\"count\":1},{\"value\":2530,\"count\":1},{\"value\":2565,\"count\":1},{\"value\":2570,\"count\":1},{\"value\":2595,\"count\":1},{\"value\":2695,\"count\":1},{\"value\":2730,\"count\":1},{\"value\":2825,\"count\":1},{\"value\":2850,\"count\":1},{\"value\":2950,\"count\":1},{\"value\":2995,\"count\":1},{\"value\":3010,\"count\":1},{\"value\":3025,\"count\":1},{\"value\":3145,\"count\":1},{\"value\":3205,\"count\":1},{\"value\":3295,\"count\":1},{\"value\":3300,\"count\":1},{\"value\":3485,\"count\":1},{\"value\":3495,\"count\":1},{\"value\":3580,\"count\":1},{\"value\":4015,\"count\":1},{\"value\":4075,\"count\":1},{\"value\":4305,\"count\":1},{\"value\":4310,\"count\":1},{\"value\":4595,\"count\":1},{\"value\":4620,\"count\":1},{\"value\":5250,\"count\":1},{\"value\":5355,\"count\":1},{\"value\":6090,\"count\":1}],\"urlTemplate\":\"http:\\\/\\\/www.domain.com\\\/catalogsearch\\\/result\\\/index\\\/?ajax=1&price=<%- from %>-<%- to %>&q=ice+machine\",\"messageTemplates\":{\"displayCount\":\"<%- count %> products\",\"displayEmpty\":\"No products in the selected range.\"},\"rate\":1}
} }
<\/script>
<\/div>
<\/div>
<\/div>
<\/div>
<\/div>
These are then added to a block on my page via html jQuery method:
$(sidebarBlock).html(this.filters);
Looking at the DOM I cannot actually see the scripts however they are there in response when reviewing with console.log(). Similarly the below shows the scripts are present:
$(sidebar).find("script").each(function() {
console.log("found a script");
}
I have tried to use .trigger('contentUpdated'); like below:
document.getElementById("layered-filter-block").innerHTML = this.filters;
$(sidebarBlock).trigger('contentUpdated');
and:
$(sidebarBlock).html(this.filters);
$(sidebarBlock).trigger('contentUpdated');
and by reapplying bindings for knockout:
ko.cleanNode($('#layered-filter-block'));
ko.applyBindings($('#layered-filter-block'));
The above throws an error about bindings already being applied however but I have used cleanNode before to unbind however error persists.
This fixed issue for me:
$(sidebarBlock).applyBindings();
https://codeblog.experius.nl/magento-2-uicomponent-reinit-ajax-reload/