Nested ng-repeat with different data - angularjs

I am using AngularJS v1.5.0 in my web application. I have feedbacks of the users which are displayed using ng-repeat. Now whenever the executive clicks on the feedback, I make a server call and fetch the history of user and show it on the panel.
Once the call is successful, I assign the data to the feebackDetails variable which is now the controller scope variable. But I dont want it to have the controller scope, as each feedback will have different user and different data.
<div class="tab-pane fade in" ng-controller="controller-feedback" id="feedback">
<div class="col-md-12">
<div class="row">
<div ng-repeat="feedback in feedbacks track by $index" ng-click="getUserHistory(feedback.userId)" data-toggle="collapse" data-target="#feedback{{$index}}" aria-expanded="false" aria-controls="#feedback{{$index}}">
<div class="alert alert-info">
<div>{{feedback.feedback}}</div>
<div class="collapse" id="feedback{{$index}}">
<div class="well">
<div ng-repeat="feedbackDetail in feedbackDetails track by $index">
<span style="font-weight: bold">Question:</span> {{feedbackDetail.question}} <br>
<span style="font-weight: bold">Answer:</span> {{feedbackDetail.answer}} <br>
<span style="font-weight: bold">Helpful:</span> {{feedbackDetail.helpful}} <br>
<span style="font-weight: bold">Feedback:</span> {{feedbackDetail.feedback}} <br>
<span style="font-weight: bold">Executive:</span> {{feedbackDetail.executive}} <br>
<hr ng-hide="$last">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
This is my controller part.
app.controller('controller-feedback', function ($scope, $http, $rootScope) {
$scope.feedbacks = [];
$scope.feedbackDetails = [];
// This will get the feedbacks
$http
.get(phpUrl + 'test-feedback', {params:{doctorId:doctorId}})
.then(function success(response) {
$scope.feedbacks = response.data.data;
$rootScope.feedbackCount = response.data.data.length.toString();
}, function error(success) {
});
$scope.getUserHistory = function(userId) {
console.log(userId);
$http
.get(phpUrl + 'test-feedback-details', {params:{userId:userId}})
.then(function success(response) {
/**
* This variable $scope.feedbackDetails must be different for each ng-repeat
*/
$scope.feedbackDetails = response.data;
}, function error(success) {
});
};
});

