My ng-repeat uses bad links - angularjs

I'm working on a web app using the MEAN stack which is 99% complete except for this persistent error that's been annoying me. I've deployed it to https://find-nightlife.herokuapp.com/#/
My issue is that when you do a search for a location, the web console will always throw these errors:
GET https://find-nightlife.herokuapp.com/%7B%7Bplace.rating_img_url%7D%7D 404 (Not Found)
%7B%7Bplace.image_url%7D%7D:1 GET https://find-nightlife.herokuapp.com/%7B%7Bplace.image_url%7D%7D 404 (Not Found)
Link to the full code on github is here https://github.com/JordanBourne/NightLife
The relevant code:
<section class = "results">
<div ng-repeat = "place in results track by $index | limitTo: 10" class = "resultContainer">
<div class = "resultsList">
<div class = "placeImg"><img src = "{{place.image_url}}" alt = "{{place.name}}"/></div>
<div class = "placeAbout">
<div class = "placeName">
{{place.name}} <img src = "{{place.rating_img_url}}" /></div>
<div class = "placeSnippet">{{place.snippet_text}}</div>
</div>
<div class = "areyouGoing"><span style = "color: red">{{error}}</span> {{place.going}} Going
<button class = "placeGo notGo" ng-click = "plusOne(place, $index)" ng-show = "attend($index)">Not Going</button>
<button class = "placeGo letsGo" ng-click = "plusOne(place, $index)" ng-hide = "attend($index)">Let's Go!</button>
</div>
</div>
<div class = "divider" ng-hide="$last"></div>
</div>
app.controller('NightLifeCtrl', [
'$scope',
'yelp',
'auth',
function ($scope, yelp, auth) {
if(yelp.places.data) {
var attendanceIndex = [];
yelp.bars.forEach(function(bar) {
if (bar.people.indexOf(auth.currentUser()) < 0) {
attendanceIndex.push(0);
} else {
attendanceIndex.push(1);
}
})
$scope.attend = function (num) {
if (attendanceIndex[num] == 1) {
return true;
} else {
return false;
}
}
$scope.results = yelp.bars;
$scope.plusOne = function(place, num) {
if (!auth.isLoggedIn()) {
$scope.error = 'You must be logged in!';
return;
}
yelp.addOne(place);
if (attendanceIndex[num] == 1) {
return attendanceIndex[num] -= 1;
} else {
return attendanceIndex[num] += 1;
}
}
}
}
]);
All the angular.js is in public/js/scripts.js, the guilty ng-repeat is in public/nightlife.html, homepage in views/index.ejs
I appreciate any help!

Replace <img src = "{{ ..}}"> with <img ng-src = "{{ ..}}">. ng-src attribute supports dynamic content.

Always use the ng-href="" attribute when having {{<expressions>}} within your href attributes.
This will stop most of the unwanted behaviour.
Here's the documentation for ng-href.
Quote from the documentation:
The wrong way to write it:
link1
The correct way to write it:
<a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>

Related

How to "connect" 2 variables in Angular by some value

