AngularJS: get dynamic data from Array - arrays

I have a node.js, socket.io and angular application, where I create a "user" object whenever somebody connects to the server and puts it into my "users" array.
Now I want to have angular to take the users and dynamically update the page to show the array.
//users array contains all active user objects
var users =[];
//user object (example)
var exampleuser = {
name: "Hans",
clicks: 0,
lang: "German"
}
//index.html angular scope for all active users
<table>
<thead>
<tr><th colspan="4">All online users</th></tr>
</thead>
<tbody>
<tr ng-repeat="user in UserList">
<td>{{$index + 1}}</td>
<td><img src="./img/flags/{{user.lang}}.png" /></td>
<td>{{user.name}}</td>
<td>(clicks: {{user.clicks}} )</td>
</tr>
</tbody>
</table>
So how do I have to configure my Angular controller in order to get all users from the array "users" and put them in an angular $scope? Thanks

You will have to wrap your users array and socket.io code that updates this array inside a angular service(s) and then inject this service into your controller. Inside your controller you can assign your array, e.g. $scope.userList = yourService.users;
The only gotcha would be that you will have to wrap your update of the service users array in $apply so that Angular knows about the change to the array. It would be as simple as:
... Inside your socket.io angular service
function updateArray(newUser){
$rootScope.$apply(function() { users.push(newUser); };
};

Related

My AngularJS view is able to access it's controller's scope, but ng-repeat doesn't work

I'm building a MEAN app and used yeoman to scaffold the client side code. I have a controller and view called movies.
Here is the movie controller:
'use strict';
/**
* #ngdoc function
* #name clientApp.controller:MoviesCtrl
* #description
* # MoviesCtrl
* Controller of the clientApp
*/
angular.module('clientApp')
.controller('MoviesCtrl', function () {
this.movies = [
{
title: 'A New Hope',
url: 'http://youtube.com/embed/1g3_CFmnU7k'
},
{
title: 'The Empire Strikes Back',
url: 'http://youtube.com/embed/96v4XraJEPI'
},
{
title: 'Return of the Jedi',
url: 'http://youtube.com/embed/5UfA_aKBGMc'
}
];
});
Here is the movie view:
<table class="table table-striped">
<thead>
<th>Title</th>
<th>URL</th>
</thead>
<tbody>
<tr ng-repeat="movie in movies">
<td>{{movie.title}}</td>
<td>{{movie.url}}</td>
</tr>
</tbody>
</table>
{{movies}}
When I view the page in my browser, the ng-repeat doesn't work (nothing is printed out), but where I explicitly print out {{movies}} in the view, I see the movies json that I defined in the controller. So, I know that the view has access to the controller's scope.
Why isn't ng-repeat working even when the view has access to the controller's scope?
In order to use this.movies, you need to use controller AS syntax (MovieCtrl as ctrl). Even so, you may run into problems if you don't save controller object (e.g., vm = this)
The way you have it now, you need to assign to $scope instead of this
ng-repeat creates a new child scope, but your parent scope doesn't have the movies object, so you would have to inject $scope into the controller and specify it how it's normally done (e.g. $scope.movies = ... ) .
For this case when you want to use the "this" keyword, you just need to use "MoviesCtrl as moviesCtrl" in your markup and then use "moviesCtrl.movies" to get the object in your ng-repeat.
The reason of why {{movie}} seems to work is most likely because of the way interpolation works.

Angular js redirect URL on same page?

I am building an node js app within which I have one page on angular js. On one of the pages, all the users are listed down(as hyperlinks) and I click on one of them to access the information about that user.
So, if the first page's URL(i.e. all the users) is /users, I want the second URL to look like /users/:id. So, in a way I want to mimic the REST API format.
The user's info has been found and once the user-id has been found, I m trying to use $location.path(index).
Initially I thought using something like ng-if would help me in rendering using the same page. So, on my .jade file, I have added this condition:
div(ng-show='loadAllUsers()')
$scope.loadAllUsers = function(){
console.log($scope.index == -1)
return $scope.index == -1;
};
$scope.index has been initialized to -1. So, I thought something like this would work when a new id value has been generated. But, the problem is it doesn't seem to reload the whole page or div.. I tried using window.location.href as well. That, even though updated the URL but even that breaks...
So, is there a way on doing this on the same jade file by any chance? If not , then how exactly should I go about this problem?
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, UserService) {
$scope.userDetails={};
//get the list of users from data base. here i am taking some dummy values.
$scope.users=[{
id:1,
name:"Rahul"
},
{
id:2,
name:"Ajay"
},
{
id:3,
name:"Mohit"
}];
// get user details
$scope.getUserDetails=function(userId){
UserService.get({id:userId},function(userDetails){
$scope.userDetails=userDetails;
});
};
});
// No need to change the page URL. Above function will
// call 'UserService' factory. UserService will communicate
// with REST API with URL pattern 'app/rest/users/:id'.
// ':id' will be replaced by the id passed by above function.
// like 'app/rest/users/2'
angular.module('myApp').factory('UserService',function ($resource) {
return $resource('app/rest/users/:id', {}, {
get':{method:'GET', isArray: false}
});
});
<div ng-app="myApp" ng-controller="myCtrl">
<div>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr ng-repeat="user in users">
<td>{{user.id}}</td>
<td ng-click="getUserDetails(user.id)">{{user.name}}</td>
<tr>
</table>
</div>
<div>
<h3>User Details</h3>
id : userDetails.id <br/>
name : userDetails.name <br/>
// other details
</div>
</div>

