I am trying to figure this out the best way to do this. I am trying to insert data into a WebSQL table and then select the data from the table and display on the screen using ng-repeat. I am using this Angular WebSQL Module https://github.com/paulocaldeira17/angular-websql#select-all.
So far, I can get the remote data and insert them into the local database. When I try to call the insert data, $scope.localproducts shows an empty array - console.log( $scope.localproducts) shows an empty array.
I use localproducts scope for my ng-repeat.
I can't get to return the ProductsFactory.localproducts array to my controller from the Factory's selectAllData function.
When clicks a button on my page, it calls the insertData function in my Controller.
What have I done wrong here? I am pretty new to angular so I would very much appreciate if someone can help me to improve the below code or suggest if there is a better way to do this.
.controller('DownloadProductsCtrl', ['$scope','ProductsFactory', function ($scope, ProductsFactory){
$scope.products = ProductsFactory.products;
$scope.localproducts = ProductsFactory.localproducts;
$scope.insertData = function(){
ProductsFactory.getRemoteData().then(function(results){
$scope.localproducts = ProductsFactory.localproducts;
console.log( $scope.localproducts); //This shows an empty array
});
}; }])
.factory('ProductsFactory', ['$webSql', function($webSql){
db = $webSql.openDatabase('myappdb', '1.0', 'Test DB', 2 * 1024 * 1024);
ProductsFactory = {};
ProductsFactory.products = [];
ProductsFactory.localproducts = [];
ProductsFactory.getRemoteData = function () {
return $http.get('./products/list.json')
.success(function (data) {
ProductsFactory.products = data;
ProductsFactory.insertData(data);
})
.error(function () {
console.error('Error');
});
};
ProductsFactory.insertData = function (data){
angular.forEach(data, function(value, key) {
db.insert('products', value).then(function(results) {
<!-- In here I like to count the total inserted items and display it on the page, but not sure sure how to send it to a scope in my controller -->
});
});
ProductsFactory.selectAllData();
};
ProductsFactory.selectAllData = function(){
db.selectAll("products").then(function(results) {
for(var i=0; i < results.rows.length; i++){
ProductsFactory.localproducts.push(results.rows.item(i)); //This added data to the array successfully.
}
console.log(ProductsFactory.localproducts); //This shows an empty array
});
};
return ProductsFactory;
}]);
Try with this resource as a start point.
https://gist.github.com/jgoux/10738978
https://github.com/paulocaldeira17/angular-websql/blob/master/angular-websql.js
The first one is very basic and easier to understand. The second more involved.
Related
I want to grab some JSON Files with its content and make it available for other controllers. For this I know that I should create a factory that grabs the json files. My problem is that I dont know how to get the factory to return deityData.
Here is my Code that i want in a factory:
app.controller("dataContainer", function($scope,$http){
$http.get("./data/deitys/data.json").then(function (response) {
$scope.myData = response.data;
for(var i = 0; i < $scope.myData["deitys"].length;i++){
var dataString = "./data/deitys/" + $scope.myData["deitys"][i] +".json";
$http.get(dataString).then(function (response_) {
var deityData = response_.data;
$scope.deitys[deityData.deityName] = deityData;
});
}
});
Let's create a service for this, say deityService.
angular.service("deityService", () => {
this.deityData = undefined;
});
In all those controllers who want that data, add deityService as a dependency.
When you are done fetching data in your dataContainer controller, set deityData like this and emit an event that will let all others know that data has been set.
this.deityService.deityData = fetched_data;
this.deityService.emit("DEITY_DATA_SET");
Add a listener to all those controller for event
this.deityService.on("DEITY_DATA_SET",() => {
// now you can do console.log(this.deityService.deityData);
}
);
I am using a static json file to simulate my server and getting my array of orders from it.
I'm presenting the orders in a table in my html file with the option of deleting one from it.
Each time I load the html file the full list gets loaded, with the orders I have deleted throught the controller function.
How can I loat the data from the factory only once?
Here is my controller:
app.controller("MainPageCtrl", function($scope, getOrdersFactory)
{
$scope.orders = [];
// Getting the data frm the facrory
var dataPromise = getOrdersFactory.getDataFunc();
dataPromise.then(function(data){
$scope.orders = data.orders;
});
// Deletes an orders.
$scope.deleteOrder = function(order){
// Finds the index of the order.
var orderIndex = $scope.orders.indexOf(order);
// Delete the order.
$scope.orders.splice(orderIndex, 1);
};
});
By default angular services and factories are singletons(loaded only once). The problem you are facing is with controller re-initialization. When route change happens the controller is re-initialized so therby getting the previous value from the factory.
You can use a setter function on your 'getOrdersFactory'.
Assuming your 'getOrdersFactory' to be
app.factory('getOrdersFactory',function(){
//code to read from file and set the file on a variable orderDetails
var orderDetails = contentsReadFromFile;
return{
getDataFunc:function(){
return orderDetails
},
setDataFunc:function(modifiedOrderDetails){
orderDetails = modifiedOrderDetails;
//code to set the new content to the static file
}
}
}
code to read the file from the static file will be rendered when you inject the factory for the first time, and on your controller set the order details on the delete function
// Deletes an orders.
$scope.deleteOrder = function(order){
// Finds the index of the order.
var orderIndex = $scope.orders.indexOf(order);
// Delete the order.
$scope.orders.splice(orderIndex, 1);
getOrdersFactory.setDataFunc($scope.orders);
};
I guess you are losing your data i.e $scope.orders .If this is the scenario just change
dataPromise.then(function(data){
$scope.orders = data.orders;
});
to
dataPromise.then(function(data){
$scope.orders = angular.copy(data.orders);
});
I've a problem that, unfortunately, I was not able to solve in a while, even looking at related StackOverflow Q/A.
I'm building an application using MEAN and I'm having an issue
when I need to render new items trough ng-repeat.
I have lots of items stored in a MongoDB instance, and I'm perfectly
able to fetch all of them trough API calls.
I need to show only 24 items at the very beginning, and 24 more every
time the user clicks on a show more button. I always need to
concatenate them after the old ones.
It works perfectly with the first 24 items but It does not render
other items.
When I try to log the new fetched items, I get them with no problems.
I'm able to see their attributes and so on.
This is a short cut of my items View:
<div class="myItem" ng-repeat="item in searchCtrl.items track by $index">
. . . .
</div>
This is my Show More Button:
<a class="showMoreButton" ng-click="searchCtrl.goToNextPage()">show more</a>
This is a simplified version of my Controller also known as searchCtrl:
function SearchController($scope, ItemFactory) {
var vm = this;
//Needed for pagination, 24 items at a time, starting from page 1
vm.searchParams = {
size : 24,
page : 1
}
//Initialize Empty Array to Contain Items
vm.items = [];
/*Calling fetchItems to fetch the items the very
first time the Controller is called*/
fetchItems();
//Calls goToPage passing it a new page (It handles pagination)
vm.goToNextPage = function() {
var next = parseInt(vm.info.currentPage) + 1;
vm.goToPage(next);
};
//Calls fetchItems after setting the new page
vm.goToPage = function(page) {
vm.searchParams.page = page;
fetchItems();
};
//Calls getItems and pushes the single items into vm.items
function fetchItems(){
ItemFactory.getItems(vm.searchParams).then(function(response){
//iterates trough items
for (var i = 0; i < response.data.data.length; i++) {
//Log current item
console.log(JSON.stringify(response.data.data[i]));
//push current item into vm.items
vm.items.push(response.data.data[i]);
}
//Print correctly the new items pool
console.log(vm.items);
}, function(error){
$log.error(error);
});
}
};
This is a simplified version of my ItemFactory:
angular.module('myApp').factory('ItemFactory',
function ($http, API_URL) {
//Getting items from API
function getItems(params) {
return $http.get(API_URL + '/item',{params: params}
).then(function success(response) {
return response;
});
}
return {
getItems : getItems
}
});
Controller binding to my view, it work as it should. I'm using this modularized approach and it always works perfectly:
'use strict';
angular.module('myApp')
.config(itemRoute);
function itemRoute($stateProvider) {
$stateProvider
.state('index.items', {
url : 'index/items',
parent : 'index',
templateUrl : 'app/main-pages/items/items.html',
controller : 'SearchController',
controllerAs : 'searchCtrl'
});
}
I also tried using concat instead of looping trough items with a for but the result does not change:
//Instead of looping
vm.items = vm.items.concat(response.data.data);
Essentially:
I'm only able to render the first 24 items
I can not render all the other items even if they get properly inserted into items array
Items starting from 25 and so on do not get into the DOM
I already tried using $scope.$apply(); but I get digest errors
Questions:
What is causing this?
How can I solve this issue?
Thanks in advance, if you need any clarification just post a comment below.
I managed to solve this issue broadcasting a message from ItemFactory when fetching new items and attaching a listener to that message in SearchController. When ItemDataRefresh gets broadcasted, then, SearchController concatenates the new data.
ItemFactory:
function getItems(params) {
return $http.get(API_URL + '/item',{params: params}
).then(function success(response) {
$rootScope.$broadcast('refreshData', response.data);
return response;
});
}
SearchController:
function fetchItems(){
ItemFactory.getItems(vm.searchParams).then(function(response){
//When vm.items is empty
if (!vm.items.length) {
vm.items = vm.items.concat(response.data.data);
}
//on refreshData
$scope.$on('refreshData', function(event, data){
vm.items = vm.items.concat(data.data);
});
}, function(error){
$log.error(error);
});
}
I know that I should not use rootScope so I'm still looking for a way to make it work in a cleaner way.
I hope it will help someone.
I've tried several different approaches, but I've been unsuccessful in having my view update its contents. My view contains a list of customers and a list of employees.
This represents what I've tried so far omitting employees as its essentially duplicated code.
function dbControl() {
var self=this;
self.customers = function() { $.ajax(...) }; // returns customers object
self.addCustomer = function() { $.ajax(...) };
self.delCustomer = function() { $.ajax(...) };
}
var DB = new dbControl();
var VM = {};
// Populate the VM
VM.selCustomer = ko.observable();
VM.customers = ko.mapping.fromJS(DB.customers);
VM.addCustomer = function() {
DB.addCustomer() // successfully adds to Database
VM.customers.push(); // push VM.selCustomer() into VM.customers
}
VM.delCustomer = function() {
DB.delCustomer() // succcessfully removes from Database
VM.customers.remove(); // removes VM.selCustomer() from VM.customers
}
// Knockout Binding
ko.applyBindings(VM);
The data-bind="foreach: customers" binding works on the webpage just fine and lists all the entries.
All the AJAX calls successfully LIST/ADD/DELETE from the Database properly, but the View does not update after a successful DELETE.
I've tried adding ko.mapping.fromJS(DB.customers, VM) to the end of the delete function in the VM as instructed in the DOCS:Then, every time you receive new data from the server, you can update all the properties on viewModel in one step by calling the ko.mapping.fromJS function again, but no luck.
I've tried adding VM.customers.valueHasMutated() to the VM and it still doesn't update.
I've tried creating an interval to run both previous attempts:
setInterval(function() {
VM.customers = [];
VM.customers = ko.mapping.fromJS(DB.customers);
VM.customers.valueHasMutated();
}, 1000);
Am I structuring this whole project wrong?
VM.addCustomer = function() {
DB.addCustomer() // successfully adds to Database
VM.customers.push(); // push VM.selCustomer() into VM.customers
}
VM.delCustomer = function() {
DB.delCustomer() // succcessfully removes from Database
VM.customers.remove(); // removes VM.selCustomer() from VM.customers
}
You're not passing anything to push or remove; those two methods of observableArray definitely want arguments. Depending on how the methods of DB are written, you may have the same problem with them.
Ok, so after more copy/pasting, I think I've solved my own problem:
The reason the View wasn't updating was because the data coming from the database was never being "re-called".
To fix the problem, within the add/delete functions in the VM, I had to call first the function that pulls the data from the server DB.getCustomers(), then make the ko.mapping.fromJS(DB.customers, {}, VM.customers) call.
function dbControl() {
var self = this;
getCustomers = function() {
$.ajax({
/* URL/TYPE/DATA/CACHE/ASYNC */
success: function() {
self.customers = data; // Now, DB.customers == Updated Data!
}
});
}
}
var DB = new dbControl();
VM = {};
VM.addCustomer = function() {
DB.addCustomer() // successfully adds to Database
//VM.customers.push(); <------ Remove the redundancy
DB.getCustomers(); // get the new list from the server
ko.mapping.fromJS(DB.customers, {}, VM.customers);
}
VM.delCustomer = function() {
DB.delCustomer() // succcessfully removes from Database
//VM.customers.remove(); <------ Remove the redundancy
DB.getCustomers(); // get the new list from the server
ko.mapping.fromJS(DB.customers, {}, VM.customers);
}
Let me explain my issue, I am trying to populate Ember.Select directly from database.
I have these routes:
this.resource('twod', function() {
this.resource('twoduser', {
path : ':user_id'
});
});
In twoduser, I am displaying a full information about a single user. In that view, I have a Select Box as well, which end user will select and then with a button, he can add the user to a team that he selected from Ember.Select.
I tried to do this,
App.TwoduserController = Ember.ArrayController.extend({
selectedTeam : null,
team : function (){
var teams = [];
$.ajax({
type : "GET",
url : "http://pioneerdev.us/users/getTeamNames",
data : data,
success : function (data){
for (var i = 0; i < data.length; i ++){
var teamNames = data[i];
teams.push(teamNames);
}
}
});
return teams;
}.property()
})
Then in my index.html:
{{view Ember.Select
contentBinding="team"
optionValuePath="teams.team_name"
optionLabelPath="teams.team_name"
selectionBinding="selectedTeam"
prompt="Please Select a Team"}}
But when I do this, for some reason it interferes with Twoduser and I am not able to view the single user.
Furthermore, here's a sample JSON response I will get through the url:
{"teams":[{"team_name":"Toronto Maple Leafs"},{"team_name":"Vancouver Canuck"}]}
Moreover, I am fetching all users using Ajax like this:
App.Twod.reopenClass({
findAll : function() {
return new Ember.RSVP.Promise(function(resolve, reject) {
$.getJSON("http://pioneerdev.us/users/index", function(data) {
var result = data.users.map(function(row) {
return App.Twod.create(row);
});
resolve(result);
}).fail(reject);
});
},
findBy : function(user_id) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var user = App.Twod.create();
$.getJSON("http://pioneerdev.us/users/byId/" + user_id, function(data) {
var result = user.setProperties(data.user);
resolve(result);
}).fail(reject);
});
}
});
Though there's one thing, I have a separate Teams route:
this.resource('teamview', function(){
this.resource('teamviewdetail', {
path : ':team_id'
});
});
Which shows all the teams and a single team when you click on a single team.
Can I use that TeamviewController? or Can I fetch team names from Twoduser Controller and push names to the array as I mentioned before?
More Information:
If I use the way I mentioned, I get this error:
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Here's a working jsfiddle on the issue I am experiencing. You can select "Storyboard" from the Designation & then select the user. That will reproduce the issue.
One more Update: Seems using ObjectController instead of ArrayController issue solves the addArrayObserver issue. But still I can't get the teams in the Ember.Select.
The biggest issue here is that you use Array#push instead of pushObject. Ember needs the special methods in order to be aware of changes. Otherwise, it will continue to think that the array of teams is as empty as when you first returned it. Second biggest issue is that your ajax success call isn't accessing the returned data properly.
Also, optionValuePath and optionLabelPath are relative to the individual select option view, so they should start with content, which is the individual item as set on the view. So: content.team_name