I have 2 variables in controller:
$scope.first = [
{ id="11", nameF="aaaa1" },
{ id="12", nameF="bbbb1" }
]
$scope.second = [
{ id="21", nameS="aaaa2", idFirst="11" },
{ id="22", nameS="bbbb2", idFirst="12" },
{ id="23", nameS="cccc2", idFirst="12" }
]
In a template I have ngRepeat for variable second:
<div ng-repeat="item in second>
<div>{{item.nameS}}</div>
<div>{{item.idFirst}}</div>
</div>
For every item.idFirst I would like to write out matching nameF instead. What is the best practice to achieve that? I can't seem to figure out a simple way to do it, but suppose there has to be one. Thanx!
You can use custom filter if you don't want to create one single object holding the expected structure.
HTML :
<div ng-repeat="item in second">
<div>{{item.nameS}}</div>
<div>{{item.idFirst | getMatchName:first}}</div>
</div>
JS:
.filter('getMatchName', function() {
return function(strName, arrFirst) {
arrFirst.forEach(function(val, key) {
if (val.id == strName) {
strName = val.nameF;
}
})
return strName;
}
})
Here is working plunker
If I understand correctly,
$scope.getNameF = function(idFirst){
for(var i = 0; i < $scope.first.length; i++){
if($scope.first[i].id === idFirst){
return $scope.first[i].nameF;
}
}
return undefined;
}
<div ng-repeat="item in second">
<div>{{item.nameS}}</div>
<div>{{getNameF(item.idFirst)}}</div>
</div>
EDIT
Also you can prepare data before rendering:
for(var i = 0; i < $scope.second.length; i++){
$scope.second[i].nameF = getNameF($scope.second[i].idFirst);
}
<div ng-repeat="item in second">
<div>{{item.nameS}}</div>
<div>{{item.nameF}}</div>
</div>
You can think of your template logic a bit like code, calling values from other variables: yourValue[another.value].nameF
Change your code to this and it should work
<div ng-repeat="item in second>
<div>{{item.nameS}}</div>
<div>{{first[item.idFirst].nameF}}</div>
</div>

Can't find Element Inside Template with querySelectorAll

Im done with visual things of my proj. Now, trying to put template tags. But my 'click toggle' event isnt working now. Tried lots of solutions. At the same time you may give hints about polymer.
Putted template tags like this:
<template is="dom-if" if="{{isUnis(tabSelected)}}">
<template is="dom-repeat" items="{{unis}}">
<paper-icon-item u="{{item.name}}">
<iron-image src="{{unImg(item.name)}}" sizing="contain" style="width:32px;height:32px" item-icon fade preload></iron-image>
<div class="flex">{{item.name}}</div>
<span>1</span>
</paper-icon-item>
</template>
</template>
<template is="dom-if" if="{{isDers(tabSelected)}}">
<paper-icon-item u="İstanbul">
<iron-image src="/unis/uni/istanbul.png" sizing="contain" style="width:32px;height:32px" item-icon fade preload></iron-image>
<div class="flex">İstanbul</div>
<span>1</span>
</paper-icon-item>
</template>
And my script inside template:
var notarApp = Polymer.dom(document).querySelector('notar-app');
document.addEventListener('polymer-ready', function() {
var p = Polymer.dom(notarApp.root).querySelectorAll('paper-icon-item'),
sifir = 0,
buyuttu = document.getElementById('#buyuttu'),
selected = document.getElementById('#selected');
for (var i = 0; i < p.length; i++) {
p[i].addEventListener('click', function() {
this.classList.toggle('shadow-2');
var listme = [];
for (var j = 0; j < p.length; j++) {
if (!(hasClass(p[j], 'shadow-2'))) {
continue;
}
listme.push(" " + p[j].attributes[0].nodeValue);
selected.innerHTML = listme;
}
if (listme.length == 0) {
selected.innerHTML = 'Listelemek için üniversite ve/ya ders seçin';
}
if (sifir == 0 && listme.length > 4) {
buyuttu.show();
sifir++;
}
});
}
});
Last i tried this inside polymer-ready and nothing happened:
if(p.length) {
console.log(p);
}
My guess is dom-if doesnt load content when if state is false.

Get model array values in controller's service

