AngularJS ngRepeat replacing bindings with nothing - javascript

I have an AngularJS app that is searching through a list of services, which then displays the results below the search bar using ngRepeat. The right amount of search results are being displayed, but the bindings are being filled with nothing, leaving the anchor tags empty.
Here is the markup being generated:
<h4 class="media-heading result-heading"></h4>
<p class="result-abstract"></p>
<p></p>
<p></p>
<div class="row">
<div class="col-all-12 result-contact">
<div class="">
<span class="icon-office"></span>
</div>
<div class="">
<span class="icon-phone"></span>
</div>
</div>
</div>
I have the exact same code running on my local machine, it works as expected, and the versions of AngularJS are the same, so I have no clue what is going on. Has anyone experienced this before, and how have hey resolved it?
No errors are thrown, the data is being retrieved via AJAX from a json file.
Here is the template with the ngRepeats in it:
<div class="media-body search-result" ng-repeat="service in services | filter: query | filterTags: allTags | orderBy: serviceOrder:direction | hasValue | startFrom:(currentPage - 1) * servicesPerPage | limitTo:servicesPerPage">
<h4 class="media-heading result-heading">{{service.serviceName}}</h4>
<p class="result-abstract">{{service.abstract}}</p>
</p>
</p>
<div class="row">
<div class="col-all-12 result-contact">
<div class="">
<span class="icon-office"></span> {{service.office}}
</div>
<div class="">
<span class="icon-phone"></span> {{service.contact}}
</div>
</div>
</div>
</div>
The code behind this isn't really all that important since it is an exact copy of everything from one page to another, and it works on the previous page. There are no differences in the HTML, CSS or Javascript, other than the fact that the markup on the new page is filled in with blanks.
I have already checked to make sure that the json data is being pulled in correctly, and that the versions of AngularJS are the same (Version is 1.2.21).
Here are the custom filters:
serviceSearchbar.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
serviceSearchbar.filter('hasValue', function() {
return function (input) {
if (angular.element('#servicesQuery').val()) {
return input;
}
return [];
}
});
serviceSearchbar.filter("isSelected", function () {
return function (input) {
var response = {};
if (input !== undefined) {
Object.keys(input).forEach(function (tag) {
if (input[tag] !== false) {
response[tag] = true;
}
});
}
return response;
}
});
serviceSearchbar.filter("filterTags", ["$filter", function ($filter) {
return function (input, tags) {
var selectedTags = [],
response = input,
found = false,
numSelectedTags;
if (input === undefined) {
return input;
}
if (tags !== undefined) {
Object.keys(tags).forEach(function (tag) {
if (tags[tag] !== false) {
selectedTags.push(tag);
}
});
}
if (selectedTags.length > 0) {
response = $filter("filter")(response, function(value) {
var found = false;
selectedTags.forEach(function(tag) {
if (value.tags.indexOf(tag) !== -1) {
found = true;
return true;
}
});
return found;
});
}
return response;
}
}]);
And here is an example of the data that is being used:
{
"services": [
{
"serviceName": "Department of Art",
"sponsor": "Department of Art",
"shortName": "art-dep",
"url": "http://www.byui.edu/art/",
"contactName": "Contact Name",
"office": "Spori some room",
"contact": "(123) 456-7890",
"mapUrl": "http://www.byui.edu/maps#SPO",
"tags": [
"art",
"design",
"art department",
"art program"
],
"abstract": "The BYU-Idaho Department of Art provides an aesthetic, conceptual, and technical foundation in the visual arts for students who possess a wide range of interests, experiences, and abilities."
},
{
"serviceName": "College of Performing and Visual Arts",
"sponsor": "College of Performing and Visual Arts",
"shortName": "performing-visual-arts",
"url": "http://www.byui.edu/performing-visual-arts",
"contactName": "Contact Name",
"office": "Spori some room",
"contact": "(123) 456-7890",
"mapUrl": "http://www.byui.edu/maps#SPO",
"tags": [
"art",
"dance",
"music",
"spori",
"theater"
],
"abstract": "The College of Performing and Visual Arts provide an important means of communication where thoughts, creativity, and expression can be directed to ennoble, uplift, inspire..."
},
{
"serviceName": "Spori Art Gallery",
"sponsor": "Department of Art",
"shortName": "spo-gal",
"url": "http://www.byui.edu/spori-gallery/",
"contactName": "Contact Name",
"office": "Spori some room",
"contact": "(123) 456-7890",
"mapUrl": "http://www.byui.edu/maps#SPO",
"tags": [
"art",
"gallery",
"spori"
],
"abstract": "Jacob Spori Art Gallery will show works of current BYU-Idaho Visual Arts students, selected by the art department faculty."
}
]
}

