How can I get rid of the curly bracket from angularjs output and to clear the screen after the display - node.js

I thought I finally understand ng-repeat but now I do not know why the output include the curly bracket and how do I clear the screen after reading the output.
Here is part of the output
{"title":"NFL Draft 2020: Over 50 prospects will take part in 'virtual' interviews to air during the event, per report - CBS Sports"}
{"title":"Illinois governor says feds sent wrong type of protective medical masks - CNN"}
but what I really want is just the following without the curly bracket, the word title and the double quotes.
NFL Draft 2020: Over 50 prospects will take part in 'virtual' interviews to air during the event, per report - CBS Sports
and after displaying the list of headlines, I want to clear the screen ( as in "cls" in the command prompt)
my angularjs code is this
$http.post('/disdata', " ").then(function(response) {
$scope.answer = response.data;
var titles = [];
for (var i = 0; i < $scope.answer.length; i++) {
titles.push ({
title: $scope.answer[i].title
});
};
$scope.titles = titles;
console.log($scope.titles);
My html is
<div ng-repeat="(key, value) in titles">
{{value}}
</div>

The syntax you are using is usually used to iterate over properties in an object. Since you already have an array, you can normally iterate over it and display the title value.
angular.module('app', []).controller('Ctrl', ['$scope', ($scope) => {
$scope.titles = [{
"title": "NFL Draft 2020: Over 50 prospects will take part in 'virtual' interviews to air during the event, per report - CBS Sports"
},
{
"title": "Illinois governor says feds sent wrong type of protective medical masks - CNN"
}
];
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<body ng-app="app" ng-controller="Ctrl">
<div ng-repeat="title in titles">
{{title.title}}
</div>
</body>

Related

Show attribute in search result template only if contains highlighted matches

I have several attributes set as attributesToRetrieve. In my template though only some of these are displayed. Roughly something like this:
<div id="results">
<div>{{_highlightResult.attr1.value}}</div>
<div>{{_highlightResult.attr2.value}}</div>
<div>{{_highlightResult.attr3.value}}</div>
</div>
This way the attributes will be rendered in any case, and highlighted if they contain a matched word.
Now I'd like to add another section where all other attributes can be displayed but only if they contain a matched word to be highlighted, something like:
<div id="results">
<div>{{_highlightResult.attr_1.value}}</div>
<div>{{_highlightResult.attr_2.value}}</div>
<div>{{_highlightResult.attr_3.value}}</div>
<!--
The following div will be rendered and displayed only
if any of these special attributes contain an highlighted word.
Only an attribute containing a matched word will be displayed
-->
<div class="other-attr">
{{_highlightResult.Long_SpecialAttr_1.value}}
{{_highlightResult.SpecialAttr_2.value}}
{{_highlightResult.SpecialAttr_3.value}}
</div>
</div>
As mentioned in the comment, this section will be rendered and displayed only if any of these special attributes contain an highlighted word, also only an attribute containing a matched word will be displayed.
Plus as you can see there is a Long_SpecialAttr_1, it's a long text attribute, wich I'd like to have it displayed as a snippeted attribute.
To give a better idea (maybe) what I'm trying to achieve for this additional section is something like the one Google display below every search results, a sort of text blob text with ellipsis containing the marked words of these attributes.
Is this possible? I'm using algolia instasearch.js, thank you!
UPDATE
Thanks to #Jerska for his answer, unfortunately a small bit of code wasn't working in my case, specifically:
['highlight', 'snippet'].forEach(function (type) {
data['_' + type + 'Result'].forEach(function (elt) {
elt.display = elt.matchLevel !== 'none';
});
});
giving me an error in the console stating data._snippetResult.forEach() is undefined. So I modified that bit with this:
for(var el in d._snippetResult)
{
// create new property with bool value, true if not "none"
d._snippetResult[el].display = d._snippetResult[el].matchLevel !== 'none';
};
First of all, just to clarify the settings of your index before going forward, Algolia also highlights the attributes in attributesToSnippet.
Also, to have an ellipsis on the attributes you snippet, you can set snippetEllipsisText.
So you might want to use these settings in your index:
attributesToHighlight: ['attr_1', 'attr_2', 'attr_3', 'SpecialAttr_2', 'SpecialAttr_3'],
attributesToSnippet: ['Long_SpecialAttr_1:3'], // Snippet should contain max 3 words
snippetEllipsisText: '…' // This is the utf-8 "Horizontal ellipsis" character
On the front-end side, in instantsearch.js you can use the transformData parameter on almost any widget to be able to access and/or modify the data passed to the template.
In this specific example, we'll want to have a look at transformData.item on the hits widget.
The first step would be to log the data:
search.addWidget(
instantsearch.widgets.hits({
transformData: {
item: function (data) {
console.log(data);
return data;
}
}
})
);
This will allow you to see that kind of response:
_highlightResult: {
attr_1: {
value: 'lorem <em>ipsum</em> dolor <em>sit</em>',
matchLevel: 'full',
matchedWords: ['ipsum', 'sit']
},
attr_2: {
value: 'lorem <em>ipsum</em> dolor',
matchLevel: 'partial',
matchedWords: ['ipsum']
},
attr_3: {
value: 'lorem',
matchLevel: 'none',
matchedWords: []
},
// ...
},
_snippetResult: {
Long_SpecialAttr_1: {
value: 'lorem <em>ipsum</em> dolor …', // Let's assume Long_SpecialAttr_1 was equal to 'lorem ipsum dolor sit'
matchLevel: 'full'
}
}
Unfortunately here, the API is a bit inconsistent since as you can see, snippeted attributes don't have the matchedWords attribute that highlighted attributes have. You can choose to set it both in attributesToSnippet and attributesToHighlight if you really want the info.
However, for your use-case, we just need matchLevel. What we want, is to display elements only if matchLevel !== 'none'. Unfortunately, Hogan.js, the underlying template engine of instantsearch.js doesn't allow for much flexibility, so you can't just put this comparison in your template.
A solution could be to precompute these conditions inside the transformData:
transformData: {
item: function (data) {
['highlight', 'snippet'].forEach(function (type) {
var group = data['_' + type + 'Result'];
for (var attr in group) {
if (!group.hasOwnProperty(attr)) continue;
var elt = group[attr];
elt.display = elt.matchLevel !== 'none';
};
});
data.displaySpecial = false ||
data._snippetResult.Long_SpecialAttr_1.display ||
data._highlightResult.SpecialAttr_2.display ||
data._highlightResult.SpecialAttr_3.display;
return data;
}
}
And then use these new attributes in your template:
<div id="results">
<div>{{{_highlightResult.attr_1.value}}}</div>
<div>{{{_highlightResult.attr_2.value}}}</div>
<div>{{{_highlightResult.attr_3.value}}}</div>
<!--
The following div will be rendered and displayed only
if any of these special attributes contain an highlighted word.
Only an attribute containing a matched word will be displayed
-->
{{#displaySpecial}}
<div class="other-attr">
{{#_snippetResult.Long_SpecialAttr_1.display}}
{{{_highlightResult.Long_SpecialAttr_1.value}}}
{{/_snippetResult.Long_SpecialAttr_1.display}}
{{#_highlightResult.SpecialAttr_2.display}}
{{{_highlightResult.SpecialAttr_2.value}}}
{{/_highlightResult.SpecialAttr_2.display}}
{{#_highlightResult.SpecialAttr_3.display}}
{{{_highlightResult.SpecialAttr_3.value}}}
{{/_highlightResult.SpecialAttr_3.display}}
</div>
{{#displaySpecial}}
</div>
(By the way, to render HTML, you should use {{{ ... }}} instead of {{...}}, I've replaced them here)

How to short data form webservice with winjs?

I have problems with grouped ListView or short group. i get data from webservice already but i don't know how to binding data to html template user behind code.
HTML:
<div class="listLayoutTopHeaderTemplate" data-win-control="WinJS.Binding.Template">
<div class="listLayoutTopHeaderTemplateRoot">
<div data-win-bind="innerHTML: title"></div>
</div>
</div>
<div id="listView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemTemplate: select('.smallListIconTextTemplate'),
groupHeaderTemplate: select('.listLayoutTopHeaderTemplate'),
layout: { type: WinJS.UI.ListLayout, groupHeaderPosition: 'top'}}">
</div>
Winjs:
var myData = new WinJS.Binding.List([
{ title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "/images/fruits/60Banana.png" },
{ title: "Lavish Lemon Ice", text: "Sorbet", picture: "/images/fruits/60Lemon.png" },
]);
var grouped = myData.createGrouped(function (item) {
return item.title.toUpperCase().charAt(0);
}, function (item) {
return {
title: item.title.toUpperCase().charAt(0)
};
}, function (left, right) {
return left.charCodeAt(0) - right.charCodeAt(0);
});
listView.winControl.groupDataSource = grouped.groupDataSource;
You need to take whatever data you get from the web service, which is presumably JSON, and transfer that data into a WinJS.Binding.List that you can use as the ListView's data source. In the code you show, this is the myData variable, which you can initialize as empty (instead of using the shown data that's just from a sample). Iterating over your web service data, call the List's add method for each item. If the JSON you want to render already has an array, then parse that out and pass the array to the WinJS.Binding.List constructor.
Either way, you end up with a WinJS.Binding.List populated with the data from the service, which you can then give to the ListView for rendering.
There are fancier ways of working with data sources if you want to avoid obtaining the web service data and copying it all into an in-memory WinJS.Binding.List. For this I'll refer you to Chapter 7 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, specifically the section "Collection Control Data Sources" on page 380.

SimpleFlip View first image coming back from json doesn't show immediately in winjs

I have a simple WINJS flipview. There are 5 images loaded from an external json file. All the images load immediately except the first one, and second question is there a simple command to automatically set these to rotate?
So we are using a single page model app. This is a little promo slider I wanted to put in on one page and rotate. I've tried everything including just the demo, but the first item always comes back undefined.
I even tried removing the first image, but the first item always comes back undefined. I've spent a few days on this now with not much luck.
<div id="promoTemplate" data-win-control="WinJS.Binding.Template" style="display: none" >
<div class="overlaidItemTemplate">
<img class="image" data-win-bind="src: picture" />
<div class="overlay">
<h2 class="ItemTitle" data-win-bind="innerText: title"></h2>
</div>
</div>
</div>
<div id="promoFlipView" class="flipView" data-win-control="WinJS.UI.FlipView" data-win-options="{ itemDataSource: ActivityPromoData.bindingList.dataSource, itemTemplate: select('#promoTemplate') }">
</div>
This is connected to the demo example flipview Data
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
(function () {
"use strict";
// This is an array that will be used to drive the FlipView in several
// scenarios. The array contains objects with the following attributes:
//
// type - There are two types that are used:
//
// item -
// The type for simple items. It informs the custom
// renderer that their is a title and picture that needs
// to be rendered.
//
// contentsArray -
// This is used for creating a table of contents. It
// informs the renderer that an array of data is present
// for use in constructing the Table of Contents.
//
// title - The title of a photo to be displayed.
//
// picture - The location of the photo to be displayed.
var array = [
{ type: "item", title: "Cliff", picture: "images/Cliff.jpg" },
{ type: "item", title: "Grapes", picture: "images/Grapes.jpg" },
{ type: "item", title: "Rainier", picture: "images/Rainier.jpg" },
{ type: "item", title: "Sunset", picture: "images/Sunset.jpg" },
{ type: "item", title: "Valley", picture: "images/Valley.jpg" }
];
var bindingList = new WinJS.Binding.List(array);
WinJS.Namespace.define("ActivityPromoData", {
bindingList: bindingList,
array: array
});
var e = ActivityPromoData.bindingList.dataSource;
})();
The original question above here is the FIRST IMAGE BUG FIX: adding this to the onready. This works providing there is no custom animations.
var proxyObject;
proxyObject = new WinJS.Binding.as({
itemTemplate: tutorialTemplate,
customAnimations: false
});
tutorialFlipView.winControl.itemTemplate = tutorialTemplate;
There is not builtin command to rotate. setInternval() can be used for this.
var timerId = setInternal(function()
{
if (flipview.winControl.count - 1 == flipview.winControl.currentPage)
flipview.winControl.currentPage = 0;
else
flipview.winControl.next();
}, slideshowInternal);
// to stop slideshow
clearInterval(timerId);
This assumes no complex transition between pages (for example: KENBURNS). if that is required, it is more involved problem, and it is good to consider using some existing javascript sdk on web and integrate in a custom winjs control. flipview control did not work well when integrating custom page transition animations.
regards image not loading - html/js code snippet will help answer it.
if <img> tag is used and bound it to the http image url, image is not guaranteed to be loaded when flipview shows the page. If number of images are handful, it might be better to download them using winjs.xhr to ensure that they are in cache and then, load the flipview.

Filtering a page with infinite scroll in AngularJS

I implemented an infinite scrolling feature on my AngularJS + Node.js app.
It is based on this JSfiddle and works the same way: http://jsfiddle.net/vojtajina/U7Bz9/
HTML:
<div id="fixed" when-scrolled="loadMore()">
<ul>
<li ng-repeat="i in items">{{i.id}}</li>
</ul>
</div>​
Javascript:
function Main($scope) {
$scope.items = [];
var counter = 0;
$scope.loadMore = function() {
for (var i = 0; i < 5; i++) {
$scope.items.push({id: counter});
counter += 10;
}
};
$scope.loadMore();
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
​
My reasons for implementing the infinite scroll is in order to save my users bandwidth by not loading all 1000 results and their corresponding images unless the user wants to see all of it.
However when searching within the results using an AngularJS filter I am encountering problems, because of course not all the results are there (unless the user has scrolled to the very bottom) so the search will only return a fraction of the required results.
I then removed the infinite scrolling feature in order to have the search function work properly but this provided new issues on chrome (not on Firefox though) when I open the page the browser starts loading images from the top. If I then filter the results by searching for something starting with "z" (at the very bottom of the results) Firefox switches focus and loads the results starting with "z" first (as they are then the only ones being displayed). However chrome continues loading through the list and thus the user will only see the results of their search (for "z") once all the images in the app have been loaded.
I am looking for a way to have angular provide both the infinite scroll and a proper search filter on the results at the same time. If this is not possible then a way to make chrome load the visible images first.
I am currently trying some weird stuff with a bunch of different arrays in the scope but I haven't had any luck so far.
Since several people here had a similar issue and I was struggling myself, I took the time to create a fiddle that works (for my case).
https://jsfiddle.net/lisapfisterer/Lu4sbxps/
The main idea is to dynamically extend the limitTo value using infinite-scroll's function call
<div infinite-scroll="loadMore()" infinite-scroll-distance="20">
<tr data-ng-repeat="logEvent in logEventFilter = (logEvents | filter:searchTerm | limitTo:numberToDisplay) track by $index">
<td> {{$index}} </td>
<td> {{logEvent.name}} </td>
<td> {{numberToDisplay}} </td>
</tr>
</div>
loadMore just increases the limit
$scope.loadMore = function() {
if ($scope.numberToDisplay + 5 < $scope.logEvents.length) {
$scope.numberToDisplay += 5;
} else {
$scope.numberToDisplay = $scope.logEvents.length;
}
};
What you want to do isn't "impossible" but it's certainly going to be a little complicated.
Have your server do all of the "filtering" to ensure that the paginated values returned are the proper values for the filter(s).
When the server returns the results, render all of the HTML to the screen except the src attributes of image tags. This way none of the images will begin loading yet.
Scroll to the proper "page".
Make sure all of the heights prior to the images being loaded are the same, now do some JS magic to figure out which ones are visible.
Set the src attribute of the visible images only, Subscribe to their "load" events and create a $q promise that is complete once all loads are complete.
after that promise completes, set the rest of the image src attributes so the remainder of the images will load.

Slickgrid basic example - In node/express, only first row of data loads but is not visible

I'm trying to run a barely modified version of SlickGrid's example1-simple.html. I'm on Mac OS X Lion, seeing the same behavior with Chrome/Safari/Firefox. Once I have all of the CSS/JS dependencies in place I can directly load the example HTML page (and my slightly modified version) without any trouble. However when I try to serve basically the same page with node/express/jade (using res.render()), the header (column names) row loads, and looking through the rendered HTML I can see that the first row of my data loads but I don't see it in the browser (I'm trying to load 10 rows of data). All of the relevant CSS/JS files seem to be loading properly and I don't see any errors either in the browser console or my node console. I've copied the entirety of the grid-canvas div below.
<div class="grid-canvas" style="height: 250px; width: 240px; ">
<div class="ui-widget-content slick-row even" row="0" style="top:0px">
<div class="slick-cell l0 r0">Battery test #1.csv</div>
<div class="slick-cell l1 r1">1024</div>
<div class="slick-cell l2 r2">1335237255112</div>
</div>
</div>
I'm pretty sure the JavaScript is all right, as it pretty much exactly matches the example HTML page and runs fine when I substitute it in the example1-simple.html document and access it directly. Also when rendering the page with express I can set a breakpoint in my browser at the new Slick.Grid() call and see that the 10-item array of row data is being sent, just not ultimately rendered.
I have basic old-school debug skills (once upon a time I did a lot of Windows programming in VB) but I'm very open to suggestions as to relevant debug tools and techniques I could bring to bear on this type of problem.
Here is my layout.jade:
!!!
html
head
title= title
link(rel="stylesheet", href="/stylesheets/slick.grid.css", type="text/css")
link(rel="stylesheet", href="/stylesheets/css/smoothness/jquery-ui-1.8.19.custom.css", type="text/css")
body!= body
And the jade page with my test code:
#content
#fileGrid
script(src="/javascripts/lib/jquery-1.7.2.js")
script(src="/javascripts/lib/jquery.event.drag-2.0.min.js")
script(src="/javascripts/lib/slick.core.js")
script(src="/javascripts/lib/slick.grid.js")
script
var grid;
var columns = [
{id: "fileName", name: "File Name", field: "fileName"},
{id: "fileSize", name: "File Size", field: "fileSize"},
{id: "lastUpdate", name: "Last Updated", field: "lastUpdate"} // use mtime
];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
$(function() {
var timeStamp = new Date();
var numRows = 10;
var data = [];
for (var i = 1 ; i <= numRows ; i++) {
data[i-1] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString()
}
};
grid = new Slick.Grid("#fileGrid", data, columns, options);
});
I figured it out. The problem was that I didn't specify a size for my #fileGrid div either in my jade/html or anywhere else in the css, and SlickGrids seemed to assume that the size was zero, and thus didn't render any rows. I changed the jade declaration to #fileGrid(style="width:500px; height:600px) and that fixed it. In retrospect that was really the only thing different between my jade page and the html example.
This looks fishy:
for (var i = 0 ; i <= numRows ; i++)
data[i-1] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString(),
};
Why data[i-1] and i <= numRows? You're trying to assign to data[-1] up until data[numRows], where you should assign to data[0] until data[numRows - 1].
On a side note, I would wrap that for loop in braces, even though it's not strictly necessary. Also, the trailing comma (on the last row) will break in Internet Explorer:
for (var i = 0 ; i < numRows ; i++) {
data[i] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString()
};
}
You also have a trailing comma in your columns assignment.
#BRValentine, you are right. Slickgrid grid container element must be given an height (width is optional - by default uses 100% available). If you do not provide an height to the container element, Slickgrid does not render the rows.
specifying the target grid div dimensions solved my problem. This is nowhere in instructions... I'm leaving it here so that others can find it.
<div id="classGrid" style="width:500px; height:600px;"></div>

Resources