I am new to Node JS and MAEN stack , i am following a tutorial to MEANJS to learn it and in this screencast http://www.youtube.com/watch?v=HNpMCFB8TFI&list=PL6rhBJX0L3TWYrwrQIi1_MzQDvVhkUVPI&index=26 we get to create new customer . However, i am not able create a new customer as i am getting an error message says "object is not a function" which refers to this line "var customer = new Customers" as the console in google chrome suggests . here is the code
customersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
// Create new Customer
this.create = function() {
// Create new Customer object
var customer = new Customers ({
firstName: this.firstName,
surname: this.surname,
suburb: this.suburb,
country: this.country,
industry: this.industry,
email: this.email,
referred: this.referred,
phone: this.phone,
channel: this.channel
});
// Redirect after save
customer.$save(function(response) {
// Clear form fields
$scope.firstName = '';
$scope.surname = '';
$scope.suburb = '';
$scope.country = '';
$scope.industry = '';
$scope.email = '';
$scope.referred = '';
$scope.phone = '';
$scope.channel = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}
]);
kindly , note that the update function in the update controller works fine , here is the code .
customersApp.controller('CustomersUpdateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
// Update existing Customer
this.update = function(updatedCustomer) {
var customer = updatedCustomer;
customer.$update(function() {
//wont do anything as the modal will be closed
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}
]);
i really would appreciate your help , thanks in advance
In your dependencies list, you are passing in $stateParams, $location, and Authentication, which may not be needed.
CustomersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) {
Regardless, the order you specify in the dependencies array is the order that they will be passed into your controller. So, in your controller, "$scope" refers to $scope, but "Customers" refers to $stateParams.
You can probably change it to look like this:
CustomersApp.controller('CustomersCreateController', ['$scope', 'Customers',
function($scope, Customers ) {
Or, if you need those services:
CustomersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, $stateParams, $location, Authentication, Customers ) {
When you declare a controller in angular you pass in 2 parameters: the name of the controller and an array that contains all of the names of other variables and modules that the controller requires and finally the constructor method of the controller at the end of the array.
Now the method signature of the constructor method needs to line up with the array of parameters passed in through the array.
var myApp = angular.module('myApp');
myApp.controllers('myController', ['$scope', 'foo', 'bar', // note the position for these
function($scope, foo, bar) { // match the position of these
foo.doStuff(bar);
}
]);
So in your example you can see that you're mapping the parameters incorrectly
customersApp.controller('CustomersCreateController', ['$scope', '$stateParams', '$location', 'Authentication', 'Customers',
function($scope, Customers ) { // note the arrangement of these parameters
If you map the arrangement of the parameters in the function to what's declare in the array you'll see that the Customers object is actually mapped to the $stateParams object and you're calling that as a function.
If you arrange your required parameters to match the method signature of the constructor then your example should start to work (assuming that the other code is wired up correctly)
customersApp.controller('CustomersCreateController', ['$scope', 'Customers', '$stateParams', '$location', 'Authentication', // note that we moved 'Customers' to be next to '$scope'
function($scope, Customers ) {
The reason your other example works is that you're not actually using the Customers module anywhere in the code.
It also doesn't seem like you're using any of those other required variables and modules in these controller and if that is the case then you should remove those as well.
Now as to why angular does this? That's because when you put a JavaScript file through a minimizer tool it's very common for these tools to rename the passed in parameters of a function to reduce the over all size of a file.
E.g.
function(foo, bar) { /* code */ }
will become
function(a, b) { /* code */ }
Thus, this provides a way for angular to maintain a way to declare dependencies through modules and not have them removed via a minimizer.
Related
What seems to be an issue for me is how to use $state.go(state, params) and have params persist through to the state's controller, regardless of a component. The plunkr I've setup that demonstrates this is linked here: https://plnkr.co/edit/u5VaMZIBtVoBGbAZaQe0?p=preview
Basically what I'm doing is setting up 2 states, one links to the other. I'm using $state.go to go one of those states with passed parameters. It doesn't appear those parameters are passed though and I'm wondering why this is.
I'm using AngularJS version 1.6.6 and ui.router version 0.4.2 if that makes a difference or not.
var app = angular.module("yay", ['ui.router']);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
var states = [
{
name: 'paramsDontWork',
url: '/doesntwork',
template: '<div>hi{{variable}}</div><a style="color:blue;" ng-click="goto()">goto</a>',
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
$scope.variable = 'hi';
$scope.goto = function() {
$state.go('newplace', { should: "should"});
};
}],
},
{
name: 'newplace',
url: '/newplace',
template: 'new place {{should}} have params<br><a ui-sref="paramsDontWork">hi</a>',
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
console.log($stateParams);
$scope.should = $stateParams.should;
}]
}
];
states.forEach(function(state) {
$stateProvider.state(state);
});
$urlRouterProvider.when('', '/doesntwork');
}]);
Routes must explicitly specify any parameters that can be overwritten (e.g. transition from another route) as part of their state configuration.
Relevant guidance from the UI-Router documentation:
Any parameters that are not specified will be inherited from currently defined parameters. Only parameters specified in the state definition can be overridden, new parameters will be ignored.
(Emphasis mine.)
In your example, /newplace doesn't specify a params property to provide the additional non-URL parameter.
All you need to do to ensure value of should from /doesntwork gets passed is to explicitly declare it as part of /newplace as a parameter with some default value:
{
name: 'newplace',
...
params: { should: null },
}
Plunker fork to demonstrate the above.
Based on this tutorial, I have as my parent state a list of people. When I click on one of them, a new view is created in order to show the details for that person. In my URL I use the ID for that person, so it's rather easy to go and fetch the ID to be used in the child state. The problem is that I also want to pass information such as the name, e-mail, age, etc.
The code is as follows:
My routes:
angular
.module('appRoutes', ["ui.router"])
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
var TalentForceState_seeProfile = {
name: 'students',
url: '/seeProfile',
templateUrl: 'public/templates/talentforce_template.html',
controller: 'People_Controller'
}
var singleStudent = {
name: 'student',
parent: 'students',
url: '/:personId',
templateUrl: 'public/templates/person_template.html',
controller: 'Person_Controller'
}
....
Then, the controller for People:
talentforceApp
.controller('People_Controller', ['$scope', '$state', '$stateParams', 'StudentService', function($scope, $state, $stateParams, StudentService) {
StudentService.query().$promise.then(function(data) {
$scope.students = data;
});
}]);
Then, the controller for Person:
talentforceApp
.controller('Person_Controller', ['$scope', '$state', '$stateParams', 'StudentService', function($scope, $state, $stateParams, StudentService) {
$scope.student_id = $stateParams.personId;
console.log($stateParams)
}]);
Also, here's the HTML for the Person:
<div ng-controller="Person_Controller">
<h3>A person!</h3>
<div>Name: {{student_name}}</div>
<div>Id: {{student_id}}</div>
<button ui-sref="students">Close</button>
</div>
Information such as student_name is what I can't seem to pass from the parent state
I tried using solutions like this, that use $state.go, or this, that use params, but it always gives me errors such as param values not valid for state or the variables are undefined.
Any solution for this problem?
You can use angular's state resolve to achieve your requirement in a better way. Although there are many choices for it.
Procedure:
When your parent state loads, you query all the people in an API call.
In that response, I am assigning the response to an instance of a service using studentService.addStudents($scope.students); where addStudents is a function in the service.
Now, when you navigate to the detail of a person, I have used resolve which fetches the stored data from the service using the function studentService.getStudents() and returns a person object to the controller.
Use that person object directly in the person controller by injecting the resolve variable
I prefer using resolve. I will tell you why.
Here is the resolve you can use:
resolve: {
student: function(studentService, $stateParams) {
return studentService.getStudents().find(function(student) {
return student.id === $stateParams.personId;
});
}
}
You will add a service studentService or you can extend your own service.
Service:
talentforceApp.service('studentService', function(){
vm = this;
vm.students = []
this.addStudents = function(students) {
vm.students = students;
}
this.getStudents = function() {
return vm.students;
}
});
I added addStudents and getStudents methods to it.
One method add students to array and the other get the data of a studenr.
People Controller revised:
talentforceApp
.controller('People_Controller', ['$scope', '$state', '$stateParams', 'StudentService', function($scope, $state, $stateParams, StudentService,studentService) {
StudentService.query().$promise.then(function(data) {
$scope.students = data;
studentService.addStudents($scope.students); // this will create a students array in service instance
});
}]);
I assigned $scope.students to the service instance.
routes revised:
var TalentForceState_seeProfile = {
name: 'students',
url: '/seeProfile',
templateUrl: 'public/templates/talentforce_template.html',
controller: 'People_Controller'
}
var singleStudent = {
name: 'student',
parent: 'students',
url: '/:personId',
templateUrl: 'public/templates/person_template.html',
controller: 'Person_Controller',
resolve: {
student: function(studentService, $stateParams) {
return studentService.getStudents.find(function(student) {
return student.id === $stateParams.personId;
});
}
}
}
Now, you can use student from resolve into your controller, as a dependency.
person controller revised:
talentforceApp
.controller('Person_Controller', ['$scope', '$state', '$stateParams', 'StudentService',student, function($scope, $state, $stateParams, StudentService,student) {
$scope.student_id = $stateParams.personId;
console.log($stateParams)
$scope.student = student // this line add student to scope
console.log($scope.student)
}]);
Check your student object in the view:
View:
<div ng-controller="Person_Controller">
<h3>A person!</h3>
{{student}}
<div>Name: {{student_name}}</div>
<div>Id: {{student_id}}</div>
<button ui-sref="students">Close</button>
</div>
here is why I prefer to use resolve and its advantages
You can't pass a large object with nested properties with $state.go. You can use event: broadcast, emit. Creating a service that hold and share data to your controllers is a better way.
I have a controller that performs the following function which looks like this:
angular.module('events').controller('EventsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Events',
function($scope, $stateParams, $location, Authentication, Events) {
//initialize some stuff
$scope.create = function() {
// Create new Event object
var event = new Events ({
name: this.name,
details: this.details,
date: this.date,
time: this.time,
pointValue: this.pointValue,
studentIDs: this.studentIDs
});
// Redirect after save
event.$save(function(response) {
$location.path('events/' + response._id);
// Clear form fields
$scope.name = '';
$scope.details = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
//rest of the controllers functions go below
};
I am in the process of writing unit tests for this controller, however, I do not know how to inject a sample Event into this function or what to compare the function's output to in the tests. I am very new at Javascript and would appreciate all the help I can get
I have a master object called "categories", which is returned by the DataService, and called by many different Controllers.
.service('DataService', ['$http', '$q', '$timeout', function($http, $q, $timeout) {
var categories = {};
// Return public API.
return({
setCategory: setCategory,
getCategory: getCategory
});
function setCategory(activities, details) {
var name = details.shortName,
category = {};
category.activities = activities;
category.details = details;
categories[name] = category;
}
function getCategory(name) {
return categories[name];
}
What is the correct way to access categories from the controllers in order to maintain state between all controllers?
For example, in Controller1 I want to grab the categories object property that matches name, and then add new values to an activity contained in it:
$scope.category = getCategory(name);
$scope.category.activities[2].input = true;
I then want to be able to access the value of category.activities[2].input everywhere that uses the getCategories service. But as far as I can tell this won't work because I've reassigned categories[name] to $scope.category, right?
Or, am I wrong, or taking entirely the wrong approach? What is the best way to call objects from the categories object, in a way that will allow me to preserve state across all controllers?
Please do not use $broadcast. Use $watch
.value('categories',{})
.service("myService",function(categories,$rootScope) {
$rootScope.$watch(function(){
return categories;
},function(val){
console.log("Some controller, any controller, has updated categories!");
},true);
})
As a suggestion, you can call the DataService.setCategory each time a controller makes a change. Then you can use $broadcast to send a message that the service has been changed, and use $on to subscribe and trigger a refresh of the 'getCategory' method so you have the latest model in each of the controllers.
For example, in your service:
.service('DataService', ['$rootScope', '$http', '$q', '$timeout', function($rootScope, $http, $q, $timeout) {
function setCategory(activities, details) {
var name = details.shortName,
category = {};
category.activities = activities;
category.details = details;
categories[name] = category;
$rootScope.$broadcast('categories:updated');
}
And in the controllers:
.controller('ControllerOne', ['$scope', '$rootScope', 'DataService', function($scope, $rootScope, DataService) {
$scope.category = DataService.getService(name);
$scope.category.activities[2].input = true;
DataService.setCategory(category.activities, category.details);
}])
.controller('ControllerTwo', ['$scope', '$rootScope', 'DataService', function($scope, $rootScope, DataService) {
$scope.category = DataService.getCategory(name);
$scope.$on('categories:updated', function(){
$scope.category = DataService.getCategory(name)
});
}]);
I'm looking at the source code for the angular plugin of JQuery File upload, and I see the following code:
angular.module('blueimp.fileupload', [])
// The fileUpload service provides configuration options
// for the fileUpload directive and default handlers for
// File Upload events:
.provider('fileUpload', function () { ... })
.controller('FileUploadController', [
'$scope', '$element', '$attrs', '$window', 'fileUpload',
function ($scope, $element, $attrs, $window, fileUpload) {
...
// Observe option changes:
$scope.$watch(
$attrs.fileUpload,
function (newOptions) {
if (newOptions) {
$element.fileupload('option', newOptions);
}
}
);
so it seems obvious to me that the module was written to allow for me to update the options on the fileupload widget (which is what $element.fileupload('option', ...) does); however, I'm not sure how to get to the $attrs.fileUpload.
I need to update the options to fileupload after an async call in my controller:
var accountEditor = angular.module('accountEditor', [ 'ngResource', 'blueimp.fileupload' ]);
accountEditor.controller('accountEditorCtrl', [
'$scope',
'$resource',
'fileUpload',
function($scope, $resource, fileUpload) {
doAsyncThing(function() {
// update options...what goes here?
});
My current solution is a hack, and it is to mess with the options on a event callback (as described in How to change dynamically the upload (jquery-file-upload) url using $scope?). I consider this a hack because it requires user interaction for the options to get set, and also leads to a race condition where user interaction might occur before the async call is complete.
Do you have the view code?
I would imagine you would need to update the options attribute on the controller.
e.g.
View:
<div data-ng-controller="accountEditorCtrl">
<form data-ng-controller="FileUploadController" data-file-upload="options">
</options>
</div>
Controller:
var accountEditor = angular.module('accountEditor', [ 'ngResource', 'blueimp.fileupload' ]);
accountEditor.controller('accountEditorCtrl', [
'$scope',
'$resource',
'fileUpload',
function($scope, $resource, fileUpload) {
$scope.options = {}; // Set options here
doAsyncThing(function() {
$scope.options = {}; // Update options
// Note if this async method is not handled by angular you may need
// to call $scope.$apply(); to notify angular of the changes to scope
});