Converting Handlebars expressing to lowercase and dashes - javascript

I am trying to reuse some of the data in my handlebars template on the front end.
To do this, I need to convert one of my expressions to lowercase, and use dashes instead of space. Is this easily done?
For example, in my JSON file I have the following:
var items = [{
id: '1',
title: "Item Number One",
},
{
id: '2',
title: "Item Number Two",
},
];
And on the Handbars template on the front-end I would like to do this:
<script type="text/x-handlebars" id="items">
<h1>
{{title}}
</h1>
<img src="{{unbound title}}.png" />
</script>
This will output the image page as <img src="Item Number One.png" /> which is no good. So is there an easy way of converting this to lowercase, and remove spaces?

Make a custom Handlebars Helper:
Handlebars.registerHelper("noSpaces", function(input) {
var output = input.toLowerCase();
return output.replace(" ", "");
});
And then call it as:
<img src="{{noSpaces title}}.png" />

You can map your items array and apply the template to that modified array like so:
var items = [
{
id: '1',
title: "Item Number One",
},
{
id: '2',
title: "Item Number Two",
},
];
items = items.map(function(el) {
el.title = el.title.toLowerCase().replace(/\s+/g, '-');
return el;
});
// items = [{id:"1",title:"item-number-one"},{id:"2",title:"item-number-two"}]
If you need to retain the original array just assign the mapped array to a different variable.

Related

mapping through array of objects and accessing object values

