renderWith output visible in console, but does not update browser - javascript

I'm unable to get my controller to properly render the results of an AJAX request in my browser. I receive the correct html response using the specified Include for Ajax requests, but it is only visible in my browser's console. The page itself does not render the new content. What am I missing?
Here is my simplified controller for searching products:
class ProductSearch_Controller extends Page_Controller {
public function index(SS_HTTPRequest $request) {
// external api request, parsing response, returning as ArrayList $units
$return_data = array(
'Products' => $units
);
if($request->isAjax()) {
return $this->customise($return_data)->renderWith('SearchResults');
} else {
return $return_data;
}
}
}
The index method actually pulls in json data from an external source, parses it, and then passes it into an ArrayList, $units, which is then made available to the template as Products. Here is the simplified ProductSearch.ss template:
<section>
<div class="container">
<div class="productsearch__container">
<aside>$SearchForm</aside>
<main>
<h2>Search Results</h2>
<% include SearchResults %>
</main>
</div>
</div>
</section>
Here's the SearchResults template Include:
<div class="product__container">
<% loop $Products %>
<a href="$URL" class="product">
<h3>Unit $UnitNo</h3>
<ul>
<li>Price: $Lot_Price</li>
<li>SQFT: $SQFT</li>
<li>Bedrooms: $bedrooms_no</li>
<li>Baths: $baths_no</li>
</ul>
</a>
<% end_loop %>
</div>
And here is my simplified javascript:
$("[name='example-filter-variable']").on('change', function(e) {
e.preventDefault();
var params = $( "#Form_SearchForm" ).serialize();
$.get("url/example?" + params, function(data) {
console.log("Data: " + data);
});
});
Triggering the ajax event returns the appropriate response using the SearchResults.ss Include, which is visible in my console.log() output:
<div class="product__container">
<a href="/homes/search/show/6/101" class="product">
<h3>Unit 101</h3>
<ul>
<li>Price: </li>
<li>SQFT: 1531</li>
<li>Bedrooms: 2</li>
<li>Baths: 2</li>
</ul>
</a>
<a href="/homes/search/show/12/102" class="product">
<h3>Unit 102</h3>
<ul>
<li>Price: </li>
<li>SQFT: 1535</li>
<li>Bedrooms: 2</li>
<li>Baths: 2</li>
</ul>
</a>
... and so on
</div>
The problem is the template is actually being rendered in the browser. Am I doing something wrong?

This line in the template: <% include SearchResults %> is causing your template to include the SearchResults template.
You also don't have anything in your javascript to append your data into the DOM.
Try this template:
<section>
<div class="container">
<div class="productsearch__container">
<aside>$SearchForm</aside>
<main>
<h2>Search Results</h2>
<div class="results"></div>
</main>
</div>
</div>
</section>
With this js:
$("[name='example-filter-variable']").on('change', function(e) {
e.preventDefault();
var params = $( "#Form_SearchForm" ).serialize();
$.get("url/example?" + params, function(data) {
console.log("Data: " + data);
$(".results").append(data); // add something along these lines
});
});

Related

Handlebars - split string and iterate within {{each}}

