I have an application that allows the user to create and edit records in a modal Angular Material Design Dialog ($mdDialog)
My problem is to put the object returned by the dialog into a collecion that is in the main controller. Is there a way to do that?
angular.module("module").controller("mainController", function ($scope, $mdDialog) {
$scope.Users = [];
function OpenEditWindow(userToEdit) {
$mdDialog.show({
templateUrl: 'Views/user.html',
controller: 'UserDialogController',
clickOutsideToClose: true,
locals: { // Envia valores para o controller do dialog
User: userToEdit
}
}).then(function (data) {
// Put the object edited into the collection on main controller, to show on the screen
$scope.Users.push(data); // ******** NOT WORKS
});
}
});
angular.module('module')
.controller('UserDialogController', function ($scope, $mdDialog, User) {
$scope.User = User;
$scope.Save = function () {
$mdDialog.hide($scope.User);
}
});
Maybe you can centralize your data and create a service model that persists the state of your user across your application. Such a service can be passed into your controller like every other dependency.
angular.module('module')
.controller('UserDialogController', function ($scope, $mdDialog, User, UserModel) {
$scope.User = User;
$scope.Save = function () {
$mdDialog.hide($scope.User);
}
});
angular.module('module').factory('UserModel', function () {
var userModel = this;
userModel.set = function(){
...
}
return userModel;
});
Given that services are singletons you are guaranteed to have access to the latest and greatest information every time.
Try using a style guide which will greatly improve your code, logic and overall quality. Such a guide could be John Papa's Angular Style Guide
What is returned from $mdDialog.show is a promise. You are putting your then in the wrong place. This is shown in the angular material docs here
angular.module("module").controller("mainController", function ($scope, $mdDialog) {
$scope.Users = [];
function OpenEditWindow(userToEdit) {
var promise = $mdDialog.show({
templateUrl: 'Views/user.html',
controller: 'UserDialogController',
clickOutsideToClose: true,
locals: { // Envia valores para o controller do dialog
User: userToEdit
}
});
promise.then(function (data) {
// Put the object edited into the collection on main controller, to show on the screen
$scope.Users.push(data); // ******** NOT WORKS
});
}
});
Related
In my angular project I'm using Angular.js material. And I want to show $mdialog with custom controller, where user changes some data and this data should be applied to my $scope variable. Example what I do now:
function myControllerFn($scope, MyService){
// I do copy of my service variable because I don't want to change it until user will click save button
$scope.name = angular.copy(MyService.name);
$scope.editCurrentProfile = function() {
$scope.showEditProfileDialog($scope.name).then(function(name){
$scope.name = name;
}
}
$scope.showEditProfileDialog = function(name) {
var deferred = $q.defer();
$mdDialog.show({
controller: 'editProfileViewCtrl',
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name,
deferred: deferred
}
});
return deferred.promise;
};
}
Then in dialog controller I do:
function editProfileViewCtrl($scope, name, deffered) {
deferred.resolve('newName');
}
But I think it is the wrong way. So what is the best way to communicate between two view controllers in angular without new service ? Or better create another service like: EditDialogService, where I will save results ?
When you open a modal, the show() function returns a promise.
$scope.showEditProfileDialog = function(name) {
var modalInstance = $mdDialog.show({
controller: 'editProfileViewCtrl',
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name
}
});
modalInstance.then(function(result){
// acces what is returned
// In your case, you would do
$scope.name = result;
}, function(error){
// Usually when you cancel your modal
});
}
Your modal controller can be injected with $mdDialog.
function editProfileViewCtrl($scope, name, $mdDialog) {
$scope.close = function() {
$mdDialog.hide('newName');
}
}
You should create a directive with your user as scope variable. Angular in itself is handling the data binding.
It is possible to create a minimal controller function that has access to $scope.
$mdDialog.show({
controller: function () { this.parent = $scope; },
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name,
deferred: deferred
}
});
I'm trying to retrieve data from Angularfire using a service, and then setting the returned value to my scope in my controller.
When I run the code below, I get undefined back for scope.sessions.
SERVICE:
app.factory('sessions', function(){
var refToSessions = new Firebase('myFireBaseURL');
var allSessions = [];
return {
getSessions: function () {
refToSessions.on("value", function (sessions) {
allSessions.push(sessions.val());
return allSessions;
});
}
};
});
CONTROLLER:
app.controller('SessionsCtrl', ['$scope', '$state', 'Auth', 'sessions', function($scope, $state, Auth, sessions){
$scope.sessions = sessions.getSessions();
$scope.submitSession = function() {
console.log($scope.sessions);
}
});
You're trying to return asynchronous data.
You are logging allSessions to the console before the data has downloaded from Firebase.
Use $firebaseArray from AngularFire.
app.constant('FirebaseUrl', '<my-firebase-url>');
app.service('rootRef', ['FirebaseUrl', Firebase);
app.factory('Sessions', function(rootRef, $firebaseArray){
var refToSessions = ref.child('sessions');
return $firebaseArray('sessions');
}
Then injection Sessions into your controller:
app.controller('SessionsCtrl', function($scope, $state, Auth, Sessions){
$scope.sessions = Sessions; // starts downloading the data
console.log($scope.sessions); // still empty
$scope.submitSession = function() {
// likely by the time you click here it will be downloaded
console.log($scope.sessions);
$scope.sessions.$add({ title: 'new session' });
};
});
The data starts downloading once it's injected into your controller. When it's downloaded, $firebaseArray knows to trigger $digest, so it appears on the page.
Since you're using ui-router, you can use resolve to make sure the data exists before injecting it into your controller:
app.config(function ($stateProvider) {
$stateProvider
.state("session", {
controller: "SessionsCtrl",
templateUrl: "views/sessions.html",
resolve: {
sessions: function(Sessions) {
// return a promise that will fulfill the data
return Sessions.$loaded();
}
}
})
});
Now you would change your controller code to this:
app.controller('SessionsCtrl', function($scope, $state, Auth, sessions){
$scope.sessions = sessions; // data is available since injected by router
console.log($scope.sessions); // logs the appropriate data
$scope.submitSession = function() {
$scope.sessions.$add({ title: 'new session' });
};
});
I'm facing the following situation in my Angular application and I would like to have some advices here.
I have a page where I show some products, this page is managed by a controller called 'ProductsController'. This controller has a method called 'showProductDetails' which is called once the user clicks on a specific product, and the goal of this method is just to retrieve the details of the product and to display these details in a modal panel.
Nothing really special until here. The problem is that because of modularity I would like to attach a different controller to the modal panel, and to manage all the logic of this modal panel in the new controller, in this case 'ProductDetailController'. The problem is that I retrieve the data of the product before opening the modal panel, but as I retrieve this data in the scope of the first controller, from the second controller I cannot access to the product that I have previously retrieved. I've been told that to share data between controllers in angularJs is done through services, but I don't see how a stateless service can help me here.
Here is my code to understand better the situation:
The first controller:
app.controller('ProductsController', ['$scope','productsFactory','commonFactory','productsFactoryHelper','$filter','$modal',function ($scope,productsFactory,commonFactory,productsFactoryHelper,$filter,$modal)
{
$scope.showProductDetails = function (size,product) {
$scope.showLoader('Loading the details of the product. Please wait...');
productsFactoryHelper.Product.query({id:product.id},function(response)
{
$scope.selectedProduct=response;
$scope.hideLoader();
var modalInstance = $modal.open({
templateUrl: 'productDetail.html',
controller: 'ProductDetailController',
size: size
});
},function(error)
{
commonFactory.Pop('error','This product is not available at this moment. Please try again later. If the problem persists contact a system administrator');
$scope.hideLoader();
});
};
_init();
}]);
And the second controller:
app.controller('ProductDetailController',['$scope','$modalInstance', function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);
So basically the question is how can access from the 'ProductDetailController' to the object 'selectedProduct' which is in the scope of the 'ProductsController'.
Thank you for your help.
Use resolve of the $modal to send your data to the new controller like below.
app.controller('ProductsController', ['$scope','productsFactory','commonFactory','productsFactoryHelper','$filter','$modal',function ($scope,productsFactory,commonFactory,productsFactoryHelper,$filter,$modal)
{
$scope.showProductDetails = function (size,product) {
$scope.showLoader('Loading the details of the product. Please wait...');
productsFactoryHelper.Product.query({id:product.id},function(response)
{
$scope.selectedProduct=response;
$scope.hideLoader();
var modalInstance = $modal.open({
templateUrl: 'productDetail.html',
controller: 'ProductDetailController',
size: size,
resolve:{
"selectedProduct":response
}
});
},function(error)
{
commonFactory.Pop('error','This product is not available at this moment. Please try again later. If the problem persists contact a system administrator');
$scope.hideLoader();
});
};
_init();
}]);
I dont know about the producfactory helper product query has a promise if it has a promise you can use like this..
$scope.showProductDetails = function (size,product) {
$scope.showLoader('Loading the details of the product. Please wait...');
var modalInstance = $modal.open({
templateUrl: 'productDetail.html',
controller: 'ProductDetailController',
size: size,
resolve:{
"selectedProduct":productsFactoryHelper.Product.query({id:product.id})
}
});
};
And in the ProductDetailController you can inject this selectedProduct like below
app.controller('ProductDetailController',['$scope','$modalInstance','selectedProduct ' function ($scope, $modalInstance,selectedProduct ) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);
This can indeed be done through services, since they are stateless and keep their data once instantiated.
function productService($http) {
this.products = [];
this.loadProducts() {
$http.get('/url/to/your/product/api').then(function(err, data) {
this.products = data.products;
});
};
this.getProducts = function() {
return this.products;
}
}
angular
.module('yourModule')
.service('productService', productService);
You can then just inject productService in both controllers, load the products using productService.loadProducts(), and get them using productService.getProducts().
This is just an example. Services can be used to share any kind of data.
Services are indeed the answer for you, or you can use pure eventing if you do not need to access the data more then once.
Pure Eventing
app.controller('parentCtrl', function($scope) {
// Do something
// Action completed
#scope.$emit('someactionComplete', data);
});
app.controller('childCtrl', function($scope) {
$scope.$on('someactionComplete', function(data) {
// Process data
});
});
Using a service. The advantage of using a service is that the data is persisted.
app.controller('parentCtrl', function($scope, MyService) {
// Do something
// Action completed
MyService.setData(data);
#scope.$emit('someactionComplete');
});
app.controller('childCtrl', function($scope) {
$scope.$on('someactionComplete', function() {
MyService.getData(data);
});
});
You could further enhance this were the service loaded the data and returns a promise in the getter.
I'm newer in AngularJS. So I have a simple question, but I can't find answer. I have code:
angular.module('app', ['app.controllers', 'ngRoute']).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/users', {templateUrl: '../pages/list.html', controller: 'UserListCtrl'}).
when('/user-details/:login', {templateUrl: '../pages/form.html', controller: 'UserCtrl' /* and here I need to call userDetails(login) from UserCtrl */}).
otherwise({redirectTo: '/users'});;
}
]);
app.controller('UserCtrl', function ($scope, $http, $location) {
$scope.userDetails = function (login) {
$http.get(url + login).success(function (data) {
$scope.user = data[0];
console.log('tst');
}).error(errorCallback);
};
$scope.createUser = function (user) {
$http.post(url, user).success(function (data) {
$location.path('/users');
}).error(errorCallback);
};
});
My problem is: I don't know how to call specific method of controller when routing matches. I need to call method and give to it parameter :login from routing. How to solve this?
Thanks for your answers
If I understand correctly, you are re-using the same controller for two parts of the view (or for two views), one for creating a user and one for fetching the details of the current user.
Since these two aspects are totally different, it is not advisable to use the same controller for both. The controllers should be different and any common or re-usable functionality should be shared through a service.
In any case, code that makes calls to the backend should not be placed inside controllers, but into services. E.g.:
app.service('UserSrv', function ($http) {
var url = '...';
this.userDetails = function (login) {
return $http.get(url + login);
};
this.createUser = function (user) {
return $http.post(url, user);
};
});
app.controller('UserCtrl', function ($scope, UserSrv) {
var login = '...';
var errorCallback = ...;
// Fetch user details upon initialiation
UserSrv.userDetails(login).success(function (data) {
$scope.user = data[0];
}).error(errorCallback);
});
app.controller('NewUserCtrl', function ($location, $scope, UserSrv) {
var errorCallback = ...;
$scope.createUser = function (user) {
UserSrv.createUser(user).success(function (data) {
$location.path('/users');
}).error(errorCallback);
};
});
You could, also, use $routeProvider's resolve property to "preload" the user's details and pass it to the UserCtrl as an argument.
I need to do a request inside the RUN method to retrieve de user data from an api.
The first page (home), depends on the user data.
This is the sequence of dispatchs in my console:
CONFIG
RUN
INIT GET USER DATA
SIDEBAR
HOME
SUCCESS GET USER DATA
My problem is, i need to wait user data before call sidebar and home (controller and view) and i don't know how can i do this.
UPDATE
I have this until now:
MY CONFIG:
extranet.config(['$httpProvider', '$routeProvider', function ($httpProvider, $routeProvider) {
// My ROUTE CONFIG
console.log('CONFIG');
}]);
My RUN:
extranet.run(function($rootScope, $location, $http, Cookie, Auth, Session) {
console.log('RUN');
var token = Cookie.get('token');
// The login is done
var success = function (data) {
Session.create(data);
console.log('USER DATA SUCCESS');
};
var error = function () {
$location.path('/login');
};
// GET USER DATA
Auth.isAuthenticated().success(success).error(error);
});
MY CONTROLLER MAIN:
extranet.controller('MainCtrl', function ($scope, $location) {
console.log('MAIN CONTROLLER');
});
By using resolver
extranet.config(['$httpProvider', '$routeProvider', function ($httpProvider, $routeProvider) {
// My ROUTE CONFIG
$routeProvider.when('/', {
templateUrl: "/app/templates/sidebar.html",
controller: "siderbarController",
title: "EventList",
resolve: {
events: function ($q, Cookie,Session) {
var deffered = $q.defer();
Cookie.get('token').$promise
.then(function (events) {
Session.create(data);
console.log('USER DATA SUCCESS');
deffered.resolve(events);
}, function (status) {
deffered.reject(status);
});
return deffered.promise;
}
}
}]);
I hope you get some idea.
If you are using AngularJS methods for server requests you will get a promise. A promise gets resolved as soon as the response is recieved. All defined callbacks "wait" until the resolve.
Naive solution
So, you will use $http or even $resource if you have a REST-like backend:
var promise = $http.get(userDataUrl, params)
$rootScope.userDataPromise = promise;
After that you can use that promise whereever you need the data:
$rootScope.userDataPromise.then(myCallback)
Better solution
Using $rootScope for that purpose is not an elegant solution though. You should encapsulate the Userdata stuff in a service and inject it whereever you need it.
app.factory('UserData', ['$http',
function($http) {
var fetch = function() {
return $http.get(userDataUrl, params)
};
return {
fetch: fetch
};
}
]);
Now you can use that service in other modules:
app.controller('MainCtrl', ['$scope', 'UserService',
function ($scope, UserService) {
var update = function(response) {
$scope.userData = response.userData;
}
var promise = UserService.fetch();
promise.then(update)
}
);