I am trying to pick a value from array of object and the values can only be from first object.
Firstly i have a array of objects like this:
items [
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
]
]
I am planning to display the title and img url on the page. I am using react and this is what i have tried so far:
items.map(item => {
//here i enter each array of the big array individually
//here i will enter first object of the give array
console.log('ITEM:', item[0])
})
in console.log i get this:
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
but I need to get each title and imgUrl so in my .map() I do this:
items.map(item => {
//here i enter each array of the big array individually
//here i will enter first object of the give array
console.log('ITEM:', item[0].title)
})
but I get this error:
TypeError: Cannot read property 'title' of undefined
I don't understand why I thought I would be able to use dot notation to access whatever key in the object you would like:
for more context:
<div>
{
items.map((item) => {
return <ul>
<li>
<div>
<p>{item[0].title}</p>
<img src={item[0].url}/>
</div>
</li>
</ul>
})
}
</div>
MY SOLUTION TO THE ABOVE PROBLEM
SO i have spend some time messing around with this issue and this works for me:
items.map((item) => {
return item.map ((x, index) => {
if (index === 0) {
return <ul>
<li>
<div>
<p>{ x.title }</p>
<img src={x.url}/>
</div>
</li>
</ul>
}
})
})
Still not sure why my initial idea item[0].title not work without having nested map() function
Try using quotes around the keys:
items [
[
{"id": 1, "title": "title1", "imgUrl": "https://someimage1"},
{"id": 2, "title": "title2", "imgUrl": "https://someimage2"}
]
]
[Update Based on OP updating Post:
Since map builds a new array, using it when you aren't using the returned array is an anti-pattern; use forEach or for-of instead.
Signs you shouldn't be using map:
A) You're not using the array it
returns, and/or
B) You're not returning a value from the callback.
Further reading on .map() via Mozilla
Furthermore nesting 2 loops in such a way will force twice as many iterations as it will still run length times.
If you need to access a sub element and know its index or key then use that value by:
array[index] or object.key and use a single map() (example in full snippet)
If you do actually need to loop over every element (due to some condition not expressed in your question) instead use:
.find() (single elements)
or .filter(elm => elm === 'someValue') (multiple elements)
or .reduce() (multiple elements) with mutating elements as you go
or even .forEach() (multi elements) with performing additional functions outside of array building (you will need to push to something like let loopArray = [])
Continue Original Answer]
Move the ul tag outside the map statement and change url to imgUrl.
Keep in mind if items is generated via async then you will also need to validate items before using map, something like:
{(items && items.length) && items.map(item=>{
/*code*/
)}
Working Snippet (without async check):
const SomeComponent = () => {
const items = [
[
{id: 1, title: "title1.1", imgUrl: "https://p7.hiclipart.com/preview/917/362/567/halloween-trick-or-treating-double-ninth-festival-illustration-halloween-design-elements.jpg"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1.2", imgUrl: "https://image.shutterstock.com/image-vector/silhouette-cat-dog-on-white-600w-671156590.jpg"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1.3", imgUrl: "https://jngnposwzs-flywheel.netdna-ssl.com/wp-content/uploads/2019/05/Transparent-OrangeWhiteCat-768x1030.png"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
]
];
return (
<div>
<ul>
{items.map((item,index) => {
return(
<li key={String(index)}>
<div>
<p>{item[0].title}</p>
<img style={{width:'10%'}} src={item.imgUrl} alt={item.title}/>
</div>
</li>
)
})}
</ul>
</div>
)
}
ReactDOM.render(
<SomeComponent />,
document.getElementById("react")
);
<div id='react'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Make a filter by words without hiding them

I currently have a code that allows me to filter a JSON object by both the name and the title. I have an array with a few words, I would like to make this same filter but without hiding the words, just mark them based on my array.
$scope.arrayFilter=["bad,bill,mikle,awesome,mosa"];
Is it possible to perform this filter by default when I start my application ?. Thank you very much.
https://jsfiddle.net/ryvu49jt/
$scope.data = [{
title: "Bad",
name: 'bill'
}, {
title: "Good",
name: 'Goe'
}, {
title: "Great",
name: 'Brad'
}, {
title: "Cool",
name: 'yan'
}, {
title: "Excellent",
name: 'mikle'
}, {
title: "Awesome",
name: 'mosa'
}, {
title: "Horrible",
name: 'morteza'
} ]
}).filter('highlight', function($sce) {
return function(text, phrase) {
if (phrase) text = text.replace(new RegExp('(' + phrase + ')', 'gi'),
'<span class="highlighted">$1</span>')
return $sce.trustAsHtml(text)
}
})
Currently if I type a word in a text field, it is filtered by both the name and the title of my JSON object. Are marked but disappear. I just want them to be marked, not disappear.
thanks a lot!
The problem is because you also filtering the <li> just delete the filter
and also concatenate the the item.name
like this example.
<div class="container" >
<input type="text" placeholder="Search" ng-model="search">
<ul>
<li ng-repeat="item in data ">
<span ng-bind-html="item.title +' '+ item.name | highlight:search"></span>
</li>
</ul>
</div>
it would search only on the bind-html
Here is a working plnkr where search over the elements
EXAMPLE
just remove the filter from your ng-repeat and leave the other code same as it is
<li ng-repeat="item in data">

Angularjs splice array of objects inside of an object always removes last object

I have an object which contains an array of objects called "blocks":
$scope.microsite = {
images: [
{url: "https://unsplash.it/800/400/?image=20"},
{url: "https://unsplash.it/800/400/?image=15"},
{url: "https://unsplash.it/800/400/?image=52"}
],
blocks: []
};
When I add stuff to this array, it behaves perfectly normally:
$scope.addElement = function(a){
if(a=='heroslider'){
var data = {
slides: [
{
id:0,
image:0,
title: "Title",
desc: "Description",
},
{
id:1,
image:1,
title: "Title",
desc: "Description",
},
{
id:2,
image:2,
title: "Title",
desc: "Description",
}
]
};
} else if(a=='threecol'){
var data = {
columns: [
{
title: "Column one",
text: "This is a column for features",
},
{
title: "Column two",
text: "This is a column for features",
}
]
};
}
var element = {
template: a,
data: data
};
$scope.microsite.blocks.push(element);
}
However when I try to remove an object from the array by calling this function on ng-click and passing in the object from an ng-repeat...
$scope.removeElement = function(element){
var x = $scope.microsite.blocks.indexOf(element);
console.log($scope.microsite.blocks[x]);
console.log(x);
$scope.microsite.blocks.splice(x, 1);
}
I am able to get both the correct object and the correct index in my console, but when it goes to splice the array, the last object is always being deleted which is very strange as this should only be happening when the index I'm trying to delete doesn't exist (and therefore would equal -1)
Any ideas why this could be happening?
EDIT: I have also tried using ng-click="microsite.blocks.splice($index, 1)" directly in the element, as well as passing the $index into the function instead of the element. In all cases, the correct index is found, but the result is still the same, only the last entry is ever deleted.
Turns out this was an error with "track by $index" in Angular. After removing "track by $index" from my ng-repeat, splice() functioned normally.