angular edit details using REST($resource) Service

I have a table with result set and there is edit option existing in each row. After clicking on the 'edit' link, i am able to populating the content on the fields.
But how do i edit the existing content using REST Service.
<table class="table-bordered table">
<thead>
<tr>
<th>id</th>
<th>Director</th>
<th>genre</th>
<th>releaseYear</th>
<th>title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in results | orderBy:'_id'">
<td>{{result._id}}</td>
<td>{{result.director}}</td>
<td>{{result.genre}}</td>
<td>{{result.releaseYear}}</td>
<td>{{result.title}}</td>
<td>Edit | Delete</td>
</tr>
</tbody>
</table>
controller
$scope.saveContact = function(){
//save or edit contact
};
I have created in plunker.
content EDIT using REST API
I put it into work. You were close to the solution.
Here is the working plunker
What i did :
1 - I changed your way of getting the element for the form.
HTML
//I give the complete object instead of the id
ng-click="edit(result)"
Controller
//I prefer to pass the entire object
$scope.edit = function(result){
$scope.cineresultsFrm = angular.copy(result);
};
Service
I just removed the service. It wasn't useful anymore.
2 - I used the method of the ressource on your object
Controller
$scope.saveContact = function(){
//The function given to $update() is the .then() function
$scope.cineresultsFrm.$update(function(){
//If the update succeed i update the whole list. Could be done a better way but if you don't have any performance issues that will do the job.
$scope.getMovies();
});
};
I also changed the way you handle the "then" in the promise. But this is just a matter of taste.
$scope.getMovies = function(){
$scope.movieResults = movie.query(function() {
$scope.results = $scope.movieResults;
});
};
$scope.getMovies();
Hope it helped you

Reuse components in AngularJS