I've been facing an issue since couple of hours. My view template looks like-
<div class="row" ng-repeat="row in CampaignsService.getRows().subItems track by $index">
<div class="col-sm-2">
<select class="form-control dropDownPercent" ng-model="CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]" ng-change="CampaignsService.wow(CampaignsService.dropDownPercent, $index)" ng-options="o as o for o in CampaignsService.showPercentDropDown().values">
</select>
</div>
<div class="col-sm-2" style="line-height: 32px">
of visitors send to
</div>
<div class="col-sm-4">
<select class="form-control" ng-model="campaignSelect" ng-options="campaign.Campaign.id as campaign.Campaign.title for campaign in CampaignsService.getRows().items">
<option value=""> Please select </option>
</select>
</div>
<div class="col-sm-4">
<a class="btn btn-default" target="_blank" href="">Show campaign</a>
</div>
Variable CampaignsService.selectCounter is a counter variable and declared in service but when I'm going to use ng-model="CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]" it gives me error -
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 35 of the expression [CampaignsService.dropDownPercent[{{CampaignsService.selectCounter}}]] starting at [{CampaignsService.selectCounter}}]]
And when I use ng-model="CampaignsService.dropDownPercent['{{CampaignsService.selectCounter}}']" it does not give any error but it takes this variable as string.
My question is how could I create a model array and get model's array values in my service ?? I read many questions in stack community and none of the trick work for me. My service under my script, is
.service('CampaignsService', ['$rootScope', 'AjaxRequests', function ($rootScope, AjaxRequests) {
this.dropDownPercent = [];
this.selectCounter = 0;
var gareeb = [];
this.showPercentDefault = 100;
// this.campaignsData = [];
this.$rowsData = {
items: [], //array of objects
current: [], //array of objects
subItems: [] //array of objects
};
this.getRows = function () {
return this.$rowsData;
}
this.addNewRow = function () {
var wowRow = {}; //add a new object
this.getRows().subItems.push(wowRow);
this.selectCounter++;
gareeb.push(0);
}
this.calculatePercentages = function (index) {
angular.forEach(this.getRows().current, function (data, key) {
if (key == index) {
console.log(data);
}
})
}
this.showPercentDropDown = function ($index) {
var balle = 0;
var start;
angular.forEach(gareeb, function (aha, keywa) {
balle += aha;
})
var last = 100 - balle;
var final = [];
for (start = 0; start <= last; start += 10) {
final.push(start);
}
return this.values = {
values: final,
};
}
this.wow = function (valueWa, keyWa) {
console.log(this.dropDownPercent);
gareeb[keyWa] = valueWa;
this.changePercentDropDown();
}
this.changePercentDropDown = function () {
var angElement = angular.element(document.querySelector('.dropDownPercent'));
angular.forEach(angElement, function (data, key) {
console.log(data);
})
}
}])
Target model structure should be
ng-model="CampaignsService.dropDownPercent[1]"
ng-model="CampaignsService.dropDownPercent[2]"
ng-model="CampaignsService.dropDownPercent[3]"
A big thanks in advance.
Since you are in context of the Angular expression, you don't need interpolation tags {{...}}. So ngModel directive should look like this:
ng-model="CampaignsService.dropDownPercent[CampaignsService.selectCounter]"

AngularJS - ng-repeat show one item at a time

looking for some ideas here. i have a meal plan object that contains an array of meals. only one meal can be set as primary at a time but i want the user to be able to cycle through the array of meals and mark a meal as primary. i am stuck trying to figure out if ngrepeat makes sense here or ngswitch or ngshow. any thoughts or samples would be highly appreciated!
I have tried multiple approaches with no luck.
thanks
You could cycle through the meals by index of the meal and have a button to choose the meal like this:
http://jsfiddle.net/c6RZK/
var app = angular.module('mealsApp',[]);
app.controller('MealsCtrl',function($scope) {
$scope.meals = [
{name:'Meatloaf'},
{name:'Tacos'},
{name:'Spaghetti'}
];
$scope.meal_index = 0;
$scope.meal = {};
$scope.next = function() {
if ($scope.meal_index >= $scope.meals.length -1) {
$scope.meal_index = 0;
}
else {
$scope.meal_index ++;
}
};
$scope.choose = function(meal) {
$scope.meal = meal;
}
});
HTML
<div ng-app="mealsApp" ng-controller="MealsCtrl">
<div ng-repeat="m in meals">
<div ng-if="meal_index == $index">
<strong>{{m.name}}</strong>
<button ng-click="choose(m)">Choose</button>
</div>
</div>
<hr>
<button ng-click="next()">Next</button>
<hr>Your Choice: {{meal.name}}
</div>
You could just attach a property to the plan, with a flag that says whether or not it's the primary plan.
Here's a sample implementation:
$scope.plans = [{name:"One"}, {name:"Two"}, {name:"Three"}];
$scope.selectPlan = function(plan) {
for(var i = 0, l = $scope.plans.length; i < l; i++) {
$scope.plans[i].primary = false;
if($scope.plans[i] === plan) {
$scope.plans[i].primary = true;
}
}
};
HTML:
<ul>
<li ng-click="selectPlan(plan)" ng-repeat="plan in plans" ng-class="{primary: plan.primary}"><a href>{{plan.name}}</a></li>
</ul>
If you'd rather not attach properties you could use something like a selected index property on your controller.