Filter an array of objects by a property containing a substring in AngularJS

Let's say i have an array like:
array = [{
title: "foo1",
content: "bar1"
},{
title: "foo2",
content: "bar2"
},{
title: "foo3",
content: "bar3"
}];
Now i want to filter this array to have the objects that their title contains a character like '3'.
So now my filtered array should be
filteredArray = [{
title:"foo3",
content: "bar3"
}];
I've tried
filteredArray = $filter('filter')(array, {
title: "foo3"
});
But the problem with this is that title needs to be exactly "foo3". if i put "3" it won't filter that because it doesn't check if it contains it, it looks for an exact match.
Any ideas how to achieve this?
The filter filter (yeah, I know) does a contains filtering...
I pasted your code (working) into PLNKR and filtered on 3 and got back the title: 'foo3' element
array = [{
title: "foo1",
content: "bar1"
},{
title: "foo2",
content: "bar2"
},{
title: "foo3",
content: "bar3"
}];
$scope.filteredData = $filter('filter')(array, {
title: "3",
});
You would need to write your own filter. Check this answer on how to achieve what you want.

Handlebars JS (or EJS) templating: Divide list into categories

I want to create an unordered list based on an array of objects using Handlebars JS.
The objects take the following form:
{ name: 'NameOne', id: 001, category: 2 }, { name: 'NameTwo', id: 002, category: 1 }, { name: 'Name Three', id: 003, category: 1 }, { name: 'Name Four', id: 004, category: 3 }...
At the moment I have a template with an each function which compiles an UL with each object making a new list item, as so:
{{#each this}}
<li>{{ name }}</li>
{{/each}}
What I would like to do is find the simplest way to divide the list depending on categories, so I could end up with something like this...
Category 1
NameTwo
NameThree
Category 2
Name One
Category 3
Name Four
If I can do this only with the template, that would be perfect, but I can also change the JS which compiles the template if needed. I would also be open for using EJS templates if that is a better solution. I have thought of a couple of solutions, but all seem very over-complicated and don't work very well, but surely this can be quite a common problem and there must be good solutions.
Many thanks.
Hi when I'm solving problems like this, I tend to manipulate the data though JavaScript to keep the template clean.
How about trying something like this:
HTML
{{#each list}}
<ul>
<li>
Category {{#key}}
<ul>
{{#each this}}
<li>{{name}}</li>
{{/each}}
</ul>
</li>
</ul>
{{/each}}
JS
var rawData = [
{ name: 'NameOne', id: 001, category: 2 },
{ name: 'NameTwo', id: 002, category: 1 },
{ name: 'Name Three', id: 003, category: 1 },
{ name: 'Name Four', id: 004, category: 3 }
];
var data = {
list:{
}
};
for (var i = 0; i < rawData.length ; i++) {
var a = rawData[i];
var cat = a.category;
if(typeof data['list'][cat] === 'undefined'){
data['list'][cat] = [];
}
data['list'][cat].push(a);
}
Also here is a jsfiddle

Categories