As a new AngularJS developer (coming from PHP+Laravel world) I'm facing some troubles designing the architecture of my new app.
Which is the best way to implement a CRUD app where entities are used more than once along the app?
For example: we have the entities 'document' and 'project'. Documents can be listed and viewed alone, but also can be attached to projects. Inside the project detail view I would like to include the attached documents, using the same template used when listing the documents alone. This widget should have its own controller and methods, since its need to make some API calls and apply some business logic; and receive the parent project data in some way.
What should I use for document listing? A directive, a ng-include or some other?
You should use module to use it as reusing component.
https://docs.angularjs.org/guide/module
i'm utilizing angular module and factory like this:
app.js
'use strict';
/* App Module */
var app = angular.module('my-app', [
'my-models',
]);
my-models.js
var myModels = angular.module('my-models', []);
myModels.factory('DocumentsModel', function ($http)
{
var DocumentsModel = function ()
{
};
DocumentsModel.get_documents = function (page, results_per_page)
{
var data = {
page: page,
results_per_page: results_per_page
};
var json = angular.toJson(data);
return $http.post('/api/documents', json);
};
DocumentsModel.set_document_state = function (document_id, document_state_id)
{
var json = angular.toJson(
{
'document_state': document_state_id
}
);
return $http.post('api/document/'+document_id', json);
};
return DocumentsModel;
});
using angular dependency injection mechanism, you can re-use this logic in multiple controllers by adding DocumentsModel to the controller function as parameter:
documents-ctrl.js
var app = angular.module('my-app');
var controller = app.controller("DocumentsCtrl",
function ($scope, DocumentsModel)
{
DocumentsModel.get_documents()
.success(function(data){
$scope.documents = data.documents;
});
});
in addition, you cad define one for your 'project' entity.
Edit:
Javier commented:
assuming your documents response is
[{name: ... , size: ... , last_modified: ... }, {name: ... , size: ... , last_modified: ... }, ...]
you can utilize ng-repeat like this:
<table>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Last Modified</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="document in documents">
<td>{{ document.name }}</td>
<td>{{ document.size/1024 | number:4 }} MB</td>
<td>{{ document.last_modified | date:'yyyy-MM-dd HH:mm:ss' }}</td>
</tr>
</tbody>
</table>
Just add it as a dependency to your own module. Like
angular.module('test', []);
angular.module('test2', ['test']);
You might want to take a look at Yeoman

ng-repeat ng-click when the click function has already been called earlier in the code

First off, I read the plethora of other questions and answers regarding ng-click, ng-repeat, and child and parent scopes (especially this excellent one.)
I think my problem is new.
I'm trying to call a function using ng-click within a table. The app allows for the sorting of Soundcloud likes. The problem is that when I try to call the ng click function using new data, it still tries to call the function using the old data. Let me explain better with the example:
Controller:
function TopListCtrl($scope, $http) {
$scope.sc_user = 'coolrivers';
$scope.getData = function(sc_user) {
var url = 'http://api.soundcloud.com/users/'+ $scope.sc_user +'/favorites.json?client_id=0553ef1b721e4783feda4f4fe6611d04&limit=200&linked_partitioning=1&callback=JSON_CALLBACK';
$http.jsonp(url).success(function(data) {
$scope.likes = data;
$scope.sortField = 'like.title';
$scope.reverse = true;
});
}
$scope.getData();
$scope.alertme = function(permalink) {
alert(permalink);
};
}
HTML
<div id="topelems">
<p id="nowsorting">Now sorting the Soundcloud likes of <input type=text ng-model="sc_user"><button class="btn-default" ng-click="getData(sc_user);">Sort</button></p>
<p id="search"><input ng-model="query" placeholder="Filter" type="text"/></p>
</div>
<table class="table table-hover table-striped">
<thead>
<tr>
<th>Song</th>
<th>Artist</th>
<th>Likes</th>
<th>Played</th>
<th>Tags</th>
</tr>
</thead>
<tr ng-repeat="like in likes.collection | filter:query | orderBy:sortField:reverse">
<td width="30%"><a href="{{ like.permalink_url }}">{{like.title}}</td>
(Trying to call it here) <td>{{like.user.username}}</td>
<td>{{like.favoritings_count}}</td>
<td>{{like.playback_count}}</td>
<td>{{like.tag_list}}</td>
</tr>
</table>
</div>
</body>
</html>
So I have that function getData. I use it to load the data in using the soundcloud api. I have a form at the top that uses getData to load a new user's Soundcloud data in. I also call the getData function in the controller so that there is an example on the page upon loading.
The problem is when I try to load a new user's data from a <td> I want to be able to click on the user to see and sort their likes.
How do I 'clear' the function or the global namespace (am I even refering to the right thing)? How can I reuse the getData function with a new variable?
Working Jsfiddle for this
In your getData function you have this line:
var url = 'http://api.soundcloud.com/users/'+ $scope.sc_user +'/favorites.json...
but you are passing in the variable sc_user to your getData function and should be using it like this (no $scope):
var url = 'http://api.soundcloud.com/users/'+ sc_user +'/favorites.json...
That being said... your initial data load fails because you are calling:
$scope.getData();
and not:
$scope.getData($scope.sc_user);

Resources