I have a simple book store example that I am working through for angularjs and I am trying to pass a book id from a home page into a service on an edit page so that the book details can be rendered. What I have happen is I can see the rest call being hit from my home' page with the correct book id being passed into the book service. However, I cannot seem to think of a way to have theBookCtrl` load that data when a different page invokes the rest service. The order I am expecting is:
1) User enters a book ID to edit
2) User presses Search button
3) book.html page is loaded
4) BookEdit service is invoked with ID from Steps 1 and 2
5) ng-model for book loads data.
Apologies in advance, there may be some errors as I was modifying this code from a different computer, so I couldn't copy/paste
code below:
home.html
<div ng-controller="HomeCtrl">
<div>
<label for="query">Book to edit</label>
<input id="query" ng-model ="editBook.query">
<button ng-click="loadBookById()">Search</button>
</div>
</div>
home.js:
var homeApp = angular.module('bookHome',['bookEdit']);
homeApp.controller('HomeCtrl',function($scope,$http,bookEditService)
{
$http.get('http://get/your/books/rest').success(function(data){
$scope.library = data;
});
$scope.editBook = {
query: '',
service:'bookEditService'
} ;
$scope.loadBookById = function()
{
$scope.$emit('loadBookById',{
query:$scope.editBook.query,
$service: $scope.editBook .service
}
$scope.$on('loadBookById', function(ev,search){
bookEditService.loadBook({
bookId: $scope.editBook.query
},
$scope.searchComplete,
$scope.errorSearching
);
});
$scope.searchComplete = function(results) {
$scope.results = results;
};
$scope.errorSearch= function(data,status,headers,config){
console.log(data);
// ...
};
}
book.html
<div ng-controller="BookCtrl" >
<div ng-model="details.title"></div>
<div ng-model="details.author"></div>
</div>
bookEdit.js
var bookEditApp = angular.module('bookEdit',[]);
bookEditApp.service('loadBook',function($http){
return{
loadBookById: function(params,success,error){
$http({
url: 'http://path/to/book/editing',
method: 'GET',
params:{bookId: params.bookId}).success(function(data,status,headers,config)
{
var results = data;
success(results || []);
}).error(function(){
error(arguments);
});
}
};
});
bookEditApp.controller('BookCtrl',function($scope){
$scope.details = {
title: "",
author: ""
};
});
An alternative that follows the order you are expecting is:
1) User enters book id and presses button
2) HomeCtrl routes to EditCtrl with the entered id as a route parameter (no need to use the book service yet):
app.controller('HomeCtrl', function ($scope, $location) {
$scope.editBook = function () {
$location.path('/edit/' + $scope.id);
};
});
3) EditCtrl is loaded, retrieves the route parameter and asks the book service for the correct book:
app.controller('EditCtrl', function EditCtrl($scope, $routeParams, bookService, $location) {
$scope.loading = true;
bookService.getBookById($routeParams.id)
.then(function (result) {
$scope.book = result;
$scope.loading = false;
});
4) When book is loaded the model ($scope.book) is populated and the html is updated
Here is a working example that hopefully will give some further guidance and ideas: http://plnkr.co/edit/fpxtAU?p=preview
Related
Here I am using "$http.get" request to retrieve data from Web API in Angular.js.
An API URL has a parameter call "pageNo=" and it's require to add a digit at the end of that parameter to retrieve a data of respective page number to maintain heavy request loads, each page has 10 list of records.
I took a scope variable ($scope.pageCount) and pass it with the URL, it's working fine and able to retrieve 10 list of records at once.
Now I am suppose to get the rest of data on scroll down (like using infinite-scroll) and append it with the existing list of data.
Is it possible or any way to retrieve more data from the same request?
I have added 'infinite-scroll' to the application and getting an alert on scroll down.
Following is the current working function.
app.controller('SpotLightCtrl', function ($scope, $http, shareEventID) {
$scope.pageCount = 1;
$http.get("https://stg.myapi.svc/pageNo="+$scope.pageCount+)
.then(function (response) {
$scope.data = response.data;
$scope.loadMore = function () {
alert('Load more records');
};
});
});
And the HTML code:
<div class="content-block spotlight-listing" ng-controller="SpotLightCtrl" infinite-scroll='loadMore()' infinite-scroll-distance='1'>
<h1>Spotlight</h1>
<div ng-repeat="event in data.eventList" class="deals-block">
<div class="col-md-4 col-xs-12 img-style">
<img ng-src="{{event.eventPosterImage}}" class="img-responsive img-rounded" />
</div>
</div>
</div>
Please share if any details is not clear, Any help would be appreciated...
You can put the http call in a function and call the function with the pagecount.
app.controller('SpotLightCtrl', function ($scope, $http, shareEventID) {
$scope.pageCount = 1;
$scope.data = [];
function getData() {
$http.get("https://stg.myapi.svc/pageNo=" + $scope.pageCount)
.then(function (response) {
$scope.data = $scope.data.concat(response.data);
});
}
$scope.loadMore = function () {
console.log('Loading more records');
++$scope.pageCount; //page count is incremented by 1, so
getData();
};
getData();
});
when the loadMore function is called pagecount is incremented by one and the next records are appended to the $scope.data
I am relatively new to Angular JS. Currently I met a problem, lets say I have 1000 items in a list. In order to display the details about each item I will pass the items_id to generate html example(123.html). In this case, do I need 1000 controller to handle this kind of situation?
Controller
app.controller('item0001',function($scope,$http,$sce){
$scope.data = [];
$scope.details=[];
$http.get("https://api.com/test/product/0001").then(function(response){
var getData = response.data;
$scope.data.push(response.data);
$scope.bindHtml = $sce.trustAsHtml(getData.details);
for(var i = 0; i<getData.specification.length; i++){
$scope.details.push(getData.details[i]);
}
});
});
app.controller('item0002',function($scope,$http,$sce){
$scope.data = [];
$scope.details=[];
$http.get("https://api.com/test/product/0002").then(function(response){
var getData = response.data;
$scope.data.push(response.data);
$scope.bindHtml = $sce.trustAsHtml(getData.details);
for(var i = 0; i<getData.specification.length; i++){
$scope.details.push(getData.details[i]);
}
});
});
View
<p>
View More
</p>
Use single controller and HTML.
Bind the HTML with some ViewModel (a property on $scope)
From your controller place the call to fetch item details (I am assuming you have fetch these details on click of some button) using a service.
In success callback of your service update the view model. and angular using 2-way binding, will update the view with last item fetched.
Controller:
app.controller('ProductCtrl', function($scope, ProductService) {
var getProduct = function(productId) {
ProductService.getProduct(productId).then(function(response) {
$scope.productDetails = response.data;
})
};
});
Service:
app.factory('ProductService', function($http) {
return {
getProduct(productID) {
return $http({
method: 'GET',
url: "https://api.com/test/product/" + productID
});
};
}
});
HTML View:
<body ng-controller="ProductCtrl">
<div ng-init="getProduct(0001)">
<p>Name {{productDetails.name}}</p>
<p>ID {{productDetails.id}}</p>
<p>Description {{productDetails.description}}</p>
</div>
<button ng-click="getProduct(productDetails.id + 1)">Get Next Product</button>
</body>
I hope this gives you a basic idea of how to implement your requirement. Please elaborate your question so that I can provide a more specific solution.
Define a single view (html) and controller to handle this.. example below.
productDetails.html (view)
<div>
<span>{{productName}}</span>
</div>
productDetails.js (controller)
app.controller('productDetailsCtrl',function($scope,$http,$sce){
$scope.productName = "";
$http.get("https://api.com/test/product/0001").then(function(response){
var getData = response.data;
$scope.productName = getData.productName;
});
});
I have a simple website that uses AngularJS with a NodeJS backend.
It has multiple pages, like a homepage, a login/register page, etc.
I'd like to implement a "Chat" page where you could send messages to other clients using socket.io. I already got that part working, using a local controller (by local, I mean active on a single page - the Chat page).
The problem is, I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
I'm having an issue when setting the Chat controller global (active on all pages).
Here's how I'm including it:
<body ng-controller="AppCtrl"> <!-- include main controller -->
<div ng-include="'header.tpl.html'"></div>
<div ng-controller="ChatCtrl" class="page"> <!-- include global Chat controller -->
<div ng-view class="container"></div>
</div>
<div ng-include="'footer.tpl.html'"></div>
<!-- ...etc. -->
</body>
This works pretty well, but it seems like I can't access a value from my Chat page, though. Functions declared from the Chat controller can still be called, but the "$scope.message" value (which contains the message that's being typed) is always empty.
Here's my Chat controller (which is actually called TravelCtrl)
angular.module('base').controller('TravelCtrl', //['$scope', 'security',
function($rootScope, $scope, security, NgMap, $geolocation, socket){
$scope.messages = [];
// Socket listeners
// ================
socket.on('init', function (data) {
$scope.name = data.name;
$scope.users = data.users;
});
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
socket.on('change:name', function (data) {
changeName(data.oldName, data.newName);
});
socket.on('user:join', function (data) {
$scope.messages.push({
user: 'Server',
text: 'User ' + data.name + ' has joined.'
});
$scope.users.push(data.name);
});
// add a message to the conversation when a user disconnects or leaves the room
socket.on('user:left', function (data) {
$scope.messages.push({
user: 'chatroom',
text: 'User ' + data.name + ' has left.'
});
var i, user;
for (i = 0; i < $scope.users.length; i++) {
user = $scope.users[i];
if (user === data.name) {
$scope.users.splice(i, 1);
break;
}
}
});
// Private helpers
// ===============
var changeName = function (oldName, newName) {
// rename user in list of users
var i;
for (i = 0; i < $scope.users.length; i++) {
if ($scope.users[i] === oldName) {
$scope.users[i] = newName;
}
}
$scope.messages.push({
user: 'Server',
text: 'User ' + oldName + ' has been authenticated as ' + newName + '.'
});
}
// Methods published to the scope
// ==============================
$scope.changeName = function () {
socket.emit('change:name', {
name: $scope.newName
}, function (result) {
if (!result) {
alert('There was an error changing your name');
} else {
changeName($scope.name, $scope.newName);
$scope.name = $scope.newName;
$scope.newName = '';
}
});
};
$scope.sendMessage = function () {
socket.emit('send:message', {
message: $scope.message
});
// add the message to our model locally
$scope.messages.push({
user: $scope.name,
text: $scope.message
});
// clear message box
$scope.message = '';
};
// ================
var init = function () {
$scope.newName = security.currentUser.username;
$scope.changeName();
}
if ($rootScope.hasLoaded() && $scope.name != security.currentUser.username) {
init();
} else {
$rootScope.$on('info-loaded', init);
}
}
//]
);
As well as the Chat page itself. The strange thing is that connected users and messages display correctly, but the controller can't seem to retrieve the typed message.
<div class='col'>
<h3>Users</h3>
<div class='overflowable'>
<p ng-repeat='user in users'>{{user}}</p>
</div>
</div>
<div class='col'>
<h3>Messages</h3>
<div class='overflowable'>
<p ng-repeat='message in messages' ng-class='{alert: message.user == "chatroom"}'>{{message.user}}: {{message.text}}</p>
</div>
</div>
<div class='clr'>
<form ng-submit='sendMessage()'>
Message: {{message}}<br/>
<input size='60', ng-model='message'/>
<input type='submit', value='Send as {{name}}'/>
</form>
</div>
When pressing the "Send" button, AngularJS successfully calls the sendMessage function, but retrieves the "message" value as an empty string, leading it to send an empty socket.io message.
I'm quite new to AngularJS, so my approach might be totally ridiculous. I'm convinced I'm missing something obvious but after re-reading the docs again, I really can't seem to find what.
Is this a proper way to organise an AngularJS app?
Thanks in advance for your help.
Having recently built a large scale Angular/Socket.IO application, I strongly suggest that you put all of your Socket implementation into a Service. This service will maintain all of your socket state, and allow you to inject it into any required controllers. This will allow you to have a main page for Chat, however still be able to display notifications, chat user information, etc in other areas of your application.
It's not about your problem, but I saw something I suspect to be wrong.
When you use another library with angularjs, you should use a bridge to it (angular-socket-io for example).
When you do an $http call with angular, it updates $scope correctly in the callback and the changes are seen in the view.
In your code:
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
There is a problem: "socket" isn't a library included in angularjs, so when the callback is called, your "$scope" modification isn't correctly noticed to angularjs.
You have to do use $scope.$apply(function() { code here which modifies $scope });
Example:
socket.on('send:message', function (message) {
$scope.$apply(function() {
$scope.messages.push(message);
});
});
EDIT:
I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
Either store the datas in a global variable, or use $rootScope which is the parent scope of all the $scope you use in the application.
EDIT 2:
In fact it should solve your problem ;)
Another things:
1) use $rootScope instead of $scope for global variables (or a global variable). In any $scope you will access $rootScope variables ($scope is a copy of either $rooScope or a parent $scope).
2) register socket.io only once. Currently, if you change pages, you will register new callbacks at EACH page change.
I have a snippet of code here running in a node application
for( i=0; i < Equipment.length; i += 1 ) {
Equipment[i].NumberForSearch = Equipment[i].ClassPrefix + "-" + Equipment[i].UnitNumber;
console.log(Equipment[i].NumberForSearch);
}
console.log(Equipment[0]);
Equipment is an array of objects and I am trying to add a property to each object in that array called "NumberForSearch".
In my application the way we want to display unit numbers for equipment is "ClasssPrefix"-"UnitNumber". The reason I am wanting to join them here into one variable is that inside my angular application, I want the user to be able to type 12345-12345 inside of a search field which then filters out the Equipment. This doesnt work if I have {{ClassPrefix}}-{{UnitNumber}} for obvious reasons, angular doesnt know that the - even exists.
The problem is that inside my for loop everything is checking out fine. Its logging just as its supposed to, so I figured it worked. When I checked the front end and changed it to display "NumberForSearch", nothing showed up.
I then added the logging statement outside the for loop to check just one of my objects to see if the field even existed and it doesnt. So my question is, why is this snippet not adding the "NumberForSearch" field in my object?
You have to make object with this field before setting to $scope's parameter.
Have a look at init() function.
After adding NumberForSearch field we defined array of objects to parameter in scope.
Of course ng-repeat will loop through it and render elements.
So we will add "| filter:query" to show only needed
<div ng-controller="EquipmentController">
<input type="text" ng-model="query.NumberForSearch" placeholder="search">
<ul>
<li ng-repeat="Item in Equipment | filter:query">{{Item.ClassPrefix}}-{{Item.UnitNumber}}</li>
</ul>
</div>
js part:
var App = angular.module('App', []);
App.controller('EquipmentController', function ($rootScope, $scope, $http, $filter) {
$scope.Equipment = [];
$scope.init = function() {
var request = $http({
method: 'get',
url: '/path/to/equipment/resource'
});
request.success(function (response) {
var Equipment = response.Equipment;
for(var i in Equipment) {
Equipment[i].NumberForSearch = Equipment[i].ClassPrefix + "-" + Equipment[i].UnitNumber;
}
$scope.Equipment = Equipment;
$scope.$apply();
});
}
$scope.init();
});
another way of filtering is to send search field to route, in another word we send query to server and it filters response on serverside.
<div ng-controller="EquipmentController">
<input type="text" ng-model="queryNumber" ng-change="searchByNumber()" placeholder="search">
<ul>
<li ng-repeat="Item in Equipment">{{Item.ClassPrefix}}-{{Item.UnitNumber}}</li>
</ul>
</div>
js part:
var App = angular.module('App', []);
App.controller('EquipmentController', function ($rootScope, $scope, $http, $filter) {
$scope.Equipment = [];
$scope.all = function() {
var request = $http({
method: 'get',
url: '/path/to/equipment/resource'
});
request.success(function (response) {
$scope.Equipment = response.Equipment;
$scope.$apply();
});
}
$scope.all();
$scope.searchByNumber = function() {
var request = $http({
method: 'get',
url: '/path/to/equipment/resource?number='+$scope.queryNumber
});
request.success(function (response) {
$scope.Equipment = response.Equipment;
$scope.$apply();
});
}
});
I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/