i want hide only specific list element

i am new to angularjs. i have created list using ng-repeat. just i want to hide the selected list element from list:
html code which i prefered:
<ul>
<li ng-repeat="profile in profileMenu">
<div class="hederMenu" ng-hide="configureDisplay" ng-click="setProfile(profile.name)">
<a class="anchor" style="width:100%" >{{profile.name}}</a>
</div>
</li>
</ul>
here is controller code
$scope.profileMenu = [{
name : "My Profile"
}, {
name : "Configure"
}, {
name : "Logout"
}
];
$scope.profile = "";
$scope.setProfile = function (test) {
$scope.profileSelected = test;
if ($scope.profileSelected == "Configure") {
$location.path("/home/configure"); // if user click configure then this element will hide
$scope.configureDisplay = true;
}
if ($scope.profileSelected == "My Profile") {
$location.path("/home/dashboard");
$scope.configureDisplay = false;
}
if ($scope.profileSelected == "Logout") {
window.location.assign("http://mitesh.demoilab.pune/")
}
return $scope.profileSelected = test;
}
You have to set the configureDisplay property on the actual "Configure" profile item. Not sure what you're doing with the selection list, but I assume you would want the "Configure" item visible again when selecting another item. Therefore you'll also have to reset the "Configure" item back to false when selecting another item.
I modified your example a bit. Notice instead of passing the profile.name on setProfile, i'm passing the profile object. This just simplifies the interaction.
<ul>
<li ng-repeat="profile in profileMenu">
<div class="hederMenu" ng-hide="profile.configureDisplay" ng-click="setProfile(profile)">
<a class="anchor" style="width:100%" >{{profile.name}}</a>
</div>
</li>
</ul>
$scope.setProfile = function (selectedProfile) {
//reset the items
for (var i in $scope.profileMenu) {
$scope.profileMenu[i].configureDisplay = false;
}
if (selectedProfile.name == "Configure") {
$location.path("/home/configure"); // if user click configure then this element will hide
selectedProfile.configureDisplay = true;
}
if (selectedProfile.name == "My Profile") {
$location.path("/home/dashboard");
}
if (selectedProfile.name == "Logout") {
window.location.assign("http://mitesh.demoilab.pune/")
}
return true;
}
you need to do few changes ,
<li ng-repeat="profile in profileMenu">
<div class="hederMenu" ng-hide="profile.configureDisplay" ng-click="setProfile(profile)">
<a class="anchor" style="width:100%" >{{profile.name}}</a>
</div>
</li>
And in controler,
$scope.setProfile = function (test) {
$scope.profileSelected = test.name;
if ($scope.profileSelected == "Configure") {
$location.path("/home/configure");
test.configureDisplay = true;
}
if ($scope.profileSelected == "My Profile") {
$location.path("/home/dashboard");
test.configureDisplay = false;
}
if ($scope.profileSelected == "Logout") {
window.location.assign("http://mitesh.demoilab.pune/")
}
return test;
}

Resources