change getUserHistory to accept feedback object.
$scope.getUserHistory = function(feedback) {
var userId = feedback. userId ....
and rather than
$scope.feedbackDetails = response.data;
assign
$scope.feedback.feedbackDetails = response.data;
Inner ng-repeat should use feedback.feedbackDetails.

You could create a directive called feedbackDetail that you put inside your ng-repeat which uses an isolate-scope and you pass in your feedbackDetail from the ng-repeat.
Then when you make the HTTP request from the directives controller and assign it to $scope it would be a scope for just that part of the repeat.

Related

Update view when api get data in Angularjs

So, i have a little problem. I made a tiny webapp that uses the omdb api. The thing is this that when i type in the movie that i'm searching for then press the search button, the view should change to the result.html view and show the data i got from the api.
The api works fine. I got the data to display, but that was in my index.html. Now i have splitted the files by using ng-routes
I can provide you the whole project if you wanna look at it. Maybe i can upload it to a online editor somewhere?
This is my app.js
var myApp = angular.module("myApp", ['ngRoute']);
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'views/home.html',
controller: 'filmController'
}).
when('/result', {
templateUrl: 'views/result.html',
controller: 'filmController'
}).otherwise({
redirectTo: '/home'
})
}]);
myApp.controller("filmController", function filmController($scope, $http, $window) {
$scope.getData = function () {
var movieTitle = document.getElementById("filmName").value;
var binding = document.getElementsByClassName("ng-binding");
$http
.get("http://www.omdbapi.com/?t=" + movieTitle + "&apikey=526345a6")
.then(function (response) {
var data = response.data;
if (data.Error) {
alert("Film inte funnen");
return false;
}
for (let key in data) {
if (data.hasOwnProperty(key)) {
let element = data[key];
if (element === "N/A") {
data[key] = "Inget hittat";
if (key === "Poster") {
$scope.post = "Ingen poster hittad";
}
}
$scope.url = data;
}
}
if (data.Ratings.length === 0) {
$scope.rate = "Ingen utmärkelse/er hittad";
}
// This api data is printed to the console on the index.html view, i want it in the result view...
console.log(data);
});
};
});
This is my index.html
<body>
<div class="main-content">
<!-- <div class="container">
<div class="row">
<div ng-controller="MyController">
<div ng-repeat="item in larare" class="col-lg-12">
<div class="card">
<img ng-src="/images/{{item.shortname}}.jpg" alt="Bild på {{item.name}}" class="card-img-top">
<div class="card-body">
<h3 class="card-title">{{item.name}}</h3>
<p class="card-text">{{item.reknown}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
<header ng-include="'header.html'"></header>
<main ng-view></main>
</div>
</div>
<script>
var elem = document.getElementsByTagName("body")[0];
if (elem.requestFullscreen) {
elem.requestFullscreen();
console.log("Nu");
} else {
console.log("Error");
}
function updateSite(event) {
window.applicationCache.swapCache();
}
window.applicationCache.addEventListener('updateready',
updateSite, false);
</script>
<script src="./js/formvalidation.js"></script>
</body>
</htm>
And my result view
<div class="show container" ng-show="url.Title">
<div class="poster">
<!-- <img ng-src="{{ url.Poster }}" alt="" /> -->
<img ng-src="{{ url.Poster == 'Inget hittat' ? './images/image_not_found.png' : url.Poster }}">
<p class="text-dark">{{ post }}</p>
</div>
<h2 class="text-dark">{{ url.Title | uppercase}}</h2>
<div class="movie-info">
<p class="text-dark">
<strong>Från:</strong> {{ url.Year }}</p>
<p class="text-dark">
<strong>Rating:</strong> {{ url.Rated }}</p>
<p class="text-dark">
<strong>Utgiven:</strong> {{ url.Released }}</p>
<p class="text-dark">
<strong>Längd:</strong> {{ url.Runtime }}</p>
</div>
<p class="text-dark">
<strong>Skådespelare:</strong> {{ url.Actors }}</p>
<p class="text-dark">
<strong>Regissör:</strong> {{ url.Director }}</p>
<p class="text-dark">
<strong>Utmärkelser:</strong> {{ url.Awards }}</p>
<p class="text-dark">
<strong>Handling: </strong> {{ url.Plot }}</p>
<h4 class="text-dark">
<strong>Utmärkelser</strong>
</h4>
<p class="text-dark ">{{ rate }}</p>
<div ng-repeat="rating in url.Ratings | orderBy: '-Value'">
<p class="text-dark">
<strong>{{ rating.Source + ': ' + rating.Value }}</strong>
</p>
</div>
This is my home view
<section class="text-white">
<img src="./images/background.jpg" alt="Bild på poster från Frankenstein filmen" class="bg-image">
<div class="main container align-middle">
<div class="row text-center justify-content-center section-intro">
<div class="col-6 mb-5">
<h1>Filmtipset</h1>
<h5>Sök efter dina favortfilmer</h5>
</div>
</div>
<form ng-submit="getData()">
<div class="inputSearch mx-auto">
<div class="row justify-content-center">
<div class="col-8">
<div class="form-group">
<label for="filmName">Film</label>
<input class="form-control" type="text" name="filmName" id="filmName" placeholder="Ex. Armageddon" autofocus>
</div>
<input type="submit" class="btn btn-primary btn-lg btn-block btn-info mt-5" onclick="validateForm()" ng-click="'#!/result'">
</div>
</div>
</form>
Just a few notes before we break down the issue you are seeing. It seems like you aren't using angularjs to its full potential. Using a mixture of jquery and angularjs is not best practice and can make debugging very difficult. I would recommend making movieTitle an ng-model, as well as using angularjs's built in form validation. This will allow you to access the movieTitle easily in your controller as well as take advantage of angular's built in form controller and actions.
As for getting the data to your other page, I would add a second controller for that page, then from your filmController, you can pass the movie title as a route param when changing views. In your resultController, you can get the movie title from the route param and then do your api call while initializing the result page/controller.
In your home.html file, we will add ng-model to the input to be more angularjs friendly. Now we can access movieTitle in the controller with $scope.movieTitle. I am also changing the method that should get run on submit to the search() method I have added to the filmController.
home.html
<form ng-submit="search()">
// ... your other html here
<input class="form-control" type="text" name="filmName" id="filmName" ng-model="movieTitle" placeholder="Ex. Armageddon" autofocus>
In your filmController, I would add $location and move the getData() method to a resultController. We will now call search() on form submit, then use $location to switch views to your result view, attaching the movie title as a route param so we can access it in the resultController.
filmController.js
myApp.controller("filmController", function filmController($scope, $http, $location) {
// ... other code you have
// this will be what gets called after the user types a movie title
$scope.search = function () {
var title = $scope.movieTitle; // or using jquery(not recommended)
// this will change location and will produce
// the url: http://[yoursite]/result?movieTitle=title
$location.path('/result').search({movieTitle: title});
}
});
In your newly created resultController, we will get that route param and use it to search for the data.
resultController.js
myApp.controller("resultController", function resultController($scope, $http, $routeParams) {
// the getData method moved to this controller
$scope.getData = function (movieTitle) {
$http
.get("http://www.omdbapi.com/?t=" + movieTitle + "&apikey=526345a6")
// the rest of your code for getData here
};
// I like to make an init method to make it clear what we are initializing
$scope.init = function () {
// this will get the movieTitle from the /result?movieTitle=title part of your url
var movieTitle = $routeParams['movieTitle'];
// now we call the method you have that uses the omdb api
// just modify it to accept the movie title as a param rather than finding it in the html
$scope.getData(movieTitle);
}
// run init method
$scope.init();
});
Finally, you will want to update your app config to have your result route use the resultController we added instead of the filmController.

Angularjs passing id into modal function()

I am using angularjs and spring mvc for my application. At first I am displaying all the placements with one button Placed Candidates. I am doing ng-repeat for displaying all the placements.
Here is my html.
<div class="row" data-ng-repeat="placement in placements">
<div class="col-xs-12 col-sm-12 col-md-12">
<h5>{{placement.companyName}}</h5>
</div>
<div class="col-xs-12 col-sm-6 col-md-3">
<h6>{{placement.placementDate | date:'dd MMM yyyy'}}</h6>
Company Target (appox):10 Achieved:
</div>
<a href="javascript:void(0);" data-ng-
click="placedcandidatespopup(placement.placementId);">//here i can
get particular placementId but how to pass this id to
savePlacementStudents() method in controller.js???//
PlacedCandidates
</a>
</div>
I am calling one modal popup function. Here is controller.js
$scope.placedcandidatespopup = function()
{
AdminUsersService.getAllUsers().then(function(response)
{
$scope.allusers=response.data;
console.log($scope.allusers);
})
$(".placedcandidates").modal("show");
}
I am calling modal by name "placedcandidates".
Here is the modal
<div class="modal right fade placedcandidates" id="myModal1"
tabindex="-1" role="dialog" aria-labelledby="myModalLabel2">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6" data-ng-
repeat="student in allusers">
<input id="{{student.studentId}}" type="checkbox" value="
{{student.studentId}}" data-ng-
checked="selection.indexOf(student.studentId) >-1"
data-ng-click="toggleSelection(student.studentId)"/>
<label for="{{student.studentId}}"></label>
{{student.firstName}}
</div>
</div>
</div>
<div class="modal-footer" style="position: fixed;">
<input type="button" value="Submit" class="btn" data-ng-
click="savePlacementStudents()">
</div>
</div>
</div>
</div>
After clicking on button popup modal will display with all Students with checkboxes placed beside them.Now I am able to save studentId into database.But I am not able to pass placementId for saving.
Here is my savePlacementStudents method in controller.js.
$scope.selectedStudents = {};
$scope.savePlacementStudents = function(selectedStudents)
{
selectedStudents.selectedList = $scope.selection;
AdminPlacementService.savePlacementStudents(selectedStudents).then
(function(response)
{
if(response.data=="success"){
$window.scrollTo(0,0);
$scope.selectedStudents = {};
$scope.getplacementdetails($scope.currentPageIndex);
$scope.successmessage="Student Selection Saved!;
$timeout(function() {
$scope.successmessage="";
$scope.closeplacedcandidatespopup();
}, 1500);
}
});
}
Can anyone tell how can I pass respective placementId to this saveStudents method() so that I can set that placementId for those students selected.
Here is the solution.
The placementId passed to function placedcandidatespopup can be saved as follows in function placedcandidatespopup
$scope.selectedPlacementId = placementId;
Now the same $scope will have access in Popup also as it is using same controller.js. And now you can use this $scope.selectedPlacementId anywhere in controller.js
So you don't need to pass placementId...!!
Hope that would help you..!!
$scope.placedcandidatespopup = function(id)
{
AdminUsersService.getAllUsers().then(function(response)
{
$scope.allusers=response.data;
console.log($scope.allusers);
})
$scope.inserted = {
placementId:id,
};
var modalInstance = $uibModal.open({
templateUrl: 'views/test/myModal1.html',
controller: 'TestCntrl',
size: "Modelwidth",
resolve: {
items: function () {
return $scope.inserted;
}
}
});
}
In model you have to access using items.placementId and when you savePlacementStudents() method send placementId as well with student info.

How to limit results with ng-repeat?

Sorry for the basic question, extremely new to software development and angular in particular. I'm currently making a small app that uses an api to find cinemas near a certain postcode.
I have made a results page that show what films are playing in a certain cinema, but I want to limit the amount of results returned and include a 'Show more films' button.
I have included my html and controller below:
HTML
<div class="main-container">
<fountain-header></fountain-header>
<div class="cinemas-container">
<h2 align="center" class="cinemas-h2">Movies playing here:</h2>
<div class="cinema2" ng-repeat="listing in $ctrl.listings">
<h3 class="cinema-h5">{{listing.title}}</h3>
<ul class="cinema-h4">
<li ng-repeat="time in listing.times">{{time}}</li>
</ul>
</div>
<div class="main-container">
</main>
</div>
<fountain-footer></fountain-footer>
</div>
</div>
ListingsController
function ListingsController($http, $stateParams) {
console.log($stateParams);
console.log($stateParams.cinemaID);
var vm = this;
$http
.get('https://api.cinelist.co.uk/get/times/cinema/' + $stateParams.CinemaID +'?day=1')
.then(function (response) {
console.log(response);
vm.listings = response.data.listings;
});
}
Could I just use limitTo to achieve this?
P.S sorry for the poor information, it's my first question on here.
Please try the following example in the fiddle link -
Using the limitTo filter.
ng-repeat="d in data | limitTo: limitvar"
https://jsfiddle.net/m0q9ju8a/
let me know if this helps.
You can do like this:
HTML
<div class="main-container">
<fountain-header></fountain-header>
<div class="cinemas-container">
<h2 align="center" class="cinemas-h2">Movies playing here:</h2>
<div class="cinema2" ng-repeat="listing in vm.listings | limitTo: vm.limit as results">
<h3 class="cinema-h5">{{listing.title}}</h3>
<ul class="cinema-h4">
<li ng-repeat="time in vm.listing.times">{{time}}</li>
</ul>
</div>
<button ng-hide="results.length === vm.listings.length" ng-click="vm.limit = vm.limit +8">show more...</button>
<div class="main-container">
</main>
</div>
<fountain-footer></fountain-footer>
</div>
</div>
and in youn controller
function ListingsController($http, $stateParams) {
console.log($stateParams);
console.log($stateParams.cinemaID);
var vm = this;
vm.limit = 8;
$http
.get('https://api.cinelist.co.uk/get/times/cinema/' + $stateParams.CinemaID +'?day=1')
.then(function (response) {
console.log(response);
vm.listings = response.data.listings;
});
}

AngularJs how to reload a template page after selecting a charater

I am using http.get and i need to reload a template after selecting a character so that my template gives the answer according to what I selected
The code of the template
<div class="container-fluid" ng-controller="CriterioCtrl">
<div id="result"></div>
<div>
Selected Items:
<div ng-repeat="id in selection">
{{id}}
</div>
</div>
<div ng-repeat-start="crit in data" class="row">
<h2 align="center">{{crit.name}}</h2>
<div ng-repeat="caracter in crit.characters" class="col-md-4">
<div type="checkbox" value="{{caracter.id}}" ng-checked="selection.indexOf(caracter.id) > -1" ng-click="clickSelection(caracter.id)">
<a href="#crit" class="thumbnail" ng-click="clickCriterios(caracter.id)">
<h4 align="center">{{caracter.name}}</h4>
<img ng-src="http://skaphandrus.com/{{caracter.image_url}}"/>
</a>
</div>
</div>
</div>
<div ng-repeat-end>
</div>
<!--<button class="btn" ng-click="toggle()">Toggle</button>
<p ng-show="visible">Hello World!</p> codigo de um botao -->
</div>
This code is for the selection
$scope.selection=[];
$scope.clickSelection = function clickSelection(caracterId) {
var idx = $scope.selection.indexOf(caracterId);
// is currently selected
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
// is newly selected
else {
$scope.selection.push(caracterId);
}
var selectedId = $scope.selection;
console.log(selectedId);
// Check browser support
if (typeof(Storage) != "undefined") {
// Store
localStorage.setItem("idSelect", selectedId);
// Retrieve
document.getElementById("result").innerHTML = localStorage.getItem("idSelect");
}
};
This code is the http part in another controller
MyApp.controller('EspeciesCtrl', function ($scope, $http) {
$http({
url: 'someurl',
method: "post",
params: {module_id: localStorage.getItem("idMod"),"characters[]": [localStorage.getItem("idSelect")]}
})
.then(function(res){
$scope.data = res.data;
});
});
This is the code of the template that have to change after the selection
<div class="container-fluid" ng-controller="EspeciesCtrl">
<div class="row">
<div ng-repeat="esp in data" class="col-md-6">
<a href="#infEsp" class="thumbnail" ng-click="clickEspecie(esp.id)">
<h4 align="center">{{esp.name}}</h4>
<img ng-src="{{esp.image_src}}"/>
</a>
</div>
</div>
</div>
How can i do that?
edit 1
If I've correctly understood, you may need to use ng-show (https://docs.angularjs.org/api/ng/directive/ngShow) whose boolean value checks if the user selected anything and show the extra bit of code you need, instead of trying to have another http request.
Also, it seems like you are using $scope.data for both the esp and the crit, so you will end up with the errors.
You probably don't need to reload the template.
You may want to use the data in $scope.data inside the template in order to let Angular manage the update on the view. As soon as the $scope.data changes, the rendered HTML changes too.
I can't comment but it would be helpful if you could share the template you are using and be more specific in your request :)

How do I target just one instance of a nested repeat?

This is probably pretty basic, but I'm going round in circles.
I have a nested controller in a ng-repeat - I would like to trigger an event in an instance of the repeat that will affect only the nested controller in that instance, not the nested controller in all instances.
Here is a basic example to try and make things clearer:
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts">
<p>{{post.body}}</p>
<div ng-controller="CommentsCtrl">
<div ng-repeat="comment in comments">
<p>{{comments.body}}</p>
<a href ng-click="showComments(post.id)">Show comments</a>
</div
</div>
</div>
</div>
angular.module('myApp')
.controller('PostsCtrl', function ($scope, Restangular) {
var posts = Restangular.all('posts');
posts.getList().then(function(posts) {
$scope.posts = posts;
});
$scope.showComments = function(id) {
$scope.$broadcast('SHOW_COMMENTS', id);
};
});
angular.module('myApp')
.controller('CommentsCtrl', function ($scope, Restangular) {
$scope.$on('SHOW_COMMENTS', function(event, id) {
var post = Restangular.one('posts', id);
post.get().then(function(post) {
$scope.comments = post.comments;
});
});
});
What would happen in this example is that all instances of the comments controller would be populated with the same comments - I just need to be able to target the relevant one. I'm sure I'm missing something pretty fundamental, and probably going about this the wrong way.
Many thanks.
You're broadcasting "SHOW_COMMENTS" to all repeated posts. You need to isolate the scope per Post.
Isolate the scope using ng-controller="PostCtrl".
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts" ng-controller="PostCtrl">
<p>{{post.body}}</p>
<div ng-controller="CommentsCtrl">
<div ng-repeat="comment in comments">
<p>{{comments.body}}</p>
<a href ng-click="showComments()">Show comments</a>
</div
</div>
</div>
</div>
Move show comments from PostsCtrl to PostCtrl.
angular.module('myApp')
.controller('PostCtrl', function ($scope) {
$scope.showComments = function() {
$scope.$broadcast('SHOW_COMMENTS', $scope.post.id);
};
});
Do you really need to use events? You could use ng-if instead.
Your comments are embedded in each Post anyway so why request them again. In this case you could do like something this...
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts" ng-controller="PostCtrl">
<p>{{post.body}}</p>
<a href ng-click="toggleComments()">Show comments</a>
<div ng-controller="CommentsCtrl" ng-if="showComments">
<div ng-repeat="comment in post.comments">
<p>{{comments.body}}</p>
</div
</div>
</div>
</div>
Toggle comments...
angular.module('myApp')
.controller('PostCtrl', function ($scope) {
$scope.showComments = false;
$scope.toggleComments = function() {
$scope.showComments = !$scope.showComments;
};
});

Resources