Basically I am rendering a JSON file into a HTML template with Handlebars and all works fine except for one value where I get returned a string with comma separated values.
JSON file:
[
{
"venue_state": "Everywhere",
"venue_country": "United States",
"venue_lat": "34.2347",
"venue_lng": "-77.9482",
"rating_score": "4",
"created_at": "2022-07-01 17:13:16",
"flavor_profiles": "malty,biscuity,grainy,clove,banana"
},
{
....
}
]
HTML
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars#latest/dist/handlebars.js"></script>
<script src="script.js"></script>
<!--handlebars template-->
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags">
<li>{{flavor_profiles}}</li>
</ul>
</div>
{{/each}}
</script>
<div class="output"></div>
script.js
$(document).ready(function () {
var request = new XMLHttpRequest();
request.open("GET", "testdata.json", false);
request.send(null);
var data = JSON.parse(request.responseText);
//console.log(data);
//compile template and fill with data
var source = $("#checkin-template").html();
var template = Handlebars.compile(source);
$('.output').append(template(data));
});
This works except for {{flavor_profiles}}. What I want to get rendered is this
<ul class="tags">
<li>malty</li>
<li>biscuity</li>
<li>grainy</li>
<li>clove</li>
<li>banana</li>
</ul>
What I actually get is
<ul class="tags">
<li>malty,biscuity,grainy,clove,banana</li>
</ul>
and I guess it is because I get returned a string of comma separated values instead of an array?
I tried something like this
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags">
{{#each flavor_profiles}}
<li>{{this}}</li>
{{/each}}
</ul>
</div>
{{/each}}
</script>
and I also tried to register some helper function in script.js which did not work. This is a small hobby thing where I am just visualizing something in another way. So I don't want to use further frameworks or server side processing. The JSON gets pulled from elsewhere so I can't change how it looks. Is there any way within Handlebars to render my desired output?
I solved it like this
script.js
var data = JSON.parse(request.responseText, function(key, x) {
if (key === "flavor_profiles") {
x = x.split(',');
return x;
}
return x;
});
HTML
<ul class="tags">
{{#each flavor_profiles}}
<li>{{this}}</li>
{{/each}}
</ul>
I think little bit of j Query can help with your problem. I don't know this is what you expected.
HTML
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags" id="stringToList">
{{flavor_profiles}}
</ul>
</div>
{{/each}}
</script>
<div class="output"></div>
and new function to script.js
$(document).ready(function () {
$("[id='stringToList']").each(function () {
let el = $(this).text().toString().split(",").map(item => `<li>${item}</li>`).join('')
$(this).html(el)
})
});

Passing data using Handlebars.js

I've created a basic app to test out handlebars.js and I'm struggling to pass data between my pages. What I want is to have a list of countries being read from a JSON file, then the user can click on a country to be taken to more detail. The data loads fine on the first page but then when I click onto the link, it takes me to the next page were the data isn't loading in.
Could someone explain where I'm going wrong?
First page:
<div data-page="countrylist" class="page">
<div class="page-content">
<div class="content-block-title">Choose a country:</div>
<div class="list-block">
<ul>
{{#each this}}
<li>
<a href="detail.html" class="item-content item-link" data-context-name="countrylist.{{#index}}​">
<div class="item-inner">
<div class="item-title">{{Country}}​</div>
</div>
</a>
</li>
{{/each}}
</ul>
</div>
</div>
</div>
Detailed page:
<div data-page="countrylist" class="page">
<div class="page-content">
<div class="content-block inset">
<div class="content-block-inner">
<h3>{{Country}}​</h3>
<ul>
<li>Region: {{Region}}</li>
<li>Population: {{Population}}</li>
<li>Area Sq. Mile: {{AreaSqMile}}</li>
<li>Population Density per sq. Mile: {{PopulationDensity}}</li>
<li>NetMigration: {{NetMigration}}</li>
<li>GDP $ per capita: {{GDP}}</li>
<li>Phones per 1000: {{Phones}}</li>
<li>Arable %: {{Arable}}</li>
<li>Climate: {{Climate}}</li>
<li>Birthrate: {{Birthrate}}</li>
<li>Deathrate: {{Deathrate}}</li>
<li>Agriculture: {{Agriculture}}</li>
<li>Industry: {{Industry}}</li>
<li>Service: {{Service}}</li>
</ul>
</div>
</div>
</div>
</div>
Javascript:
Template7.registerHelper('json_stringify', function (context) {
return JSON.stringify(context);
});
var myApp = new Framework7({
animateNavBackIcon: true,
// Enable templates auto precompilation
precompileTemplates: true,
// Enabled pages rendering using Template7
template7Pages: true
// Specify Template7 data for pages
});
// Export selectors engine
var $$ = Dom7;
function getCountries() {
$$.getJSON('../countries.json', function (json) {
myApp.template7Data.countrylist = json ;
});
};
getCountries();
// Add main View
var mainView = myApp.addView('.view-main', {
// Enable dynamic Navbar
dynamicNavbar: true,
});

Compile error for Angular JS with js/ajax.js and index.html

When I compile my code there is an error with angular.
The code is written from a book, and it works when I load the page using the code at the end of the question.
There seems to be two issues from what I have read. Where do I put the $scope and $compile code?
Mostly, the question is how do I load a document ready trigger with a button for angular JS?
Or should I always load the angular js and hide the code?
<div id="myTabs">
<div class="menu">
<ul class= "tabs">
<li >LIST</li>
<li >GRID</li>
<li >GRID</li>
</ul>
</div>
<div class="container" style="margin-top:100px">
<div id="a">
</div>
<div id="b">
<ul class="gallery-items" id="grid">
</ul>
</div>
<div id="c" >
<div ng-controller="myController">
<div ng-repeat="item in items">
<img ng-src="{{item.img}}" />
{{item.description}}<br>
Rating: {{item.rating}} stars<br>
<span ng-repeat="idx in stars"
ng-class=
"{true: 'star', false: 'empty'}[idx <= item.rating]"
ng-click="adjustRating(item, idx)">
</span>
<hr>
</div>
</div>
</div>
</div>
ajax.js has a function that calls the #c tab to load
$(document).ready(function(){
$('#listC').click(function(){
angular.module('myApp', [])
.controller('myController', ['$scope', function($scope) {
$scope.stars = [1,2,3,4,5];
$scope.items = [100];
$.ajax({
type:'post',
url:'changeView.php',
data:{action: 'getGrid'},
success:function(data){
var data = JSON.parse(data);
for (i=0;i<data.length;i++) {
var imageLI = makeImage(data[i]['imageID'], data[i]['name'], data[i]['desc']);
$scope.items[i] = imageLI;
}
}
});
console.log($scope.items);
$scope.adjustRating = function(item, value){
item.rating = value;
};
}]);
});
$('#listC').trigger('click');
function makeImage(ID, name, description){
var image = {
description: description,
img: '../uploads/'+name,
rating: 4
}
return image;

Angular view is not being updated

Does anyone know where I am going wrong? I am trying to load up a new view on a section of the page in AngularJS.
I have the following code within sidebar.html:
<ul>
<li><a ui-sref="MainPage({themeName:'theme1'})">Theme 1</a></li>
<li><a ui-sref="MainPage({themeName:'theme2'})">Theme 2</a></li>
</ul>
Then my module.js file which contains all my routes looks like this:
var main = {
name: "MainPage",
url: "/:themeName",
views: {
"mainContent": {
controller: "MainPageController",
templateUrl: function($stateParams){
var url = 'modules/main/template/' + $stateParams.themeName + '/home.html';
console.log(url);
return url;
},
},
"sidebar":{
templateUrl: "modules/main/template/sidebar.html",
controller: "SidebarController"
}
}
};
$stateProvider.state(main);
When I look at the console I can see that the urlbeing returned is correct but my view isn't updated on the screen.
Would anyone be able to point out where I'm going wrong, I'm not seeing any errors in my console and the application is loading fine.
Any help is much appreciated
The views I'm trying to load reside in modules/main/template/theme1/home.html and modules/main/template/theme2/home.html
the HTML for these views are as follows:
/theme1/home.html
<h1>This is theme 1</h1>
/theme2/home.html
<h1>This is theme 2</h1>
This is where I am using ui-view:
<body>
<!-- SIDEBAR -->
<div id="sidebar" class="col-md-2 animated fadeIn" ui-view="sidebar" ng-cloak>
<div ng-include="modules/main/template/sidebar.html"></div>
</div>
<!-- HEADER -->
<div id="header" class='col-md-10'>
<img src='assets/images/logo.png' alt='' width='270' />
<ul class="breadcrumb">
<li class="active">{{ 'MAIN.NAVIGATION.LINK1' | translate }}</li>
<li>{{ 'MAIN.NAVIGATION.LINK2' | translate }}</li>
<li><a>{{ 'MAIN.NAVIGATION.LINK3' | translate }}</a></li>
</ul>
</div>
<!-- MAIN CONTENT -->
<div id="main-container" class="col-md-10 animated fadeIn" ui-view="mainContent" ng-cloak></div>
<div id="footer" class="col-md-12"><p>This is a test site</p></div>
<!-- SCRIPTS -->
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false"></script>
<script src="lib/all.js"></script>
<script src="modules/app.js"></script>
</body>
Ok, so we've established that it looks like a routing problem on NodeJS. I am using gulpJS to start up my server like this:
gulp.task('server', function() {
gulp.src([DIRDEST])
.pipe(webserver({
fallback : 'index.html',
livereload : false,
open : false,
directoryListing: {
enable: true,
path: 'target/index.html'
}
}));
});
Make sure you have defined ui-view where you want to render the template:
<div ui-view="mainContent"></div>
Update
I guess, you should use the template instead of templateUrl:
template: function($stateParams){
var url = 'modules/main/template/' + $stateParams.themeName + '/home.html';
console.log(url);
return url;
}
Tip
Also, if you want to re-execute your MainPageController on view change (since both the states are same, just the parameter is changing) then you have to use ui-sref-opts:
<ul>
<li><a ui-sref="MainPage({themeName:'theme1'})" ui-sref-opts="{reload: true}">Theme 1</a></li>
<li><a ui-sref="MainPage({themeName:'theme2'})" ui-sref-opts="{reload: true}">Theme 2</a></li>
</ul>

How to implement multiple ng controllers with http calls in AngularJS?

In an app / on a page I have several places, where data should be loaded asynchronously. E.g.: a DIV with images and another one with cateories (for the navi). I implement it like this:
app.js
(function() {
var app = angular.module('portfolio', []);
app.controller('ProjectsListController', ['$http', function($http) {
var projectsList = this;
projectsList.projectsListData = [];
$http.get(config['api_server_url'] + '/projects').success(function(data) {
projectsList.projectsListData = data;
});
}]);
app.controller('NaviCategoriesController', ['$http', function($http) {
var categoriesList = this;
categoriesList.categoriesListData = [];
$http.get(config['api_server_url'] + '/categories').success(function(data) {
categoriesList.categoriesListData = data;
});
}]);
})();
list.phtml
<!-- projects -->
<div id="projects" ng-app="portfolio">
<div id="projectsList" ng-controller="ProjectsListController as projectsList">
<div class="projectItem" ng-repeat="projectItem in projectsList.projectsListData._embedded.projects">
<div class="project-image">
<img
ng-src="{{projectItem._embedded.images[0].src}}"
title="{{projectItem.title}} - {{projectItem._embedded.images[0].title}}"
alt="{{projectItem.title}} - {{projectItem._embedded.images[0].title}}"
/>
</div>
</div>
</div>
</div>
navi-categories .phtml
<!-- navi-categories -->
<div id="navi-categories" ng-app="portfolio">
<ul id="categoriesList" ng-controller="NaviCategoriesController as categoriesList">
<li class="categoryItem" ng-repeat="categoryItem in categoriesList.categoriesListData._embedded.categories">
{{categoryItem.short_name}}
</li>
</ul>
</div>
Every of these two blocks works fine. But I cannot use both at the same time. That means: The categories only get displayed, when the projects/images list is commented out (in the HTML) -- when I activate the list, the categories' HTTP request doesn't get sent anymore.
What am I doing wrong? How to implement this correctly?
Well... the problem is that they both have the ng-app="portfolio". You should wrap both components in one div with the ng-app="portfolio" attribute:
<div id="app" ng-app="portfolio">
<div id="projects">
<div id="projectsList" ng-controller="ProjectsListController as projectsList">
<div class="projectItem" ng-repeat="projectItem in projectsList.projectsListData._embedded.projects">
<div class="project-image">
<img
ng-src="{{projectItem._embedded.images[0].src}}"
title="{{projectItem.title}} - {{projectItem._embedded.images[0].title}}"
alt="{{projectItem.title}} - {{projectItem._embedded.images[0].title}}"
/>
</div>
</div>
</div>
</div>
<div id="navi-categories">
<ul id="categoriesList" ng-controller="NaviCategoriesController as categoriesList">
<li class="categoryItem" ng-repeat="categoryItem in categoriesList.categoriesListData._embedded.categories">
{{categoryItem.short_name}}
</li>
</ul>
</div>
</div>

Categories