I have a SPA with two different views one for subjects and one for student,
in subject view I have a save button in app/views/subject/subject.html:
<button type="button" class="btn btn-warning" ng-click="saveInfo()">
Save
</button>
I want to add the same function in the student views , saveInfo() pass the data into a service in the app factory which save the data in DB through fill_table.php.
the app factory in app/javascript/services.js:
var app = angular.module('myApp');
app.factory("services", ['$http', function($http) {
var serviceBase = 'http://localhost/php/';
var obj = {};
document.title = "myApp on " + serviceBase;
obj.postData = function (user, data) {
return $http.post(serviceBase + 'fill_table.php', { "data": data, "user": {'username': user.name, 'password': user.password }).then(function (results) {
return results.data;
});
};
saveInfo() is in app/views/subject/subject.js:
$scope.saveInfo = function() {
console.log("saveInfo");
$scope.loadingInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'modalLoading.html',
size: "l",
});
return getChanges( $indexedDB, $q).then( function(responsesArray) {
var jsonData = {};
$scope.errorInstance = undefined;
for (var i=0; i < DB_TABLES.length; i++) {
var table = DB_TABLES[i];
var items = responsesArray[i]
if (items.length > 0){
jsonData[table] = items;
}
}
console.log(JSON.stringify(jsonData));
return services.postData($scope.selectedUser, jsonData);
})
}
I want to add the mentioned button into app/views/student/student.html. i tried and copied the code from the subject.js into Student but for some reason it does not work eventhough i checked everything was correct so is there a way to only that function from subject.js into Student.html
note 1 getChanges() is another function get the inserted info and pass it into saveinfo().
note 2 right now I can save the info inserted student view by pressing save button in subject view
If I understand you correctly, you have two html files and two controller (student and subject). To share data/functions between these, you could use a service or factory to handle all your http request. This is reusable and accessible from all your controllers.
app.factory("services", ['$http', function($http) {
var postStudent = function (student) {
return $http.post("api/Student/Post", student);
};
var getChanges = function (){
return $http.get("api/Student/Changes", student);
};
return {
postStudent : postStudent,
getChanges : getChanges
};
}])
Now you can use can call the services from your controller as you see fit.
app.controller('StudentController', ['service', function(service){
service.postStudent(student).then(function successCallback(response) {
console.log('success');
}, function errorCallback(response) {
console.log('error ' + response);
});
service.getChanges().then(function successCallback(response) {
console.log('success');
}, function errorCallback(response) {
console.log('error ' + response);
});
}]);
app.controller('SubjectController', ['service', function(service){
service.postStudent(student).then(functionsuccessCallback(response){
},
function errorCallback(response) {
});
service.getChanges().then(function successCallback(response) {
},
function errorCallback(response) {
});
}]);
Note that the above has not been implemented, but should provide you with an outline.
Related
I would like to use Angular 1.6.5 for a project rebuild, but I'm not sure how to use the $http.get request in a factory when the source returns only a limited number of records at a time (1000 returned per request) and there are over 2000 records that I need to get.
In my current code I use jquery ajax and in the .done method I check for the presence of the value "__next", and if it exists, I recall the function passing the value "__next". When the "__next" value isn't returned, I do something with the data.
function getSpecifiedList(url){
var specUrl = url;
$.ajax({
url: specUrl,
type: "GET",
headers:{"accept":"application/json;odata=verbose",
error: function(xhr){
console.log(xhr.status + " " + xhr.statusText);
}
}
}).done(function (results){
$("#wc_report_holder").text(results.length);
//buildObjects processes the results and adds to an array
buildObject(results);
if(results.d.__next){
getSpecifiedList(results.d.__next);
}else{
buildGridView();
}
}).fail(function(error){
$("#wc_report_holder").text("There was an error: " + error);
});
}
I would like to figure out how to implement that same value check and recursive call in angular 1.6.5 using best practice and most efficient but I haven't had luck figuring it out based on the angular docs and Googling.
Here is a short version of what I currently have using Angular 1.6.5.
<script>
var sitesApp = angular.module("sitesApp", ['ngRoute']);
sitesApp.controller('SitesListCtrl', ['$scope', 'sites',
function ($scope, sites) {
sites.list().then(function (response) {
$scope.sites = response.data.value;
});
}
]);
sitesApp.controller("SiteDetailsCtrl", ['$scope', '$routeParams', 'sites',
function ($scope, $routeParams, sites) {
sites.find($routeParams.SiteCodePc, function (site) {
$scope.site = site;
});
}
]);
sitesApp.config(function ($routeProvider, $locationProvider) {
$locationProvider.hashPrefix('!');
$routeProvider.
when('/', {
templateUrl: 'https://machine/sites/site-list.html',
controller: 'SitesListCtrl'
}).
when('/:SiteCodePc', {
templateUrl: 'https://machine/sites/site-details.html',
controller: 'SiteDetailsCtrl'
}).
otherwise({
redirectTo: '/'
});
});
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
var cachedData;
function getData(callback) {
if (cachedData) {
callback(cachedData);
} else {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
//HERE IS WHERE I THINK THE SOLUTION NEEDS TO BE IMPLEMENTED
cachedData = response;
return cachedData;
});
}
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
</script>
<div ng-app="sitesApp">
<div ng-view></div>
</div>
Thanks in advance
It looks like you can do a simple recursion where you accept a second (optional) parameter. If you are calling getData() for the first time then you can get your first 1000 results. However if you find __next then you will call it again sending the current 1000 results you have and concat the next 1000 results with the previous 1000.
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
function getData(callback, results) {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
// If you have found a previous batch of results then concat the two arrays
if(results) {
response = response.concat(results);
}
// If there are more results to be found then recursively call the same function passing the batched results
if(response.__next) {
return getData(callback, response);
}
// If there are no more results to be found then trigger your callback function
else {
callback(response);
}
});
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
I have implemented same kind of scenario with pagination logic and $q. In this sample code I am pulling the records recursively as lazy based on the LazyloadingLimit. You can specify the limit based on your requirement.So it only pulls the records based on the count from the total collection. In this below sample I am not using $http. On your real sample you can use the $http to pull the records from the server. Here I just hard coded the collection initially.
In your case you have to fetch total records count initially and apply some pagination logic or some other parameter to pull the next records.
angular.module('app', []);
angular.module('app').controller('SampleController', function ($scope,$http, $timeout, $q) {
// $scope.initialize();
$scope.mainCount = 0;
$scope.lazyloadingLimit = 2;
$scope.tileDefinitions = null;
$scope.tempList = null;
$scope.totalRecordCollection = [
{ "Name": "Record1" },
{ "Name": "Record2" },
{ "Name": "Record3" },
{ "Name": "Record4" },
{ "Name": "Record5" },
{ "Name": "Record6" },
{ "Name": "Record7" },
];
function getTotalRecordCollection() {
var deferred = $q.defer();
deferred.resolve($scope.totalRecordCollection);
return deferred.promise;
}
$scope.initialize = function () {
debugger;
var currentCount=0;
var pageList = new Array();
var currentPage = 1;
var numberPerPage = 2;
var numberOfPages = 0;
function makeList() {
numberOfPages = getNumberOfPages();
}
function getNumberOfPages() {
return Math.ceil($scope.tempList.length / numberPerPage);
}
function nextPage() {
currentPage += 1;
}
function loadList() {
var deferred = $q.defer();
if (currentCount !== $scope.tempList.length) {
var begin = ((currentPage - 1) * numberPerPage);
var end = begin + numberPerPage;
pageList = $scope.tempList.slice(begin, end);
currentCount = currentCount + pageList.length;
$scope.mainCount = currentCount;
deferred.resolve(true);
} else {
debugger;
return $q.reject();
}
return deferred.promise;
}
function loadNextRecords() {
loadList().then(function (res) {
nextPage();
loadNextRecords();
});
}
getTotalRecordCollection().then(function (response) {
debugger;
$scope.tempList = response;
makeList();
loadNextRecords();
});
}
});
<body ng-controller="SampleController">
<input type="button" value="Click Here" ng-click="initialize()"/>
{{mainCount}}
</body>
Once all the records are loaded , you should reject the promise else the recursive loops never end.
Hope this helps
I'm pretty new to angular js, currently, it's going well but now I have a question.
I have a template with a topnav and a contentpart. All with its own controller.
With a button I can open a "submenu" where I can choose data from the database, within the "Liquid"-section. Thats working pretty well.
Since the Topnav is rendered at the login of the page, the topnav wont be rendered again.
If I add a Liquid in the content section, I have to reload the data behind the "Liquid"-Dropdown.
That dropdown is encapsulated in a directive:
function liquidselect(Data){
return {
restrict: 'AE',
templateUrl: 'views/controls/liquid_select.html',
scope: {
selectedValues : '='
},
link: function(scope) {
},
controller: function ($scope) {
//
Data.post('ownrecipes').then(function (results) {
$scope.searchRes = results;
});
// debugger;
//$scope.searchRes = RecipeDataService.data;
$scope.disabled = undefined;
$scope.searchEnabled = false;
$scope.searchRes = [];
$scope.flavoring = {amount: {}};
$scope.updateModelValue = function (selected) {
// console.log(selected);
//
$scope.selectedValues = selected;
};
}
}
}
The communication to the server is done by a factory:
app.factory("Data", ['$http', 'toaster',
function ($http, toaster ) { // This service connects to our REST API
// var deffered = $q.defer();
var serviceBase = 'api/v1/';
var obj = {};
obj.toast = function (data) {
toaster.pop(data.status, "", data.message, 3000, 'trustedHtml');
}
obj.get = function (q) {
return $http.get(serviceBase + q).then(function (results) {
return results.data;
});
};
obj.post = function (q, object) {
return $http.post(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.put = function (q, object) {
return $http.put(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.delete = function (q) {
return $http.delete(serviceBase + q).then(function (results) {
return results.data;
});
};
return obj;
}]);
How can I update/reload the data of the directive from a child scope? Is there any chance?
Here is a plunker which showcases my problem and want I want to do:
http://plnkr.co/edit/bNANkQYZfBaS4CHH3dwX
I hope it's helpful for you :-)
Layout:
Basic Layout
I'm having a little problem trying to pass a service within controllers.
What I'm trying to do is a shopping cart, I have a list of items and when I hit a button, those items get added to the cart, then I want to list those items in the cart in a separate page using a separate controller, so I'm trying to use a factory for the cart, but I don't know if you can set a factory object within a controller.
Here's my code, hope you can point me in the right direction.
var app = angular.module("Shop", []);
app.factory('DataService', function () {
var cart = [];
var set = function (data) {
cart = data;
}
var get = function () {
return cart;
}
});
app.controller("catalogController", function ($scope, $http) {
$scope.bookStore = {
selected: {},
books: null
};
$scope.cart = [];
$http.get("json/books.json")
.success(function (data) {
console.log(data);
$scope.bookStore.books = data;
})
.error(function (err) {
});
$scope.addToCart = function (book) {
var found = false;
$scope.cart.forEach(function (item) {
if (item.id === book.id) {
item.quantity++;
found = true;
}
});
if (!found) {
$scope.cart.push(angular.extend({
quantity: 1
}, book));
}
};
$scope.removeFromCart = function (item) {
var index = $scope.cart.indexOf(item);
$scope.cart.splice(index, 1);
};
$scope.getCartPrice = function () {
var total = 0;
$scope.cart.forEach(function (product) {
total += product.price * product.quantity;
});
return total;
};
});
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService;
});
Change things a bit to something like:
app.factory('DataService', function () {
var cart = [];
return {
set: function (data) {
cart = data;
},
get: function () {
return cart;
},
add: function (item) {
cart.push(item);
}
}
});
...
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService.get();
});
And then move the $http.get method and all the operations on the card in the other controller to functions in the factory and declare them on the same way as the above Dataservice.get()
You should do something like this:
A service is a singleton in angular js, that's mean you only have one instance of this class in your app.
var app = angular.module("Shop", []);
app.factory('DataService', function ($http) { // usualy your service is the one which call your API (not your controller)
var cart = null; // the cart array is set in the instance of the class as private
return{ // here you declare all the functions you want to call from outside (your controllers)
set : function (data) {
cart = data;
},
get: function(){
return cart;
},
getFromAPI = function () { // the code you have in your controller should goes here
return $http.get("json/books.json")
.success(function (data) {
console.log(data);
cart = data; //now you set you cart variable
})
.error(function (err) {
});
},
});
Then in your controllers:
app.controller("catalogController", function ($scope, DataService) { // include your service as a dependency
$scope.bookStore = {
selected: {},
books: null
};
$scope.cartInCatalogController = DataService.get(); // it will set the value of cart that's in your service to your controller's scope
if(!$scope.cartInCatalogController) {// if it's null so call the API
DataService.getFromAPI()// this function should return a promise
.success(function(data){// so call the success function
$scope.cartInCatalogController = data;
})
.error(function(error){
// do something here if you want
});
});
You can do the same in your other controller.
About the addToCard function and other stuff I let you find it by yourself.
You can start from here :)
I am trying to use an ng-repeat inside of an ng-view, but it is not pulling in the data. I was reading on the forums that I could use a factory, but I don't think using a service would be acceptable since the data for my $scope uses $routeParams to query its data.
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:name', {
templateUrl: 'welcome.html',
controller: 'myController'
}).
otherwise ({
redirectTo: '/'
});
}]);
myApp.controller('myController', ['$scope', '$routeParams', '$q', function($scope, $routeParams, $q) {
var pls = users($q, $routeParams.name);
$scope.sBP = pls;
}]);
function users($q, name) {
var playersDFD = $q.defer();
var players = new Array();
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", name);
query.find({
success: function (results) {
for (var i in results) {
sPlayer = new player(results[i].get("playerName"), results[i].get("score"), results[i].get("cheatMode"));
players.push(sPlayer);
}
playersDFD.resolve(players);
},
error: function (error) {
alert('error');
playersDFD.reject(data);
}
});
return playersDFD.promise
.then(function (results) {
return results;
})
.catch(function (error) {
alert(error.message);
});
};
function player(name, score, cheatm){
this.name = name;
this.score = score;
this.cheatm = cheatm;
};
And the view:
<p ng-repeat="s in sBP">
{{ s.name }}
</p>
Let your users function return the promise rather than trying to resolve it, this ends up with code that is a lot easier to follow and will give the consumers of the user function control of what to do once you receive a response. For example.
function users($q, name) {
var playersDFD = $q.defer();
var players = new Array();
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", name);
query.find({
success: function (results) {
for (var i in results) {
sPlayer = new player(results[i].get("playerName"), results[i].get("score"), results[i].get("cheatMode"));
players.push(sPlayer);
}
playersDFD.resolve(players);
},
error: function (error) {
alert('error');
playersDFD.reject(data);
}
});
return playersDFD.promise;
}
And then use the users function and handle then itself.
users($q, $routeParams.name).then(function (response) {
$scope.sBP = response;
}, function (error) {
// handle error.
});
Also I would recommend breaking out users into it's own service to inject $q rather than pass it in.
Hope that helps.
I have a service which will make a call to the server and returns the data. I am binding service to a variable on scope.
Example:
Let the service be DataModelService
in the controller : $scope.data = DataModelService
in the view <div ng-repeat="value in data.persons">{{value.name}}</div>
My Code :
This is how my code looks like:
/**DataModelService**/
factory('DataModelService', [
'DataService',
function (DataService) {
var service;
service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
service.loadData(response.data);
});
},
loadData: function (responseData) {
service.persons = responseData.persons;
}
}
return service;
}
]);
/**DataService**/
factory('DataService', ['$http',
function ($http) {
return {
changePersonState: function (params) {
return $http.post("url", params);
}
}
}
]);
/**DataController**/
.controller('DataController', ['DataModelService',
function (DataModelService) {
$scope.data = DataModelService;
}
]);
/view/
<div ng-repeat = "person in data.persons" >{{person.name}} </div>
On the view I am doing a ng-repeat on a key in data i.e. ng-repeat="value in data.persons"
and also I have an option to change the state of person to active or inactive, so whenver i make a change to the state of the person, a call is sent to the server and data is set into the Service and as it is binded to the view, it should automatically update the data. But whats happening in my case, ng-repeat is not removing old data and instead it is appending new data to the old data.
For me its not good approach to write promise callback (then) into service. Because in your case, DataModelService returns data with some delay but not promise. And we don't know when.
So the way to make it work to add basic $timeout and fetch data from service by using other method.
So my suggestion is Demo
and your fixed example: Demo2
If we will take your example, it should be like:
JS
var fessmodule = angular.module('myModule', ['ngResource']);
fessmodule.controller('fessCntrl', function ($scope, DataModelService, $timeout) {
$scope.alertSwap = function () {
DataModelService.changeState('ff');
$timeout(function(){
$scope.data = DataModelService.getResponse();
}, 10);
}
});
fessmodule.$inject = ['$scope', 'Data', '$timeout'];
/**DataModelService**/
fessmodule.factory('DataModelService', [ 'DataService',function (DataService) {
var value = [];
var service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
value = response.persons;
});
},
getResponse : function(){
return value;
}
}
return service;
}
]);
/**DataService**/
fessmodule.factory('DataService', ['$q',function ($q) {
var data = { // dummy
persons: [{
name: "Bob"
}, {
name: "Mark"
}, {
name: "Kelly"
}]
};
var factory = {
changePersonState: function (selectedSubject) {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
} //function
]);