I have a 'notifications app' in my program. In the app there is an 'notices' section. Each notice has an option to delete the notice with a delete button on that row. The 'notices' section also has a clear button. I want to make that clear button delete all of the notices in the 'notices' section. However I am having trouble doing this.
CONTROLLER
(function () {
'use strict';
angular
.module('notifications')
.controller('NotificationsCtrl', NotificationsCtrl);
function NotificationsCtrl($scope, $state, SecService, Service, RecService) {
var vm = this;
vm.ctrlName = 'NotificationsCtrl';
$scope.showAppsMenu = false;
$scope.appList = {};
$scope.dismissNotification = function(notification) {
EcosystemService.dismissNotification(notification.id).then(
function (response) {
delete $scope.appList[notification.appId].notifications[notification.id];
});
};
$scope.dismissAppNotifications = function(app) {
var notifications = app.notifications;
for (var i = 0; i < notifications.length; i++) {
EcosystemService.dismissNotification(notifications[i].id).then(
function(index) {
return function (response) {
delete app.notifications[index];
}
}(i));
}
};
I need to make a change in $scope.dismissAppNotifications. I am just stumped on what I need to add. The 'alerts' are not being cleared still. I added where the Service is being used. Maybe I need to add something here as well?
If I understand you correctly - you want to delete all notifications within one app - so iterating over appList is not necessary. Iterate over one app's notifications instead:
$scope.dismissAppNotifications = function(app) {
var notifications = app.notifications;
for (var id in notifications) {
// may be reasonable to add checking for only own properties
EcosystemService.dismissNotification(notifications[id].id).then(
function(notificationId) { // outer function needed because of the way scopes and closures works in js
return function (response) {
delete app.notifications[notificationId];
}
}(id));
}
};
notifications[id].id can be replaced with simply id if key and id property are the same.
Related
I have an issue in which users aren't updating case status when saving cases. The status field is required and usually always populated. I have a requirement from the client to prompt the user to make sure the case status is updated and correct. Without building a bunch of code for this, I leveraged a form, made everything required not visible (it will be there anyways initially) and only made the case status fields visible.
I did this so that it's always navigated to OnSave as seen as follows:
The following is the JS:
function OnSave(context)
{
debugger;
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
var formContext = formContext;
if (formContext.ui.getFormType()==2)
{
var lblForm = "Case Status Modal";
if (formContext.ui.formSelector.getCurrentItem().getLabel() != lblForm)
{
var items = formContext.ui.formSelector.items.get();
for (var i in items)
{
var item = items[i];
var itemId = item.getId();
var itemLabel = item.getLabel()
if (itemLabel == lblForm)
{
item.navigate();
}
}
}
}
}
The problem here is when I navigate here:
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
The formContext.data.entity.save();kicks the OnSave() off again and the user gets a prompt asking them to save again when they already saved. Totally kills the whole flow.
So I thought I'd create an OnSave helper variable like so: var saveCounter = 0;
I immediately knew this would cause probs.
Then I found this: https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/save-event-arguments/getsavemode
The problem here is that it doesn't same to tell me if the user executed this or was this kicked off by JS? -- The OnSave function.
Am I thinking to hard? am I missing something? any extra eye would help.
Regards,
I suppose .save() to be async... That is why I suggest a setTimeout() below. Anyway, using a global flag to make an exception on the programmatic save is a way to investigate.
let savingProgramatically = false
function OnSave(context)
{
debugger;
// return if the flag is on
if(savingProgramatically) return
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
// Set the flag true
savingProgramatically = true
// save
formContext.data.entity.save();
// set the flag back to false asap -- Adjust the delay
setTimeout(()=>{savingProgramatically = false}, 100) // maybe 0 delay works... Or no setTimeout at all.
...
I am trying to implement a list-details view. The list is generated with $firebaseArray(ref). When an item on the list is clicked, the list controller uses list.$getRecord(item.$id) to get the particular record. Puts it in a global service(just and empty object with set and get) and in my detail controller i assign that service(already set to the selected item) to a scope variable in the detail controller and display it.
The information in the detail view is editable. and when it is editted, a save button appears which when clicked saves the edit using this code
item = editItem; //editItem contains the new changes made on the detail page
list.$save(item).then(function(ref){
//ref.key() === item.$id; //
console.log("Your Edit has been saved');
});
This works. The edits are reflected on the remote firebase data.
But the problem occurs when i navigate back to the list view and try to click another item. It gets an error which says list.$getRecord() is not a function. Now this error doesn't occur when you don't save an edit on the details view.
I printed out the list array before and after i save and i realised this
List array before an item is saved (contains AngularFire methods)
List array after an item is saved (no longer contains AngularFire methods)
I have no idea why $firebaseArray is reacting this way. Is there something i am missing? is this a normal behaviour?
PS: i am using ionic and angularFire.
I can post more code if neccesary
EDIT
Here is an abstraction of the code
List.html
<ion-list>
<ion-item href="#/lead/info" ng-click="vm.selectItem(l.$id)" ng-repeat="l in vm.list" >
<h3>{{l.firstName}} {{l.lastName}}</h3>
<h4 class="">
<p>{{l.topic}}</p>
</h4>
</ion-item>
</ion-list>
list.js (controller function)
function ListCtrl(selectedItem, $firebaseArray) {
/* jshint validthis: true */
var vm = this;
vm.list= {};
vm.selectItem = selectItem;
loadList(); //Loads the List array
function loadList() {
var fireList = new Firebase("https://xxxxx.firebaseio.com/list");
var r = $firebaseArray(fireList);
r.$loaded(function () {
vm.list = r;
});
console.log(vm.list); //Outputs the first image(above). But after an item edit and i go back, outputs the second image(above)
}
function selectItem(index) {
var sItem = vm.list.$getRecord(index);
selectedItem.setList(vm.list);
selectedItem.setItem(sItem);
}
}
The selectedItem service is simple. i use it to set a single object or array of objects
function selectedItem() {
var sItem = {};
var List = {};
return {
getItem: function () {
return sItem;
},
setItem: function (authObject) {
sItem = authObject;
},
getList: function(){
return List;
},
setList: function(al){
List = al;
}
};
};
The detail view controller is ass so:
item.js(controller function)
function ItemCtrl(selectedItem, $scope, $firebaseObject) {
/* jshint validthis: true */
var vm = this;
$scope.selectedItem = selectedItem.getItem();
$scope.listArray = selectedItem.getList();
//vm.item = $scope.selectedItem;
function saveEdit() {
var t = $scope.selectedItem;
$scope.listArray.$save(t).then(function(ref){
//console.log(ref);
});
}
};
UPDATE
After serious cross checking throughout my code i realised the issue is not from AngularFiire array. Even the workaround i did with the r.$watch and r.$loaded was unnecessary. the need for the work around was cause by another part of my code i didnt think was relevant.
I apologise for the mistake. I'd be deleting this question and a related one soon
Try using a watcher to reload the data:
var fireList = new Firebase("https://xxxxx.firebaseio.com/list");
var r = $firebaseArray(fireList);
r.$watch(function() {
r.$loaded(function () {
vm.list = r;
});
});
This is a common way of dealing with updates in an easy way, might solve your problem.
Right now, I am able to create objects and save them to a rails database through a json api. It is a simple todo list app that when I add an object, it goes into the todo list above the input fields. I am also able to query all the objects on the index view and list all of my todo objects.
When I click on a checkbox next to each todo item, it is supposed to change it's :done attribute to true, so that I do not show it anymore after they click a "Clear" button which is the clearCompleted function.
Here is my js code
angular.module("Todo", ["ngResource"])
function TodoController($scope, $filter, $resource) {
Todo = $resource("/todos.json", {id: "#id"}, {update: {method: 'PUT'}})
$scope.todos = Todo.query()
$scope.getTotalTodos = function () {
return $scope.todos.length;
};
$scope.clearCompleted = function () {
Todo.save({done:true})
$scope.todos = $filter("filter")($scope.todos, {done:false});
};
$scope.addTodo = function () {
entry = Todo.save({title:$scope.newTodo.title, importance:$scope.newTodo.importance, description:$scope.newTodo.description, done:false})
$scope.todos.push(entry);
$scope.newTodo.title = '';
$scope.newTodo.importance = '';
$scope.newTodo.description = '';
};
}
What is happening is that rather than updating that object in the database, it is creating a brand new todo item and just giving it the attribute of :done => true. It makes sense because in that clearCompleted function it looks like we are creating an object not updating an existing one.
How do I update the record in the database then?
You shouldn't use Todo.save({}) to update a todo. As you said it create a new object.
Write code as follows.
Todo = $resource("/todos.json", {id: "#id"}, {'save': {method: 'PUT'}})
$scope.todos = Todo.query();
var todo = todos[0];//A todo that should be updated
todo.done = true;
todo.$save();
Mabe you need a parameter for todo key in your save url.
using $resource
In my app, I have to fetch some JSON data and assign it to an array before the page is loaded. This is my code for fetching the JSON using the CardService service:
cards = [];
var cs = {
...
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
cards = data;
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},
getCards: function() { return cards; };
...
}
In the controller's resolve block, I have the following:
WalletController.resolve = {
getCards: function(CardService) {
CardService.fetchCards().then(loadView, showError);
}
}
And in the actual controller, I have the following:
function WalletController($scope, CardService) {
$scope.cards = CardService.getCards();
}
The problem is, the fetchCards function in the service seems to resolve the promise before the JSON data is assigned to the cards variable. This leads to my view loading with blank data until I refresh a couple times and get lucky.
I can confirm the late loading as when I log the cards variable in the console, I get an empty array at line 122 (when my view is loaded) and a full array at line 57 (when the JSON call is successful). Line 57's code somehow executes after the view is loaded.
How do I fix this?
I haven't used resolve but I'm throwing this out there just in case the issue you are having is related to binding to an array returned from a service.
If you are returning your cards array from a service and binding to it in the UI you may want to try to populate that same array instead of setting cards = data; (which will overwrite the local cards with a new array which is not bound to the UI).
Something like:
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
cards.length = 0;
for(var i = 0; i < data.length; i++){
cards.push(data[i]);
}
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},
See this fiddle for a working example of what I'm trying to describe. Clicking the first button multiple times will update the view but once you click on the second button the binding will be broken.
The main difference between the two is:
First button uses data.length = 0 and data.push() to retain the original array's reference
Second button overwrites the original data array reference with a new one using data = newArray
Update: Also, as Mark Rajcok, mentioned below you can use angular.copy to retain the original array's reference by emptying it out and adding new ones from the source like this:
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
angular.copy(data, cards);
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},
I want to update a div with a list of anchors that I generate from a local database in chrome. It's pretty simple stuff, but as soon as I try to add the data to the main.js file via a callback everything suddenly becomes undefined. Or the array length is set to 0. ( When it's really 18. )
Initially, I tried to install it into a new array and pass it back that way.
Is there a setting that I need to specify in the chrome manifest.json in order to allow for communication with the database API? I've checked, but all I've been able to find was 'unlimited storage'
The code is as follows:
window.main = {};
window.main.classes = {};
(function(awe){
awe.Data = function(opts){
opts = opts || new Object();
return this.init(opts);
};
awe.Data.prototype = {
init:function(opts){
var self = this;
self.modified = true;
var db = self.db = openDatabase("buddy","1.0","LocalDatabase",200000);
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS listing ( name TEXT UNIQUE, url TEXT UNIQUE)",[],function(tx,rs){
$.each(window.rr,function(index,item){
var i = "INSERT INTO listing (name,url)VALUES('"+item.name+"','"+item.url+"')";
tx.executeSql(i,[],null,null);
});
},function(tx,error){
});
});
self._load()
return this;
},
add:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("INSERT INTO listing (name,url)VALUES(?,?)",[item.name,item.url],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',error)
})
});
self._load()
},
remove:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("DELETE FROM listing where name='"+item.name+"'",[],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',tx,error);
});
});
self._load()
},
_load:function(callback){
var self = this;
if(!self.modified)
return;
self.data = new Array();
self.db.transaction(function(tx){
tx.executeSql('SELECT name,url FROM listing',[],function(tx,rs){
console.log(callback)
for(var i = 0; i<rs.rows.length;i++)
{
callback(rs.rows.item(i).name,rs.rows.item(i).url)
// var row = rs.rows.item(i)
// var n = new Object()
// n['name'] = row['name'];
// n['url'] = row['url'];
}
},function(tx,error){
//console.log('error',tx,error)
})
})
self.modified = false
},
all:function(cb){
this._load(cb)
},
toString:function(){
return 'main.Database'
}
}
})(window.main.classes);
And the code to update the list.
this.database.all(function(name,url){
console.log('name','url')
console.log(name,url)
var data = []
$.each(data,function(index,item){
try{
var node = $('<div > '+item.name + '</div>');
self.content.append(node);
node.unbind();
node.bind('click',function(evt){
var t = $(evt.target).attr('href');
chrome.tabs.create({
"url":t
},function(evt){
self._tab_index = evt.index
});
});
}catch(e){
console.log(e)
}
})
});
From looking at your code above, I notice you are executing "self._load()" at the end of each function in your API. The HTML5 SQL Database is asynchronous, you can never guarantee the result. In this case, I would assume the result will always be 0 or random because it will be a race condition.
I have done something similar in my fb-exporter extension, feel free to see how I have done it https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js
To solve a problem like this, did you check the Web Inspector and see if any errors occurs in the background page. I assume this is all in a background page eh? Try to see if any error occurs, if not, I believe your encountering a race condition. Just move the load within the callback and it should properly call the load.
Regarding your first question with the unlimited storage manifest attribute, you don't need it for this case, that shouldn't be the issue. The limit of web databases is 5MB (last I recall, it might have changed), if your using a lot of data manipulation, then you use that attribute.
Just make sure you can guarantee the this.database.all is running after the database has been initialized.