Related

VueJS: JSON objects are not showing in my code

I have API that stores JSON data as shown in JSON body below... I wanted to show the data amount stored in installments but it didn't work good because its showing me each amount value two times and I couldn't figure out the problem here.
{
"response": [{
"floors": [{
"flats": [{
"status": "sold",
"price": "150000",
"currency": "USD",
"end_date": "Not Set",
"buyer": "ella",
"buyer_phone_number": "002822128",
"receipt_number_field": "553108012022",
"size_unit": "M",
"_id": "61d9b61397e87e39832a5abb",
"flat_number": 1,
"description": "This is a newly created flat.",
"city": "NY",
"payment": {
"installment_payment": {
"installments": [{
"amount": "1344",
"date": "2022-01-13",
"is_paid": false
},
{
"amount": "444",
"date": "2022-01-24",
"is_paid": false
},
{
"amount": "44444",
"date": "2022-01-17",
"is_paid": false
}
],
"remaining": "150000"
},
"paid_amount": "1234"
},
"floor": "61d9b61397e87e39832a5aba",
"building": "61d9b61397e87e39832a5ab9",
"size": "176.25",
"directions": " south",
"createdAt": "2022-01-08T16:04:43.557Z",
"updatedAt": "2022-01-08T16:22:29.220Z",
"__v": 0
},
my code:
<div v-for="(flat,index) in Flats" :key="index">
<div v-for="(find,indexT) in flat.payment" :key="indexT" >
<div v-if="flat.payment.installment_payment">
<div v-for="(find,indexT) in flat.payment.installment_payment.installments" :key="indexT">
<div v-if="find.amount >0">
<p> {{find.amount}}$ amount </p>
</div>
</div>
</div>
</div>
</div>
p.S: I stored my API data in array Flats
This will probably work, but it's untested.
You generally do not want to use v-if inside of v-for; instead, you should filter the data first and use the result in the v-for loop. [reference]
Also, since each flat has an _id field, you can use that instead of the index for the top level :key attribute.
<div v-for="flat in flatsWithPayments" :key="flat._id">
<div v-for="(installment, index) in getInstallmentsWithPaymentGTZero(flat.payment.installment_payment.installments)" :key="index">
<p> {{installment.amount}}$ amount </p>
</div>
</div>
Obviously, replace Flats with your data, but also note that in order to compare the payment amount, it needs to be converted with either Number(), parseInt() or parseFloat()
// Flats = { ... }
export default {
computed: {
flatsWithPayments() {
return Flats.filter(f => f.payment != undefined)
}
},
methods: {
getInstallmentsWithPaymentGTZero(installments) {
return installments.filter(i => Number(i.amount) > 0)
}
}
}

Custom Match Formula for EasyAutocomplete

I'm using a JS plugin called EasyAutocomplete to handle advanced autosuggestion on my website. It supports remote data sets (JSON, XML, plain text) and uses ajax method calls to search, sort and match the response phrase.
I'm trying to write a custom match formula, which it allows me to do, but would love some help writing this out.
Right now, as you type, it searches for any part of a word/phrase for what you're typing. I'd like to have it only check the beginning of each word in a single or multi-word phrase.
Let's say my remote data set contains:
"Market Street"
"Market Thestreet Lane"
"Street South Road"
If my search is "Street", by the plugin's default functionality, all 3 results will show, because the word "street" is part of each of those 3 results. (Demo Below)
I found a similar question to mine on the plugin's Github repo (and the answer from the plugin author), but that question was for matching only the beginning of each result item (not each word in each result item). So if my search is "Street", only the third result will show. (Demo Below)
My question is for a match formula that searches the beginning of any word in a result item. So, if my search is "Street", the first and third result will show.
Here is how the plugin functions by default (I've commented out the custom match formula):
jQuery(document).ready(function($) {
var options = {
data: [
{"name": "Market Street", "parent": "Dog Management", "link": "https://marketstreet.com"},
{"name": "Market Thestreet Lane", "parent": "Dog Management", "link": "https://marketlane.com"},
{"name": "Street South Road", "parent": "Cat Management", "link": "https://streetsouthroad.com"}
],
getValue: "name",
list: {
match: {
enabled: true,
/*
method: function(element, phrase) {
if(element.indexOf(phrase) === 0) {
return true;
} else {
return false;
}
}
*/
},
onSelectItemEvent: function() {
var selectedItemValue = $("#companies").getSelectedItemData().parent;
var selectedItemLink = $("#companies").getSelectedItemData().link;
$("#company_result").html('Contact '+selectedItemValue+'').trigger("change");
},
},
template: {
type: "description",
fields: {
description: "parent"
}
}
};
$("#companies").easyAutocomplete(options);
});
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css'>
<link rel='stylesheet' href='https://d3vv6lp55qjaqc.cloudfront.net/items/040r1I2Y1K3T3w3L3i3E/easy-autocomplete.css'>
<script src='https://code.jquery.com/jquery-1.11.2.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
<script src='https://d3vv6lp55qjaqc.cloudfront.net/items/2Q0s1P3D0D2i3u0M1m1x/jquery.easy-autocomplete.min.js'></script>
<div class="container">
<br>
<div class="row">
<div class="col-md-8 col-offset-3">
<p>Search for "Street"</p>
<form style="text-align: center; margin: 0 auto;" onkeypress="return event.keyCode != 13;">
<input class="form-control" id="companies" placeholder="Type your resort property name..."/>
<div id="company_result"></div>
</form>
</div>
</div>
</div>
Here is that custom match formula that only matches if the result item begins with the search phrase:
jQuery(document).ready(function($) {
var options = {
data: [
{"name": "Market Street", "parent": "Dog Management", "link": "https://marketstreet.com"},
{"name": "Market Thestreet Lane", "parent": "Dog Management", "link": "https://marketlane.com"},
{"name": "Street South Road", "parent": "Cat Management", "link": "https://streetsouthroad.com"}
],
getValue: "name",
list: {
match: {
enabled: true,
method: function(element, phrase) {
if(element.indexOf(phrase) === 0) {
return true;
} else {
return false;
}
}
},
onSelectItemEvent: function() {
var selectedItemValue = $("#companies").getSelectedItemData().parent;
var selectedItemLink = $("#companies").getSelectedItemData().link;
$("#company_result").html('Contact '+selectedItemValue+'').trigger("change");
},
},
template: {
type: "description",
fields: {
description: "parent"
}
}
};
$("#companies").easyAutocomplete(options);
});
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css'>
<link rel='stylesheet' href='https://d3vv6lp55qjaqc.cloudfront.net/items/040r1I2Y1K3T3w3L3i3E/easy-autocomplete.css'>
<script src='https://code.jquery.com/jquery-1.11.2.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
<script src='https://d3vv6lp55qjaqc.cloudfront.net/items/2Q0s1P3D0D2i3u0M1m1x/jquery.easy-autocomplete.min.js'></script>
<div class="container">
<br>
<div class="row">
<div class="col-md-8 col-offset-3">
<p>Search for "Street"</p>
<form style="text-align: center; margin: 0 auto;" onkeypress="return event.keyCode != 13;">
<input class="form-control" id="companies" placeholder="Type your resort property name..."/>
<div id="company_result"></div>
</form>
</div>
</div>
</div>
Thank you!
method: function(element, phrase) {
return !!element.toLowerCase().split(" ").filter((word) => word.indexOf(phrase.toLowerCase()) === 0).length;
}
This function:
Makes the element lowercase (no capitalization woes)
Splits it into its words
Filters out any words that don't start with the given input
If the filtered array has any elements in it, it must match. Otherwise, it doesn't. That's what the .length check there is for.

How to load different part of data from JSON file for each page using AngularJS?

I'm trying to create a shopping website using angularJs. My website has several html pages like women.html, men.html,...
I has 1 JSON file for the whole website. I want each website has different products, I did it but it only works on the main page (the page which showing all the products). When I click on a specific product, it doesn't show anything thing.
For more clearly, here is my code:
JSON file:
{
"Men": [
{
"name": "Adidas",
"price": 20,
"id": "adidas1",
"image": "pic4.png",
"description": "abcđsfadfdfsgsg"
},
{
"name": "Nike",
"price": 37,
"id": "nike1",
"image": "pic2.png",
"description": "abcđsfadfdfsgsg"
},
{
"name": "Converse",
"price": 25,
"id": "converse1",
"image": "pic3.png",
"description": "abcđsfadfdfsgsg"
}
],
"Women": [
{
"name": "Adidas2",
"price": 20,
"id": "adidas2",
"image": "pic6.png",
"description": "abcđsfadfdfsgsg"
},
{
"name": "Nike2",
"price": 37,
"id": "nike2",
"image": "pic7.png",
"description": "abcđsfadfdfsgsg"
},
{
"name": "Converse2",
"price": 25,
"id": "converse2",
"image": "pic5.png",
"description": "abcđsfadfdfsgsg"
}
]
}
Men.html:
<div ng-repeat="item in items.Men" style="float:left;">
<div><a href="#/item/{{item.name}}"><img src="/image/{{item.image}}" />
</a>
</div>
</div>
Women.html:
<div ng-repeat="item in items.Women" style="float:left;">
<div>
<a href="#/item/{{item.name}}"><img src="/image/{{item.image}}" />
</a>
</div>
</div>
App.js:
app.controller('ItemCtrl',
['$scope', '$routeParams', 'cartService',
function ($scope, $routeParams, cartService) {
$scope.item = {};
angular.forEach($scope.items, function (item) {
if (item.name == $routeParams.itemName) {
$scope.item.itemName = item.name;
$scope.item.itemPrice = item.price;
$scope.item.itemId = item.id;
$scope.item.itemImage = item.image;
$scope.item.itemDescription = item.description;
}
});
$scope.addProduct = function (item) {
cartService.addToCart(item, $scope.numberOfProducts);
};
}
]);
Product details page:
<div style="float:left; margin:0px 50px 300px 50px;">
<img src="/image/{{item.itemImage}}" style="margin- bottom:50px;margin-
left:200px;">
<p style="margin-right:0px;margin-top:50px;margin-left:50px;">Description:
{{item.itemDescription}}</p>
</div>
The code is really long so I only show its parts which I think making my code doesn't work.
Hopefully, there would be some help. Thanks in advance!
Check whether 'ItemCtrl' controller is binded to both men.html and women.html, if both the html files are not views.
Make two controllers and use $http method to call each page.Then using scope,reference data that you want and render in each page

Onclick select specific set of JSON Data

I have a JSON object, like so:
{
"workouts":
[
{
"title": "Full Body",
"exercises":
[
{
"name": "Push Ups",
"duration": 3,
"break": 3
},
{
"name": "Squats",
"duration": 3,
"break": 3
},
{
"name": "Running in Place",
"duration": 3,
"break": 3
}
]
},
{
"title": "God Legs",
"exercises":
[
{
"name": "Running in Place (High Knees)",
"duration": 3,
"break": 3
},
{
"name": "Squats",
"duration": 3,
"break": 3
},
{
"name": "Clams",
"duration": 3,
"break": 3
}
]
},
{
"title": "Morning Stretch",
"exercises":
[
{
"name": "Downward Dog",
"duration": 3,
"break": 3
},
{
"name": "Face Plant",
"duration": 3,
"break": 3
},
{
"name": "Warrior",
"duration": 3,
"break": 3
}
]
}
]
}
I am looping through this data to create a list of cards dynamically. I would like to make it so that when a card is tapped/clicked, the information relative to that specific data set is displayed/accessed.
EXAMPLE if I tap the card with the "Full Body" title, I would like a list to be displayed containing only the exercises in the "Full Body" exercises array, which should be: Push Ups, 3, 3 - Squats, 3, 3 - Running in Place 3, 3.
I am using Firebase to host my data on the real time database.
HTML:
<!--LIST THAT THE DYNAMICALLY GENERATED CARDS ARE APPENDED TO-->
<ul id="cardList" class="cards"></ul>
<!--MARKUP FOR THE CARDS, USED IN THE JSON FOR LOOP-->
<li>
<div class='card' onclick='selectWorkout(this)'>
<a class='startIt' href='timer.html'>
<div class='cardInfo'>
<h3>Full Body</h3>
<p>10 min.</p>
</div>
</a>
<a class='cardOptions' href='overview.html'>
<p>Options</p>
</a>
</div>
</li>
JavaScript:
// Initialize Firebase.
firebase.initializeApp(config);
// Reference data.
var dbRef = firebase.database().ref().child("workouts");
// Sync with Firebase in real time.
dbRef.on("value", snap =>
{
var workouts = snap.val();
for (var i = 0; i < workouts.length; i++) // WORKS GREAT.
{
//display.innerHTML = exercises[0].name;
//$("#cardList").append(exercises[i].title);
var routine = workouts[i].title;
$("#cardList").append("<li><div class='card'><a class='startIt' href='#' onclick='selectWorkout(this)'>\n\
<div class='cardInfo'><h3>" + routine + "</h3><p>10 min.</p>\n\
</div></a><a class='cardOptions' href='overview.html'>\n\
<div id='options'><p>Options</p></div></a></div></li>"); // ALL GOOD.
}
function selectWorkout(this) // NOT ONLY DOES NOT ACHIEVE DESIRED GOAL, BREAKS ENTIRE FUNCTION.
{
var selected = workouts[this].exercises;
$("#cardList").append(selected);
}
});
I think I am doing, as usual, something stupid: It would seem "this" is not allowed in the workouts square brackets, but how do I get a specific set of data otherwise?
Yeah, you can't pass in this as a parameter name - it's a reserved word. In general I'd try to avoid using this in javascript if you can - it's really tricky.
A more robust way to do this is to create your card HTML as a separate jQuery object and add a specific clickhandler, like this:
var routine = workouts[i].title;
//create card element
var $card = ("<li><div class='card'><button class='startIt'><div class='cardInfo'><h3>" + routine + "</h3><p>10 min.</p></div></a><a class='cardOptions' href='overview.html'> <div id='options'><p>Options</p></div></button></div></li>");
//add click handler
$card.find("button.startIt").on("click", function() {
selectWorkout( workouts[i] );
});
//put card on the page
$("#cardList").append($card);
And then
function selectWorkout(workout)
{
var selected = workout.exercises;
//not sure what you're trying to do here??
$("#cardList").append(selected);
}
You may notice that I've changed your <a href='#'> into a <button> - generally better for accessibility!

How to dynamically populate display objects in Angular JS based on properties from the JSON object.?

I am reading the below json value from a module.js
.controller('home.person',['$scope','$filter','personResource',function($scope,$filter,personResource) {
$scope.searchPerson = function() {
var params = $scope.search || {};
params.skip=0;
params.take =10;
$scope.personDetails =
{
"apiversion": "0.1",
"code": 200,
"status": "OK",
"mydata": {
"myrecords": [
{
"models": [
{
"name": "Selva",
"dob": "10/10/1981"
}
],
"Address1": "ABC Street",
"Address2": "Apt 123",
"City": "NewCity1",
"State": "Georgia"
},
{
"models": [
{
"name": "Kumar",
"dob": "10/10/1982"
}
],
"Address1": "BCD Street",
"Address2": "Apt 345",
"City": "NewCity2",
"State": "Ohio",
"Country":"USA"
},
{
"models": [
{
"name": "Pranav",
"dob": "10/10/1983"
}
],
"Address1": "EFG Street",
"Address2": "Apt 678",
"City": "NewCity3",
"State": "NewYork",
"Country":"USA",
"Zipcode" :"123456"
}
]
}
}
}
}])
Now i am able to statically build the UX. But my each record set's key value pair count is different. So i want to build my html dynamically as per the current record set's count.Country & Zipcode is not exist in all records so i need to build dynamically the build and populate the html output.Most of the time, my json output is dynamic. Instead of persondetails, i may get the json output of a product details instead of PersonDetails.
<div ng-show="personDetails.mydata.myrecords.length > 0" ng-repeat="recordSingle in personDetails.mydata.myrecords">
<div >
<span >Address1: {{recordSingle.Address1}}</span>
<span >Address2: {{recordSingle.Address2}}</span>
<span>City: {{recordSingle.City}}</span>
<span>State: {{recordSingle.State}}</span>
<span>Country: {{recordSingle.Country}}</span>
<span>Zipcode: {{recordSingle.Zipcode}}</span>
</div>
</div>
One way is to use ng-if statement, for the optional span elements:
<span ng-if="recordSingle.Address1">Address1: {{recordSingle.Address1}}</span>
[Update #1: updated based on revised comments to question]
[Update #2: fixed typos in function and included plunkr]
I now understand that you want to dynamically build the display objects based on properties from the JSON object. In this case, I would iterate through the properties of the object. I would use a function to produce this array of properties for each object so that you can filter out any prototype chains. I would also remove out any unwanted propoerties, such as the internal $$hashKey and perhaps the array objects e.g.
In your controller:
$scope.getPropertyNames = getPropertyNames;
function getPropertyNames(obj) {
var props = [];
for (var key in obj) {
if (obj.hasOwnProperty(key) && !angular.isArray(obj[key]) && key !== '$$hashKey') {
props.push(key);
}
}
return props;
}
Then in your HTML view:
<div ng-repeat="record in personDetails.mydata.myrecords">
<div ng-repeat="prop in getPropertyNames(record)">
<span ng-bind="prop"></span>: <span ng-bind="record[prop]"></span>
</div>
</div>
This works for me... see this plunker. It is displaying each of the properties of the object in the array dynamically (you could have any property in the object). Is this not what you are trying to achieve?

Categories

Resources