I'm struggling to display 'nested' [array] data in my modal. I am able to see all my data fetched from a url in angular, on my view page. However, when I ng-click on the same element to pop out a modal to see the same information on the modal, I can't see some of the information. In this case I cant the employees or developers.
url fetching code in angular
**controller used is 'HomeController'
**/comments is where my json data is located with 'employees'
being a one-to-many relationship entity in my backend (which might be the issue in my head)
var vm = this;
vm.projects = [];
$http.get('/comments')
.then(function(result) {
console.log(result);
vm.projects = result.data;
});
data as used in my modal template (not working code in modal)
<div ng-repeat="data in controller.projects">
<ul ng-repeat="employee in data.employees">
{{employee.name}}
</ul>
</div>
controller comes from <div ng-controller="HomeController as controller">
I have also tried this;
<div ng-repeat="employee in Project.Employees" class="selected-item">
{{employee.name}}
</div>
but doesn't work!
As seen below, the employees show up on the 'normal' page.
However, when I ng-click="editProject(data)" the above card to pop up the modal, everything else shown above (not shown below here) pops up except the employees. Could it be a routing issue?
Basically everything works perfect on the template view until I pop out the modal. This is the "modal popping" code;
$scope.editProject = function(data) {
$scope.showSelected = true;
$scope.SelectedProject = data;
var fromDate = moment(data.start).format('DD/MM/YYYY LT');
var endDate = moment(data.end).format('DD/MM/YYYY LT');
$scope.Project = {
ProjectID : data.projectID,
Client : data.client,
Title : data.title,
Description: data.description,
Employees: data.employees,
StartAt : fromDate,
EndAt : endDate,
IsFullDay : false
}
$scope.ShowModal()
},
//This function is for show modal dialog
$scope.ShowModal = function(){
$scope.option = {
templateUrl: 'modalContent.html',
controller: 'modalController',
backdrop: 'static',
resolve: {
Project : function () {
return $scope.Project;
},
SelectedProject : $scope.SelectedProject
}
};
Could there be a promise I'm missing in the ShowModal resolve?
Meanwhile in chrome devtools, the employees seemed to have been 'picked' up as shown below;
However when I open the object, interestingly it says array is empty;
I am new to angular and web development in general. Let me know if you need me to reveal my backend or any other code you think is missing. I'm just thinking it's an angular issue. Thanks for your help.
you need to pass projects as dependency injection to you modal instance controller:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($uibModalInstance, projects) {
var $ctrl = this;
$ctrl.projects = projects;
$ctrl.ok = function () {
$uibModalInstance.close();
};
$ctrl.cancel = function () {
$uibModalInstance.dismiss();
};
});
and resolve it in your opening function:
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($http, $uibModal, $log, $document) {
var $ctrl = this;
$http.get('data.json').then(function(result) {
console.log(result);
$ctrl.projects = result.data;
});
$ctrl.open = function (size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.modal-demo ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
controllerAs: '$ctrl',
size: size,
appendTo: parentElem,
resolve: {
projects: function () {
return $ctrl.projects;
}
}
});
};
});
please note also that controller name is passed with controllerAs property and not within HTML template.
plunker: https://plnkr.co/edit/wLI0pa07UNhr9Ld8CSg3?p=preview
Related
I am developping a MEAN stack application, and I got a question about using md-dialog from Angular Material.
On my webpage I have a calendar displayed with the events displayed on the calendar. When the user clicks on that date, I want a dialog popping up with a list and some info about the events taking place that day.
I have succesfully implemented the dialog behaviour of showing up when clicked on a date. Now my problem is that the data in the dialog isn't updated.
This is the function in my controller that's called when a date is clicked in the calendar
function dayClick(date){
vm.dateClicked = date;
getEventsByDay(date);
showDialog();
}
As you can see I then call another method in my controller to get the events taking place on the selected date from my database.
function getEventsByDay(date){
vm.eventsday = eventService.getEventsByDay(date).then(function(res){
vm.eventsday = res;
return vm.eventsday;
});
}
The events are successfully retreived from the database.
After we get the events are retreived, showDialog is called.
function showDialog(ev) {
$mdDialog.show({
parent: angular.element(document.body),
controller: MainController,
controllerAs: 'ctrl',
templateUrl: '/templates/dialogevent.html',
hasBackdrop: true,
panelClass: 'dialog-events',
clickOutsideToClose: true,
escapeToClose: true
});
}
So when using some logging in my code I found out that somehow the dialog is rendered and called before data is retreived. I tried to chain the methods to force them to execute after one another, but I can't seem to get that to work.
Any suggestions ?
Edit:
This is the template html
<md-dialog aria-label="Event Dialog">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Events on {{ctrl.dateClicked | date:"dd/MM/yyyy"}}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="cancel()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content style="max-width:800px;max-height:810px; ">
<md-list>
<md-list-item ng-repeat="event in ctrl.eventsday">
<div class="md-list-item-text">
<h4>{{event.name}}</h4>
<h5>{{event.eventType.name}}</h5>
<p>{{event.description}}</p>
</div>
</md-list-item>
</md-list>
</md-dialog-content>
Edit 2:
I'm initializing "vm.clickedDate" in the top of my controller
vm.dateClicked = new Date();
New date will give today's date. When I add events with today's date, the events are not shown in the dialog.
Edit 3:
This is the complete controller code
(function() {
'use strict';
angular.module('ptlab').controller('MainController', MainController);
MainController.$inject = ['$http', '$log', 'auth', '$state', '$stateParams', 'eventService', 'MaterialCalendarData', '$scope', '$mdDialog', '$timeout'];
function MainController($http, $log, auth, $state, $stateParams, eventService, MaterialCalendarData, $scope, $mdDialog, $timeout) {
var vm = this;
vm.users = [];
vm.getUsers = getUsers;
vm.openingsuren = [];
vm.events = [];
vm.setDayContent = setDayContent;
vm.dateClicked = new Date();
$scope.dayClick = dayClick;
$scope.cancel = cancel;
vm.showDialog = showDialog;
vm.eventsday = {};
vm.getEventsByDay = getEventsByDay;
vm.userStudent = true;
vm.userCoworker = true;
vm.userManager = true;
activate();
function activate() {
return load();
}
function load(){
getUsers();
getOpeningsuren();
getEvents();
}
function getUsers() {
return auth.getAll()
.then(function(data) {
vm.users = data.data;
return vm.users;
});
}
function getOpeningsuren(){
return $http.get('/javascripts/content.json').success(function(data){
vm.openingsuren = data.openingsuren.dag;
});
}
function getEvents(){
vm.events = eventService.getAll().then(function(res){
vm.events = res.data;
var evenement;
for(evenement of vm.events){
var content = createContentCalendar(evenement);
setDayContent(evenement.startdate, content);
}
return vm.events;
});
}
function setDayContent(date, content){
MaterialCalendarData.setDayContent(new Date(date), content);
}
function dayClick(date){
vm.dateClicked = date;
getEventsByDay(date);
}
function cancel(){
$mdDialog.cancel();
}
function createContentCalendar(evenement){
var string = "";
string += "<div class='item-box text-center'><h7>" + evenement.name + "</h7></div>";
return string;
}
function showDialog(ev) {
console.log("showDialog");
console.log(vm.eventsday);
$mdDialog.show({
parent: angular.element(document.body),
controller: MainController,
controllerAs: 'ctrl',
templateUrl: '/templates/dialogevent.html',
hasBackdrop: true,
panelClass: 'dialog-events',
targetEvent: ev,
clickOutsideToClose: true,
escapeToClose: true,
allowParentalScroll: true
});
}
function getEventsByDay(date){
vm.eventsday = eventService.getEventsByDay(date).then(function(res){
vm.eventsday = res;
$timeout(showDialog, 1000);
return vm.eventsday;
});
}
} })();
Edit4:
Tried like suggested by adding the following to showDialog:
locals: {
eventsday: vm.eventsday,
day: vm.dateClicked
},
controller: CalendarController,
controllerAs: 'ctrl'
I also made a controller called "CalendarController":
(function() {
'use strict';
angular.module('ptlab').controller('CalendarController', CalendarController);
CalendarController.$inject = ['$mdDialog', 'eventsday', 'day'];
function CalendarController($log, auth, $state, $stateParams, $mdDialog, eventsday, day) {
var vm = this;
vm.eventsday = eventsday;
vm.dateClicked = day;
function cancel(){
$mdDialog.cancel();
}
}})();
Now I get the error that "CalendarController" used in showDialog is not defined. I've added the calendarcontroller file to my script tags, so it should find the file in my index.ejs. Am I missing something or do I need to inject something else ?
Edit 5:
Found the solution. Everything works like in "Edit 4", just forgot the quotes around CalendarController in the show method.
You don't need to load MainController again in $mdDialog.show. It hasn't data in vm.eventsday during second-time-running.
Just create new controller and inject data there.
So you controller in $mdDialog.show is something like this
locals:{parent: vm},
controller: function () { this.parent = vm },
controllerAs: 'ctrl',
Or see example here https://material.angularjs.org/latest/api/service/$mdDialog it's similar your task
I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>
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
}
});
}
What I am trying to do here is:
Type in the new language name and click "Add" button, the new language will be added into the existing object.
For example: the existing object: {"default": "English"}, When I type in "German", a new object is added like this: {"default": "English", "German": "German"}
Here is my PLUNKER.
Could someone help me on that? Thanks so much, I will appreciate!
I would prefer to use events. Just subscribe one piece on some event like:
$rootScope.$on('myEvent', function(event, info){
// do something
});
And another one will fire it:
scope.$broadcast('myEvent', info);
The system glitched when I was trying to save your plunkr or I don't have a permission so here the code:
var app = angular.module('plunker', ['ui.bootstrap']);
app.factory('Data', function(){
var data =
{
Language: ''
};
return {
setLanguage: function(language) {
data.Language = language;
}
}
})
var ModalDemoCtrl = function ($scope, $modal, $log) {
$scope.languages = {"sch": "Simple Chinese"};
$scope.$on('newLanguageAdded', function(e, lang){
$scope.languages[lang] = lang;
});
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
languages: function () {
return $scope.languages;
},
newLanguage: function () {
return $scope.newLanguage;
}
}
});
};
};
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
var ModalInstanceCtrl = function ($scope, $modal, $modalInstance, languages, newLanguage) {
$scope.languages = languages;
$scope.ok = function () {
$modalInstance.close($scope.languages);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.openDialog = function () {
var modalInstance = $modal.open({
templateUrl: 'addNewLanguageDialog.html',
controller: AddNewLanguageCtrl,
});
}
var AddNewLanguageCtrl = function ($scope, $rootScope, $modalInstance, Data){
$scope.newValue = {text: ''};
$scope.$watch('newLanguage', function(newValue) {
if(newValue) Data.setLanguage(newValue);
});
$scope.add = function () {
alert($scope.newValue.text);
$rootScope.$broadcast('newLanguageAdded', $scope.newValue.text);
$modalInstance.close($scope.languages);
}
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
}
}
};
You can just copy this piece into plunkr instead yours.
Also change the layout:
<div class="modal-body">
<input ng-model="newValue.text">
</div>
Let me know if something doesn't work
You need to use a service, by definition singletons, and inject it in both models, adding a watch to the array in the service and updating accordingly in the scope of every model, from the values in the service.
An angular-ui way to achieve what you need would be to use these two basic methodologies found in the angular-ui documentation. See associated plunker for the answer below.
First is to use the close(result) inside the Instance Controller of the modal which updates the result promise property of the Instance Controller
Second is to use the result promise property to get the result(s) passed on to the close() method
Inside The AddNewLanguageCtrl is something like this
$scope.data = {newLanguage: ""};
$scope.add = function() {
$scope.close(data.newLanguage);
};
Inside the your addNewLanguageDialog.html script template
instead of using
<input ng-model="newLanguage">
use this
<input ng-model="data.newLanguage">
This is because whenever a modal is created, a new scope is created under the $rootScope(default) if a scope is not passed on to the options when the $modal.open() is invoked as stated in the angular-ui documentation. If you use newLanguage as the model then it won't receive any updates inside the AddNewLanguageCtrl. You can read this to get a better understanding of what I'm talking about regarding scopes
Inside the first modal ModalInstanceCtrl is something like this
$scope.newLanguages = [];
$scope.openDialog = function () {
var modalInstance = $modal.open({
templateUrl: 'addNewLanguageDialog.html',
controller: AddNewLanguageCtrl,
});
modalInstance.result.then(function(newLanguage) {
if(newLanguage)
$scope.newLanguages.push(newLanguage);
});
};
And then in your ModalDemoCtrl
$scope.languages = [];
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl
});
modalInstance.result.then(function(languages) {
$scope.languages = $scope.languages.concat(languages);
});
};
I'm trying to include an angular-ui modal in my web application but am having issues with getting everything set up.
The documentation indicate that you can use $modalInstance to inject the child controller into the parent controller but I don't quite understand how to go about doing so.
Here is the current code (it is straight from the modal demo from the documentation):
angular.module('myApp.controllers', []).
controller('addContent', function ($scope, $http, $modal, $log){
//modaltest
$scope.items = ['item1', 'item2', 'item3'];
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: 'newTermModal.jade',
controller: newTerms,
resolve: {
items: function () {
return $scope.items;
}
}
});
newTerm.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
}).
controller("newTerms",function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
When I run the app like it is now and click the button to open the modal (addTerm function) the app crashes with the error "ReferenceError: newTerms is not defined."
As I mentioned above, the angular-ui site indicates you can inject a controller with $modalInstance but I have not been able to figure out how.
a
Any help would be greatly appreciated!
EDIT
After adding the quotation marks on the pathname as suggested by Chandermani, it seems the modal is loading in the current page rather than the specified template.
I've changed the path to the following: templateUrl:
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: './views/partials/newTermModal.jade',
controller: 'newTerms',
resolve: {
items: function () {
return $scope.items;
}
}
});
A screenshot of the issue follows:
Any idea what could be causing this?
Well you can pass the controller as a string value. I took the default demo sample for modal and changed it to pass controller name instead of controller itself.
See my plunker http://plnkr.co/edit/jpJX4WvHw0SSYm3pAAzq?p=preview
So something like this
controller: 'newTerms',
should work.
I got the same problem, the modal loads main HTML file but not template.
My previous configuration was:
opens dialogs but dialog content is main HTML (like on your pic)
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'app/reports/modalContent.html',
controller : 'ModalInstanceCtrl',
resolve: {}
};
works as expected
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : '/app/reports/modalContent.html',
controller : 'ModalInstanceCtrl',
resolve: {}
};
Sounds like if you put wrong templateUrl, it by default uses main page HTML.
Be sure that you have right path for templateUrl
Hope it will help,
Have you tried to declare a dependency of 'ui.bootstrap' module? Like this:
angular.module('myApp.controllers', ['ui.bootstrap'])
Happened to me today, too. The templateUrl in the controller must match the id for the modal in the html file.
You need to define the newTerms controller before your other controller. Or you can change the code and just create a function inside your main controller with the name newTerms and remove the quotation marks for the name of your controller in your modal.
$scope.addTerm = function () {
var newTerm = $modal.open({
templateUrl: './views/partials/newTermModal.jade',
controller: newTerms,
resolve: {
items: function () {
return $scope.items;
}
}
});
var newTerms = function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}