I'm an angular newb. I've followed this tutorial http://www.bradoncode.com/tutorials/learn-mean-stack-tutorial/
and have slightly modified things (I have items instead of categories).
Basically I've created a MEAN app, with two models, users and items. I would like to add a property to the items model called "owner" which stores the username of the user who created the model. This isn't too hard, but a problem arises if the user changes their username - the "owner" property doesn't change in the item model instances created by the user. From what I gather there are ways of syncing data between views/models/controllers, but how would I sync data between different model instances of different models. Any help is appreciated, thanks.
Here is item.client.controller.js
'use strict';
// Items controller
angular.module('items').controller('ItemsController', ['$scope','$stateParams', '$location', 'Authentication', 'Items',
function($scope, $stateParams, $location, Authentication, Items) {
$scope.authentication = Authentication;
$scope.currentPage = 1;
$scope.pageSize = 10;
$scope.offset = 0;
// Page changed handler
$scope.pageChanged = function() {
$scope.offset = ($scope.currentPage - 1) * $scope.pageSize;
};
// Create new Item
$scope.create = function() {
// Create new Item object
var item = new Items ({
name: this.name,
description: this.description,
owner: $scope.authentication.user.username
});
Here is items.client.services.js
'use strict';
//Items service used to communicate Items REST endpoints
angular.module('items').factory('Items', ['$resource',
function($resource) {
return $resource('items/:itemId', { itemId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
And users.client.services.js
'use strict';
// Users service used for communicating with the users REST endpoint
angular.module('users').factory('Users', ['$resource',
function($resource) {
return $resource('users', {}, {
update: {
method: 'PUT'
}
});
}
]);
Related
I am trying to implement server side pagination in Angular JS .I have created my server response consisting of results and a variable called isMoreResults . When isMoreResults is true I can safely assume that it has more results to be displayed . Now how can I go about creating a pagination on angular Js side ,such that I can display PREVIOUS and NEXT . when user clicks on next I can call the server to fetch next response .Any leads on how can I implement and what is the correct format to implement pagination ? I have went through many sites and majoritily I could see client side validation .should I also switch to client side validation ?
You can do something like this.
angular.module('app', ['ui.bootstrap']);
angular.module('app').controller('PaginationDemoCtrl', function($scope, $http) {
$scope.currentPage = 1;
$scope.limit= 10;
$scope.tracks = [];
getData();
function getData() {
$http.get("https://api.spotify.com/v1/search?query=iron+&offset="+($scope.currentPage-1)*$scope.limit+"&limit=20&type=artist")
.then(function(response) {
$scope.totalItems = response.data.artists.total
angular.copy(response.data.artists.items, $scope.tracks)
});
}
//get another portions of data on page changed
$scope.pageChanged = function() {
getData();
};
});
in html
<div ng-controller="PaginationDemoCtrl">
<h4>Sample Server Pagination</h4>
<uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()" items-per-page="100"></uib-pagination>
</div>
Usually do this for pagination:
1 - Create an API that takes how many SKIP and how many TAKE .. somehting like:
http://www.test.com/api/Users?skip=0&take=10
Then:
2 - in Angular install this plugin: (maybe with bower or npm)
https://github.com/brantwills/Angular-Paging
3 - in your HTML something like:
<paging page="currentPage"
page-size="pageSize"
total="total"
paging-action="DoPaging(page, pageSize, total)">
</paging>
4 - In your Controller:
/**
* MainCtrl - controller
*/
"use strict";
angular
.module('demo')
.controller('UsersCtrl', [
"$scope",
"User",
"$state",
"$timeout",
function (
$scope,
User,
$state,
$timeout) {
var vm = this;
// Binded Functions
$scope.currentPage = 1;
$scope.pageSize = 10;
$scope.DoPaging = _doPaging;
// Page Load
init();
/////////////////////////////////
// PRIVATE FUNCTION
////////////////////////////////
//Page Load
function init() {
$scope.promUsers = User.GetUsers(0, 10).$promise.then(function (resp) {
vm.users = resp;
$scope.total = resp[0].total //<-- put in your Back End Entities a field where you store how many record you have on your DB (all)
}).catch(function (err) {
console.log(err);
});
}
function _doPaging(text, page, pageSize, total) {
var skip = pageSize * (page -1);
User.GetUsers(skip, pageSize).$promise.then(function(resp){
vm.users = resp;
$scope.total = resp[0].total
}).catch(function (err) {
console.log(err);
});
}
////////////////////////////////
}]);
5 - In your service:
"use strict";
angular
.module("demo")
.factory("User", [
"$resource",
"$q",
"enviroment",
"$timeout",
function (
$resource,
$q,
enviroment,
$timeout) {
// Private Filed
var _serviceBase = "api/User/";
// Private Method
//skip and take in query string
var resource = $resource(enviroment.apiUrl + _serviceBase, { skip: '#skip', take: '#take' }, {
query: { method: "GET", isArray: true },
create: { method: "POST", isArray: false },
update: { method: "PUT", isArray: false }
});
// Public Method
return {
GetUsers: function (skip, take) {
return resource.query({ skip: skip, take: take });
}
};
}]);
First of all, I know this error has been covered many times around here, and I read thoroughly many questions about this but no one seems to fit my problem exactly. Why? Well most of them are not related to the Angular UI modal and the way it asks for the resources to display.
I'll start showing you my controller:
'use strict';
// Customers controller
angular.module('customers').controller('CustomersController',
['$scope', '$stateParams', '$location', '$filter', 'Authentication', 'Customers', '$modal', '$log',
function($scope, $stateParams, $location, $filter, Authentication, Customers, $modal, $log ) {
// Find a list of Customers
$scope.find = function() {
Customers.query(function (data) {
$scope.customers = data;
$scope.buildPager();
});
};
// Find existing Customer
$scope.findOne = function() {
$scope.customer = Customers.get({
customerId: $stateParams.customerId
});
};
//List Pager
$scope.buildPager = function () {
$scope.pagedItems = [];
$scope.itemsPerPage = 15;
$scope.currentPage = 1;
$scope.figureOutItemsToDisplay();
};
/* Modal stuff */
$scope.animationsEnabled = true;
$scope.openUpdateModal = function (size, selectedCustomer) {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'modules/customers/views/edit-customer.client.view.html',
controller: function ($scope, $modalInstance, upcustomer) {
$scope.upcustomer = upcustomer;
},
size: size,
resolve: {
upcustomer: function () {
return selectedCustomer;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
//Controller Closing brackets
}
]);
In the view I have a list of customers, so if I click one of them I want the modal to open:
<a data-ng-repeat="customer in pagedItems" ng-click="openUpdateModal('lg', customer)" class="list-group-item"></a>
I did this thing based on a Bossable.com video tutorial called: MEAN Stack: 20 - Pass Customer Details to an Angular UI Modal
The only difference is that I got this error when clicking a customer:
[$resource:badcfg] Error in resource configuration. Expected response to contain an object but got an array
Thanks for your time.
Just remove the $scope.findOne function because it uses the get method and returns and array. If you look back at her tutorial, she kept it commented.
Your problem is to do with your Customer.query() or Customer.get() call. These are $resource calls and expect to be told what the response is.
Usually a get method expects an object while the query method expects an array. If you're not return one of those for those methods you need to configure it using the isArray: true/false option depending on your needs.
Documentation on this can be found:
https://docs.angularjs.org/api/ngResource/service/$resource
$resource( 'apiendpoint', {}, {
query: {
isArray: false
}
});
}
I have a service which I would like to use across multiple controllers. The services is defined like this:
app.factory('Data', ['$http',
function($http) {
var Data = this;
var theProduct = {};
return {
product: function() {
return theProduct;
},
getProduct: function(ext_id) {
return $http.post('get_product', {
product_id: ext_id
}).success(function(data) {
theProduct = data;
});
}
}
}
]);
I have a form, that uses the ProductFormController to retrieve product data when it's submitted. That controller looks like this:
app.controller('ProductFormController', ['$scope', '$http', 'Data',
function($scope, $http, Data) {
/*
* Get a product and all of it's data from the server
*/
$scope.getProduct = function() {
Data.getProduct($scope.extProductId).success(function(data) {
console.log(data);
});
}
}
]);
Then, I have an AppController, which should display certain sections when a product object exists in the Data service.
<div class="row" id="productInfo" ng-show="product().id" ng-controller="AppController">/div>
Within AppController, I have this line:
$scope.product = Data.product;
I would like the productInfo div to show whenever a product object exists in the Data service, but it seems that the variable never gets updated. I've seen this question, but do not believe the accepted answer actually answers the question:
Angular: share asynchronous service data between controllers
I have two angular services that need to share models (a list of messages and an individual message), which they get from a call to our API. The service is as follows:
angular.module('CmServices', ['ngResource'])
.factory('Messages', function ($resource, $routeParams, $rootScope) {
var data = {};
data.rest = $resource(url, {}, {
query: {method:'GET', params: params},
post: {method:'POST', params: params}
});
// Trying to set this through a call to the API (needs to get param from route)
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
var messages = data.rest.query({m_gid: $routeParams.gid}, function () {
data.messages = messages;
});
});
return data;
});
and the controllers are:
function MessagesCtrl ($scope, $http, $location, $routeParams, Messages) {
$scope.messages = Messages.messages;
}
function MessageCtrl ($scope, $http, $location, $routeParams, Messages) {
$scope.messages = Messages.messages[0];
}
But neither of the controllers update when the data loads from the REST API (I've logged the data coming back, and it definately does).
Instead of assigning a new array to data.messages like this:
data.messages = messages
use angular.copy() instead, which will populate the same array:
angular.copy(messages, data.messages)
That way, the controllers will see the update.
The problem is that you are returning a different version of data to each controller. I would place messages in $rootScope. So
data.rest.query({m_gid: $routeParams.gid}, function () {
$rootScope.messages = messages;
});
Incidentally, what is the purpose of setting the return value of data.rest.query to var messages? That variable gets blown as soon as you leave the function.
I'm building a simple Contact Management app with Crud using AngularJS 1.0.0rc8.
Getting a list of currently existing contacts is no problem, but while attempting to save a new Contact to the server, a new row is created - complete with the correct id, created_at, and updated_at values - but the rest of the models data is ignored.
Here is a screenshot to show what I mean:
As you can see, numbers 4 and 5 were given the Id's but first_name, last_name, and phone_num were not saved to the database.
I am using a $scope.addContact function within the Controller that deals with the object.
Here is the entire code for the Contact List Controller:
'use strict';
function ContactListCtrl($scope, $http) {
$http.get('/contacts').success(function(data) {
$scope.contacts = data;
});
$scope.addContact = function(data) {
$http.post('/contacts/', data).success(function(data) {
console.log(data);
data.first_name = $("#new_contact_first_name").val();
data.last_name = $("#new_contact_last_name").val();
});
this.newFirstName = '';
this.newLastName = '';
};
};
After clicking 'Save' on the new-contact.html partial, the Object is logged to the Console, if I inspect its contents, than sure enough the values are collected - notice Jimi Hendrix is there:
Here is the form as it appears in the new-contact.html template:
<form id="contact_form" ng-submit="addContact()">
<input type="text" id="new_contact_first_name" name="newContactFirstName" ng-model="newFirstName" placeholder="First Name"></br>
<input type="text" id="new_contact_last_name" name="newContactLastName" ng-model="newLastName" placeholder="Last Name"></br>
<input type="button" id="contact_submit_btn" value="Add Contact" class="btn btn-primary">
</form>
The addContact() function is fired after the form is submitted with JQuery:
$(document).ready(function(){
$("#contact_submit_btn").click(function(){
$("#contact_form").submit();
});
});
(Something tells me that I may not be using the ng-model attributes correctly.)
Any ideas on where I am going wrong with this? Or ideas on how I can better go about implementing this design?
Thanks.
UPDATE BELOW:
Here is my entire updated controller code - with help from Sathish:
// contacts controllers
'use strict';
function ContactListCtrl($scope, $http, Contacts) {
$scope.contacts = Contacts.index();
$scope.addContact = function() {
var newContact = {
first_name: $scope.newContactFirstName,
last_name: $scope.newContactLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data
$scope.newContactFirstName = "";
$scope.newContactLastName = "";
})
}
};
ContactListCtrl.$inject = ['$scope', '$http', 'Contacts'];
function ContactDetailCtrl($scope, $routeParams, Contacts) {
$scope.contact = Contacts.get( {contact_id: $routeParams.contact_id} );
}
ContactDetailCtrl.$inject = ['$scope', '$routeParams', 'Contacts'];
I am now receiving the error: Unknown Provider for Contacts. Here is a screenshot of the error
Ok, I managed to fix that error by providing a ngResource to the main App file. Here's what it looks like:
// main app javascript file
'use strict';
angular.module('contactapp', ['ngResource']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/contacts', {template: 'assets/app/partials/contact-list.html', controller: ContactListCtrl}).
when('/contacts/new', {template: 'assets/app/partials/new-contact.html', controller: ContactListCtrl}).
when('/contacts/:contact_id', {template: 'assets/app/partials/contact-detail.html', controller: ContactDetailCtrl}).
otherwise({redirectTo: '/contacts'});
}]);
I am receiving a new error: WARNING: Can't verify CSRF token authenticity
Alright, managed to fix that problem too by adding a callback to the API controller:
Rails shows "WARNING: Can't verify CSRF token authenticity" from a RestKit POST
Now I am back to the original problem. When the create method is called, a new row is saved to the database, but the models data is not.
Awesome... finally got this thing working.
The problem was the $scope.addContact function. It was using the 'name' of the input instead of the ng-model binding called 'newFirstName' and 'newLastName' that resides in the template.
Here's what the updated function looks like:
$scope.addContact = function() {
var newContact = {
first_name: $scope.newFirstName,
last_name: $scope.newLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data
$scope.newFirstName = "";
$scope.newLastName = "";
})
}
This can be better implemented using a Contacts service. Please define a Contacts service in app/assets/javascripts/services.js.erb as shown below:
var servicesModule = angular.module('<your app name>',
[<list of modules needed by this app>]);
servicesModule.factory('Contacts', function($resource) {
var ContactsService = $resource('/contacts/:contact_id', {}, {
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
});
return ContactsService;
});
Change the addContact method in the controller as shown below:
function ContactListCtrl($scope, $http, Contacts) {
...
...
...
$scope.addContact = function () {
var newContact = {
first_name: $scope.newContactFirstName,
last_name: $scope.newContactLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data.
$scope.newContactFirstName = "";
$scope.newContactLastName = "";
});
};
...
...
...
}
ContactListCtrl.$inject = ['$scope', '$http', 'Contacts'];
In addition to this, you can simplify the $http.get(...) part also. You can use Contacts.index();
Note:
If you gave ng-app="MyAppName" then please replace <your app name> with MyAppName.
<list of modules needed by this app> needs to the replaced by a comma-separated list of strings representing any modules needed by your application.
Check the attr_accessible on your model. With new 3.2.3 rails all model attributes became protected from mass-assignment by default.