We are new to AngularJS but are working on an AngularJS/Web API application that updates a data model from an AngularJS Bootstrap popover/directive.
We've successfully updated the database from the directive/popover, however are having trouble figuring out how to refresh the data on the page with the updated data without reloading the page.
Main Page CSHTML:
<div ng-app="FFPA" ng-controller="myCtrl">
<div svg-floorplan="dataset"></div>
</div>
Popover HTML:
<div>
<div>
ID: {{ person.Id }}<br />
Name: {{ person.fullName }}<br />
Current Cube/Office: {{ person.seatId }}
<br />
Dept: {{ person.deptId }}
<br />
Job Desc: {{ person.jobDesc}}
<br />
Phone:{{ person.phone}}
<br />
<!--<input type="button" value="Click Me" ng-click="changeName()">-->
</div>
<div class="hiddenDiv" ng-hide="toggle">
<div class="form-group">
<label for="floor">Floor</label>
<input id="floor" ng-model="person.floor" type="text" ng-trim="true" class="form-control" />
</div>
<div class="form-group">
<label for="section">Section</label>
<input id="section" ng-model="person.section" ng-trim="true" type="text" class="form-control" />
</div>
<div class="form-group">
<label for="offCubeNum">offCubeNum</label>
<input id="offCubeNum" ng-model="person.offCubeNum" ng-trim="true" type="text" class="form-control" />
</div>
<div class="form-group">
<label for="cbCube">Cubicle?</label>
<input id="cbCube" ng-model="person.cbCube" type="checkbox" size="1" class="checkbox" />
</div>
</div>
<div ng-hide="buttonToggle">
<input type="button" value="Move" class="btn btn-success" ng-click="moveEmp()">
<input type="button" value="Term" class="btn btn-danger" ng-click="changeName()">
</div>
<div ng-hide="submitToggle">
<input type="button" value="Submit" class="btn btn-success" ng-click="submitMove()">
<input type="button" value="Cancel" class="btn btn-warning" ng-click="cancel()">
</div>
</div>
The main page initially gets data from a service in the angular controller:
var app = angular.module('FFPA', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.router']);
app.controller('myCtrl', function ($scope, dataService) {
$scope.test = 'test';
dataService.getData().then(function (data) {
//The reduce() method reduces the array to a single value.
$scope.dataset = data.reduce(function (obj, item) {
obj[item.seatId.trim()] = item;
item.fullName = item.fName + ' ' + item.lName;
item.deptId = item.deptId;
item.jobDesc = item.jobDesc;
item.phone = item.phone;
return obj;
}, {});
});
});
Get Data Service:
angular.module('FFPA').service('dataService', function ($http) {
this.getData = function () {
//web api call
return $http.get("api/Controller/GetData).then(
function (response) {
return response.data;
}, function () {
return { err: "could not get data" };
}
);
}
});
The Update Service is called from the Popover Directive.
Update Service:
angular.module('FFPA').service('updateService', function ($http) {
this.putData = function (oc) {
//web api call
return $http.put("api/Controller/PutUpdateData", oc).then(
function (response) {
return response.data;
}, function () {
return { err: "could not update data" };
}
);
}
});
Here is a snippet from our Popover directive where the update occurs and where we thought we could refresh the scope, and the data for the page:
updateService.putData(data).then(function (response) {
if (response == false)
alert("Move Failed!");
else {
alert("Move Succeeded.");
//$window.location.reload() causes a page reload..not desirable
//$window.location.reload();
$state.reload();
}
});
We tried a $state.reload(); in the popover directive just after updateService.putData(data), however this caused -> Error: Cannot transition to abstract state '[object Object]' error.
Here is the full Popover Directive:
angular.module('FFPA').directive('svgFloorplanPopover', ['$compile', 'updateService', 'vacancyService', 'addService', 'terminateService', '$window', '$state', function ($compile, updateService, vacancyService, addService, terminateService, $window, $state) {
return {
restrict: 'A',
scope: {
'person': '=svgFloorplanPopover',
//UPDATE 8-MAY-2017
onDataUpdate: '&'
},
link: function (scope, element, attrs) {
scope.moveToggle = true; //hide move toggle
scope.addToggle = true; //hide add toggle
scope.submitToggle = true; //hide submit toggle
scope.$watch("person", function () {
if (scope.person) {
if (scope.person.vacant == true) {
scope.addToggle = false; //show add button
scope.empInfoToggle = true; //hide emp info
}
else
scope.moveToggle = false; //show move
}
});
//add employee---------------------------------------------------------
scope.addEmp = function () {
scope.addToggle = scope.addToggle === false ? true : false;
scope.buttonToggle = true;
scope.submitToggle = false;
var data = {
deptId: scope.person.deptId,
divisionId: scope.person.divisionId,
empId: scope.person.empId,
floor: scope.person.floor,
fName: scope.person.fName,
lName: scope.person.lName,
jobDesc: scope.person.jobDesc,
officeCode: scope.person.officeCode,
phone: scope.person.phone,
section: scope.person.section,
seat: scope.person.seat,
seatId: scope.person.seatId,
seatTypeId: scope.person.seatTypeId,
vacant: scope.person.vacant
};
//call to update/move the employee
//updateService.putData(scope.person).then(function () {
addService.putData(data).then(function (response) {
if (response == false)
alert("Create Failed!");
else {
alert("Create Succeeded.");
//UPDATE 8-MAY-2017
$scope.onDataUpdate({ person: $scope.person, moreData: $scope.moreData });
//$window.location.reload();
//$route.reload();
//scope.toggle = false;
}
});
}
//cancel function---------------------------------------------------------
scope.cancel = function () {}
//Term emp---------------------------------------------------------
scope.termEmp = function () {
var data = {
seatId: scope.person.seatId,
floor: scope.person.floor
};
terminateService.putData(data).then(function (response) {
if (response == false)
alert("Term Failed!");
else {
alert("Term Succeeded.");
$window.location.reload();
//$route.reload();
//scope.toggle = false;
}
});
}
//move employee---------------------------------------------------------
scope.moveEmp = function () {
scope.toggle = scope.toggle === false ? true : false;
scope.buttonToggle = true;
scope.submitToggle = false;
if (scope.person && scope.person.fullName.indexOf('changed') === -1) {
//scope.person.fullName += ' move?';
}
//Json object to send to controller to check for vacancy
var data = {
floor: scope.person.floor,
section: scope.person.section,
seat: scope.person.offCubeNum
};
//can't send object via $http.get (?) stringigy json and cast to Office object in controller.
var json = JSON.stringify(data);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//CHECK VACANCY service call
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vacancyService.getData(json)
.then(function (response) {
if (response == false)
alert("cube/office occupied");
else{
//+++++++++++++++++++++++++++++++++++++++++++
//UPDATE service call
//+++++++++++++++++++++++++++++++++++++++++++
//CONSTS
var CONSTFLOORPREFIX = "f";
var CONSTSEAT = "s";
var CONSTC = "c"
var floor = scope.person.floor;
var section = scope.person.section;
var offCube = scope.person.offCubeNum;
scope.person.oldSeatId = scope.person.seatId;
var newOfficeId = CONSTFLOORPREFIX + floor + CONSTSEAT; //f3s
//IF CUBE
if (scope.person.cbCube) {
var trimSection = section.trim();
newOfficeId += trimSection + CONSTC; //f3s313c
var trimOffCube = offCube.trim();
newOfficeId += trimOffCube;
}
else {
newOfficeId += 0 + CONSTC + section; //f3s0c
}
scope.person.seatId = newOfficeId;
//Json object to send to controller to check for vacancy
var data = {
Id: scope.person.Id,
seatId: scope.person.seatId,
oldSeatId: scope.person.oldSeatId,
empId: scope.person.empId,
lName: scope.person.lName,
fName: scope.person.fName,
refacName: scope.person.refacName,
deptId: scope.person.deptId,
divisionId: scope.person.divisionId,
jobDesc: scope.person.jobDesc,
seatTypeId: scope.person.seatTypeId,
officeCode: scope.person.officeCode,
phone: scope.person.phone,
floor: scope.person.floor,
section: scope.person.section,
seat: scope.person.seat,
vacant: scope.person.vacant
};
//call to update/move the employee
//updateService.putData(scope.person).then(function () {
updateService.putData(data).then(function (response) {
if (response == false)
alert("Move Failed!");
else {
alert("Move Succeeded.");
//$window.location.reload();
$state.reload();
//$route.reload();
//scope.toggle = false;
}
});
}//end else
});
}
if (element[0].querySelector('text') != null){
scope.htmlPopover = './HTML/popoverTemplate.html';
element[0].setAttribute('uib-popover-template', "htmlPopover");
element[0].setAttribute('popover-append-to-body', 'true');
element[0].setAttribute('popover-trigger', "'click'");
//element[0].setAttribute('popover-trigger', "'mouseenter'");
element[0].setAttribute('popover-placement', 'right');
element[0].removeAttribute('svg-floorplan-popover');
$compile(element)(scope);
}
}
}
}]);
UPDATED: 8-MAY-2017
Originally there is one additional data service and a directive that we left out of this post since it may be considered not essential information, however recently added since it may be needed.
SVG Load Directive:
angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
return {
restrict: 'A', //restrict attributes
templateUrl: './SVG/HQ3RD-FLOOR3v10.svg',
scope: {
'dataset': '=svgFloorplan'
},
link: {
pre: function (scope, element, attrs) {
//filter groups based on a cube/office id
var groups = element[0].querySelectorAll("g[id^='f3']");
//groups.style("pointer-events", "all");
scope.changeName = function (groupId) {
if (scope.dataset[groupId] && scope.dataset[groupId].lastName.indexOf('changed') === -1) {
scope.dataset[groupId].lastName += ' changed';
}
}
groups.forEach(function (group) {
var groupId = group.getAttribute('id');
if (groupId) {
//set vacancy colors on vacant cubes
scope.$watch("dataset", function () {
if (scope.dataset) {
if (typeof scope.dataset[groupId] !== "undefined") {
//vacant cubes and offices hover
if (scope.dataset[groupId].vacant == true) {
//seat type id 1 = cube
if (scope.dataset[groupId].seatTypeId == 1){
d3.select(group).select("rect").style("fill", "#99ff33").style("opacity", 0.4)
.style("pointer-events", "all")
.on('mouseover', function () {
d3.select(this).style('opacity', 0.9);
})
.on('mouseout', function () {
d3.select(this).style('opacity', 0.4);
})
}
//vacant office
else {
d3.select(group).select("path").style("stroke", "#ffffff").style("opacity", 1.0);
d3.select(group).select("path").style("fill", "#99ff33").style("opacity", 0.4)
.style("pointer-events", "all")
.on('mouseover', function () {
d3.select(this).style('opacity', 0.9);
})
.on('mouseout', function () {
d3.select(this).style('opacity', 0.4);
})
}
}
else { //Occupied
//seat type id 1 = cube
if (scope.dataset[groupId].seatTypeId == 1) {
d3.select(group).select("rect").style("fill", "#30445d").style("opacity", 0.0)
.style("pointer-events", "all")
.on('mouseover', function () {
d3.select(this).style('opacity', 1.0);
d3.select(group).select('text').style("fill", "#FFFFFF");
})
.on('mouseout', function () {
d3.select(this).style('opacity', 0.0);
d3.select(group).select('text').style("fill", "#000000");
})
//TODO: cubes have rects and on the north side of the building wall, paths.
d3.select(group).select("path").style("fill", "#30445d").style("opacity", 0.0)
.style("pointer-events", "all")
.on('mouseover', function () {
d3.select(this).style('opacity', 1.0);
d3.select(group).select('text').style("fill", "#FFFFFF");
})
.on('mouseout', function () {
d3.select(this).style('opacity', 0.0);
d3.select(group).select('text').style("fill", "#000000");
})
}
//occupied office
else {
//d3.select(group).select("path").style("stroke", "#ffffff").style("opacity", 0.8);
d3.select(group).select("path").style("fill", "#5A8CC9").style("opacity", 1.0)
.style("pointer-events", "all")
.on('mouseover', function () {
//alert("office");
d3.select(this).style("fill", "#2d4768").style('opacity', 1.0);
d3.select(group).selectAll('text').style("fill", "#FFFFFF");
})
.on('mouseout', function () {
d3.select(this).style("fill", "#5A8CC9").style('opacity', 1.0);
d3.select(group).selectAll('text').style("fill", "#000000");
})
}
}//end occupied else
}
}
});
//UPDATE 8-MAY-2017->Implementation Question
scope.onDataUpdateInController = function (person, moreData) { };
var datasetBinding = "dataset['" + groupId + "']";
group.setAttribute('svg-floorplan-popover', datasetBinding);
//UPDATE 8-MAY-2017
//on-data-update corresponds to onDataUpdate item on svgFloorplanPopover's scope.
group.setAttribute('on-data-update', onDataUpdateInController);
$compile(group)(scope);
}
});
}
}
}
}]);
Vacancy Service (check before update):
angular.module('FFPA').service('vacancyService', function ($http) {
...
}
The main question is:
How can we have our application refresh our page with the updated data without reloading the page?
We used to be able to do this in UpdatePanels in ASP.Net webforms back in the day. I think they were partial postbacks/AJAX calls..
EDITED 2-AUG-2017
+++++++++++++++++++++++++++++++++++
Even though the bounty was automatically awarded, we still don't have an answer to this question. Without any implementation context the answers given are not useful.
Can anyone expand on the answers given to give us an idea on how this problem can be solved?
Thanks
Just add your data on $scope object and use it in your view, whenever you update or modify the data just
eg: consider you have a function to get the data where you are making rest call to your db
$scope.getdata=function(){
$http.get(url).success(function(data)
{ $scope.data=data;
});
Whenever you modify your data just call this function in your case on click of close of directive/popup
To refresh your view (not bind the received data) use the answers for the following questions:
Using ngRoute Module
How to reload or re-render the entire page using AngularJS
Using ui-router Module
Reloading current state - refresh data
With that I would recommend you to assign the received data to your bounded $scope property.
I'll add a full example after you'll provide an updated plnkr :)
Please try the following steps:
1. Create a method in svgFloorplanPopover directive and call it by passing in the data
In your svgFloorplanPopover directive, add onDataUpdate item in the scope
declaration:
...
scope: {
'person': '=svgFloorplanPopover',
onDataUpdate: '&'
}
...
and where you are trying to reload state, instead of reloading the state or page, call the below code. This is to create an event system which is fired from within the directive to let the controller or parent directive know that data has changed and view can now be updated.
$scope.onDataUpdate({person: $scope.person, moreData: $scope.moreData});
2. Create a method in svgFloorplan to accept the passed data
Since you are using nested directive approach, you'll need to use the below code in svgFloorplan directive.
group.setAttribute('svg-floorplan-popover', datasetBinding);
group.setAttribute('on-data-update', onDataUpdateInController);
on-data-update corresponds to onDataUpdate item on svgFloorplanPopover's scope.
Declare onDataUpdateInController method on the scope of svgFloorplan directive.
scope.onDataUpdateInController = function(person, moreData) {};
The object properties that you pass from within the directive are laid out flat to the number of parameters.
If you need to pass this data further up to your controller where svgFloorplan is declared. Repeat the above two steps for svgFloorplan directive.
I hope this approach is clear. It is no different than what is explained in Angular Directives, section Creating a Directive that Wraps Other Elements and code where a close button is added. Here is the direct link to the code in plunkr.
Just a question: Are you going to use these directives separately from each other? If no, you may try to create one directive instead of making them two. This will reduce complexity.
I'm able to send the same json data from home.js to content.js.But I'm unable to populate the content.js scope data into the html page
Can anyone please help me out regarding this issue ...
My home.js:
angular.module('myApp')
.controller('firstCtrl', ['$scope', 'myService',
function ($scope,myService) {
$scope.list1= [];
$scope.list2= [];
var sampleItem = this.item;
myService.setJson(sampleItem);
$.each($scope.samples, function (i, x) {
if (x.name === sampleItem .secName && x.id === sampleItem .id) {
if (sampleItem .secName === $scope.secsList[0]) {
$scope.list1.push(x);
}
else {
$scope.list2.push(x);
}
$scope.myData = x.dataList;
}
});
});
My content.js :
angular.module('myApp')
.controller('secondCtrl', function ($scope,myService) {
$scope.myreturnedData = myService.getJson();
console.log($scope.myreturnedData);
})
.factory('myService', function(){
var sampleItem = null;
return {
getJson:function(){
return sampleItem;
},
setJson:function(value){
sampleItem = value;
}
}
});
My content.html :
<div ng-controller="secondCtrl" > {{myreturnedData.sampleName}}</div>
My home.html:
<div ng-controller="firstCtrl" ng-repeat="item in list1" >
<div > {{item.sampleName}} </div>
</div>
This is work solution jsfiddle
angular.module('ExampleApp',[])
.controller('firstCtrl', function($scope, myService) {
$scope.sampleItem = {
sampleName: "sampleName"
};
myService.setJson($scope.sampleItem);
}
)
.controller('secondCtrl', function($scope, myService) {
$scope.myreturnedData = myService.getJson();
console.log($scope.myreturnedData);
})
.factory('myService', function() {
var sampleItem = null;
return {
getJson: function() {
return sampleItem;
},
setJson: function(value) {
sampleItem = value;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="firstCtrl">
<h2>
firstCtrl
</h2>
<input ng-model="sampleItem.sampleName">
</div>
<div ng-controller="secondCtrl"> <h2>
secondCtrl
</h2>{{myreturnedData.sampleName}}</div>
</div>
I am using this angular js service/directive github pageto display alerts. An issue has been opened relating to the question I am asking but it has not been addressed by the developer.
i want first alert shown when user logs in to disappear when the user clicks logout but the alerts are stacking up on top of each other.
Here is a fiddle although I could not replicate the issue but it shows the structure of my code. fiddle
html:
<body ng-app="app">
<mc-messages></mc-messages>
<button ng-click="login()">Login</button>
<button ng-click="logout()">Logout</button>
</body>
js:
/*jshint strict:false */
'use strict';
var app = angular.module('app', ['MessageCenterModule']);
app.controller(function ($scope, messageCenterService, $location) {
$scope.login = function () {
$location.path('/');
messageCenterService.add('success',
'You are now loggedin!', {
status: messageCenterService.status.next
});
};
$scope.logout = function () {
$location.path('login');
messageCenterService.add('success',
'You are now loggedout!', {
status: messageCenterService.status.next
}
};
});
// Create a new angular module.
var MessageCenterModule = angular.module('MessageCenterModule', []);
// Define a service to inject.
MessageCenterModule.
service('messageCenterService', ['$rootScope', '$sce', '$timeout',
function ($rootScope, $sce, $timeout) {
return {
mcMessages: this.mcMessages || [],
status: {
unseen: 'unseen',
shown: 'shown',
/** #var Odds are that you will show a message and right after that
* change your route/state. If that happens your message will only be
* seen for a fraction of a second. To avoid that use the "next"
* status, that will make the message available to the next page */
next: 'next',
/** #var Do not delete this message automatically. */
permanent: 'permanent'
},
add: function (type, message, options) {
var availableTypes = ['info', 'warning', 'danger', 'success'],
service = this;
options = options || {};
if (availableTypes.indexOf(type) === -1) {
throw "Invalid message type";
}
var messageObject = {
type: type,
status: options.status || this.status.unseen,
processed: false,
close: function () {
return service.remove(this);
}
};
messageObject.message = options.html ? $sce.trustAsHtml(message) : message;
messageObject.html = !! options.html;
if (angular.isDefined(options.timeout)) {
messageObject.timer = $timeout(function () {
messageObject.close();
}, options.timeout);
}
this.mcMessages.push(messageObject);
return messageObject;
},
remove: function (message) {
var index = this.mcMessages.indexOf(message);
this.mcMessages.splice(index, 1);
},
reset: function () {
this.mcMessages = [];
},
removeShown: function () {
for (var index = this.mcMessages.length - 1; index >= 0; index--) {
if (this.mcMessages[index].status == this.status.shown) {
this.remove(this.mcMessages[index]);
}
}
},
markShown: function () {
for (var index = this.mcMessages.length - 1; index >= 0; index--) {
if (!this.mcMessages[index].processed) {
if (this.mcMessages[index].status == this.status.unseen) {
this.mcMessages[index].status = this.status.shown;
} else if (this.mcMessages[index].status == this.status.next) {
this.mcMessages[index].status = this.status.unseen;
}
this.mcMessages[index].processed = true;
}
}
},
flush: function () {
$rootScope.mcMessages = this.mcMessages;
}
};
}]);
MessageCenterModule.
directive('mcMessages', ['$rootScope', 'messageCenterService', function ($rootScope, messageCenterService) {
/*jshint multistr: true */
var templateString = '\
<div id="mc-messages-wrapper">\
<div class="alert alert-{{ message.type }} {{ animation }}" ng-repeat="message in mcMessages">\
<a class="close" ng-click="message.close();" data-dismiss="alert" aria-hidden="true">×</a>\
<span ng-switch on="message.html">\
<span ng-switch-when="true">\
<span ng-bind-html="message.message"></span>\
</span>\
<span ng-switch-default>\
{{ message.message }}\
</span>\
</div>\
</div>\
';
return {
restrict: 'EA',
template: templateString,
link: function (scope, element, attrs) {
// Bind the messages from the service to the root scope.
messageCenterService.flush();
var changeReaction = function (event, to, from) {
// Update 'unseen' messages to be marked as 'shown'.
messageCenterService.markShown();
// Remove the messages that have been shown.
messageCenterService.removeShown();
$rootScope.mcMessages = messageCenterService.mcMessages;
messageCenterService.flush();
};
$rootScope.$on('$locationChangeStart', changeReaction);
scope.animation = attrs.animation || 'fade in';
}
};
}]);
Hope this is clear enough for someone to help me. If not let me know and I can try to clarify.
Let me start by saying that I am very new to angular.
I have tried to setup a modal ui which works fine. But when I click the "cancel" button on the interface nothing happens.
This is my code:
(function () {
var domainName = 'TournamentCategory';
angular.module('tournamentCategories').controller("CategoriesController",
['$scope', '$modal', 'guidGenerator', 'eventBus', domainName + "Service",
'TournamentCategoryModelFactory',
function ($scope, $modal, guidGenerator, eventBus, domainService, modelFactory)
{$scope.openTournamentTree = function () {
var modalInstance = $modal.open({
templateUrl: 'TournamentTreeModal.html',
controller: 'TreeController',
size: 'wide-90',
resolve: {
}
});
modalInstance.result.then(function (selectedItem) {
//$scope.selected = selectedItem;
}, function () {
//$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.ok = function () {
modalInstance.close();
}
$scope.cancel = function () {
modalInstance.dismiss('cancel');
};
}]);
})();
The HTML:
<script type="text/ng-template" id="TournamentTreeModal.html">
<div class="modal-header">
</div>
<div class="modal-body">
<div class="include-tree" ng-include="'tournament-tree.html'"></div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">GEM</button>
<button ng-click="cancel()" class="btn btn-warning" type="button" data-dismiss="modal">LUK, uden at gemme</button>
</div>
This is my "treeController"
angular.module('tournamentTree').controller("TreeController", ['$scope', 'guidGenerator', function ($scope, guidGenerator) {
$scope.allMatches = [];
$scope.finalMatch = [createNewMatch()];
$scope.tierCount = 1;
$scope.previousMatchTier = 0;
$scope.deletePreviousMatches = function (parentMatch) {
for (var i in parentMatch.previousMatches) {
var previousMatch = parentMatch.previousMatches[i];
_.remove($scope.allMatches, function (match) { return match.id == previousMatch.id });
}
parentMatch.previousMatches = [];
}
$scope.addMatches = function (thisMatch) {
if (thisMatch && !thisMatch.previousMatches)
thisMatch.previousMatches = [];
if (thisMatch && thisMatch.previousMatches.length == 0) {
thisMatch.previousMatches.push(createNewMatch(thisMatch));
thisMatch.previousMatches.push(createNewMatch(thisMatch));
}
}
$scope.addMatchTier = function () {
for (var i in $scope.allMatches) {
var match = $scope.allMatches[i];
if (match.previousMatches.length == 0) {
$scope.addMatches(match);
}
}
$scope.tierCount++;
}
$scope.previousMatchTier = function () {
for (var i in $scope.allMatches) {
var previous;
if (previous.allMatches.length == i) {
previous = $scope.allMatches[i] - $scope.allMatches[i - 1];
}
}
}
$scope.removeMatchTier = function () {
//if (confirm('Er du sikker på, at du vil slette det yderste niveau af turneringstræet?')) {
var matchesToDelete = [];
for (var i in $scope.allMatches) {
var match = $scope.allMatches[i];
if (match.previousMatches.length == 0) {
matchesToDelete.push(match.nextMatch);
//$scope.deletePreviousMatches(match.nextMatch);
}
}
for (var i in matchesToDelete) {
$scope.deletePreviousMatches(matchesToDelete[i]);
}
$scope.tierCount--;
//}
}
$scope.hasPreviousMatches = function (match) {
return match && match.previousMatches && match.previousMatches.length > 0;
}
$scope.moveWinnerToNextMatch = function (match, winnerName) {
match.nextMatch.setPlayerName(match.id, winnerName);
}
function createNewMatch(nextMatch) {
var newMatch = {};
newMatch.tier = nextMatch && nextMatch.tier ? nextMatch.tier + 1 : 1;
newMatch.id = guidGenerator.newGuid();
newMatch.player1 = "";
newMatch.player2 = "";
newMatch.nextMatch = nextMatch;
newMatch.previousMatches = [];
newMatch.setPlayerName = function(matchId, playerName) {
for (var i in newMatch.previousMatches)
{
if (newMatch.previousMatches[i].id == matchId)
{
if (i == 0)
newMatch.player1 = playerName;
else
newMatch.player2 = playerName;
}
}
}
$scope.allMatches.push(newMatch);
return newMatch;
}
}]);
$dismiss is already available on modal scope so in template raplace cancel() with $dimiss() http://angular-ui.github.io/bootstrap/#/modal
That needs to go in your modals controller 'TreeController'
angular.module('tournamentCategories').controller('TreeController', function($scope, $modalInstance) {
$scope.ok = function () {
modalInstance.close();
}
$scope.cancel = function () {
modalInstance.dismiss('cancel');
};
});
I have an example in this plunker
I am having one chart directive created, and I am bootstrpping the app after loading google api. In following code, a simple data table is working fine. But when I load data from server in async manner, chart is not being displayed.
Controller
'use strict';
myNetaInfoApp.controller('allCandidatesController', [
'$scope','allCandidates2009Svc', '$timeout',
function ($scope, allCandidates2009Svc, $timeout) {
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string", "Party");
$scope.data1.dataTable.addColumn("number", "qty");
$scope.data1.dataTable.title = "ASDF";
$timeout( function (oldval, newval) {
allCandidates2009Svc.GetPartyCriminalCount().then(function(netasParty) {
var i = 0;
for (var key in netasParty) {
$scope.data1.dataTable.addRow([key.toString(), netasParty[key]]);
i++;
if (i > 20) break;
}
});
});
$scope.dataAll = $scope.data1;
//sample data
$scope.data2 = {};
$scope.data2.dataTable = new google.visualization.DataTable();
$scope.data2.dataTable.addColumn("string", "Name");
$scope.data2.dataTable.addColumn("number", "Qty");
$scope.data2.dataTable.addRow(["Test", 1]);
$scope.data2.dataTable.addRow(["Test2", 2]);
$scope.data2.dataTable.addRow(["Test3", 3]);
}
]);
Service
'use strict';
myNetaInfoApp.factory('allCandidates2009Svc', ['$http', '$q',
function ($http, $q) {
var netas;
return {
GetPartyCriminalCount: function () {
var deferred = $q.defer();
$http.get('../../data/AllCandidates2009.json')
.then(function (res) {
netas = res;
if (netas) {
var finalObj = {};
_.each(netas.data, function(neta) {
finalObj[neta.pty] = finalObj[neta.pty] ? finalObj[neta.pty] + 1 : 1;
});
deferred.resolve(finalObj);
}
});
return deferred.promise;
}
};
}]);
Directive
"use strict";
var googleChart = googleChart || angular.module("googleChart", []);
googleChart.directive("googleChart", function () {
return {
restrict: "A",
link: function ($scope, $elem, $attr) {
var dt = $scope[$attr.ngModel].dataTable;
var options = {};
if ($scope[$attr.ngModel].title)
options.title = $scope[$attr.ngModel].title;
var googleChart = new google.visualization[$attr.googleChart]($elem[0]);
$scope.$watch($attr.ngModel, function (oldval, newval) {
googleChart.draw(dt, options);
});
}
};
});
HTML
<div ng-controller="allCandidatesController">
<div class="col-lg-6">
<h2>Parties and Candidates with Criminal Charges</h2>
<div google-chart="PieChart" ng-model="dataAll" class="bigGraph"></div>
<!--<p><a class="btn btn-primary" href="#" role="button">View details »</a></p>-->
</div>
<div class="col-lg-6">
<h2>Heading</h2>
<div google-chart="BarChart" ng-model="data2" class="bigGraph"></div>
</div>
</div>
I think you need to wrap your function body in allCandidates2009Svc factory with scope.$apply(). But the return deferred.resolve() will be outside scope.$apply().
function asyncGreet(name) {
var deferred = $q.defer();
setTimeout(function() {
// since this fn executes async in a future turn of the event loop, we need to wrap
// our code into an $apply call so that the model changes are properly observed.
scope.$apply(function() {
deferred.notify('About to greet ' + name + '.');
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!');
} else {
deferred.reject('Greeting ' + name + ' is not allowed.');
}
});
}, 1000);
return deferred.promise;
}
Read the docs here
http://docs.angularjs.org/api/ng.$q