In my app I have one page that shows the details of a particular users document in which a document can be deleted and another that shows the users recently viewed documents. I'm trying to make it so when a user deletes a document in the view page it also deletes the listing in the users recently viewed documents history.
I'm attempting to use $broadcast and $rootScope to communicate between the two modules but I suspect I'm doing something wrong with the syntax or there's an issue with scope or project structure.
Here's the two separate functions that delete the different entries
history.js
//gets the selected item the user clicks and deletes it and updates history
$scope.removeFavorite = function(item) {
var items = $scope.recent[item.type];
item = items.splice(items.indexOf(item), 1)[0];
$rootScope.$on('deleteRecent', function(data) {
historyManager.remove(data);
});
historyManager.remove(item).then(loadHistoryItems, loadHistoryItems);
};
documentView.js
//confirmation that user wants selected document deleted
function confirmDelete() {
var delObj = _.pick(sDocument, 'Doc_Type', 'Doc_Num');
repos
.sDoc
.del(delObj);
var item = sDocument.Doc_Type + ';' + sDocument.Doc_Num;
$rootScope.$broadcast('deleteRecent', item);
tabBarViewModel.removeTabByState($state.get('sDocument'), delObj);
$scope.modalOptions.hide();
}
Given that your rootScope injection is fine your problematic part is this one:
$scope.removeFavorite = function(item) {
var items = $scope.recent[item.type];
item = items.splice(items.indexOf(item), 1)[0];
$rootScope.$on('deleteRecent', function(data) {
historyManager.remove(data);
});
historyManager.remove(item).then(loadHistoryItems, loadHistoryItems);
};
First if you're using $rootScope to broadcast, message can be received on any $scope, other then that, you don't wrap it into function on $scope and first parameter is event.
So correct code would look like this:
$scope.$on('deleteRecent', function(event, data) {
historyManager.remove(data);
});
$scope.removeFavorite = function(item) {
var items = $scope.recent[item.type];
item = items.splice(items.indexOf(item), 1)[0];
historyManager.remove(item).then(loadHistoryItems, loadHistoryItems);
};
Related
I’m running into the following scenario, which I am unsure how to tackle.
I need to be able to copy the list-item-level permissions from one list item to another one in a separate list. In other words:
List A contains an item named itemA. List B contains an item named itemB. I want to duplicate the item-level permissons of itemA into itemB.
Does anyone know a way to do this? This can be with front-end code using JSOM, or with a workflow (2010 or 2013).
Things I've already tried:
The "Inherit parent permissions" workflow action. However, I want to inherit the workflow permissions from another list item, not from the list itself.
My scenario is similar to this one: Inheriting list item permissions via permissions on lookup field item. Except I don't have the ability to build event-receivers so I'm looking for a JSOM or workflow solution.
The following example shows how to copy list item permissions via SharePoint JSOM API:
Prerequisites:
Unique permissions should be assigned for source and target list items
SP.SOD.executeFunc('SP.js', 'SP.ClientContext',
function(){
var ctx = SP.ClientContext.get_current();
var sourceListItem = ctx.get_web().get_lists().getByTitle("InTasks").getItemById(1);
var targetListItem = ctx.get_web().get_lists().getByTitle("OutTasks").getItemById(1);
copyListItemPermissions(sourceListItem,targetListItem,
function(){
console.log("Done");
},
function(sender,args){
console.log(args.get_message());
}
);
});
where
function copyListItemPermissions(sourceListItem,targetListItem,success,error)
{
var ctx = targetListItem.get_context();
var sourceItemPermissions = sourceListItem.get_roleAssignments();
var targetItemPermissions = targetListItem.get_roleAssignments();
ctx.load(sourceItemPermissions,'Include(Member)');
ctx.load(targetItemPermissions,'Include(Member)');
ctx.executeQueryAsync(
function(){
targetItemPermissions.get_data().forEach(function(targetAssignment){
targetAssignment.deleteObject();
});
sourceItemPermissions.get_data().forEach(function(sourceAssignment){
var principal = sourceAssignment.get_member();
if(principal.get_principalType() == 1)
targetItemPermissions.add(principal,sourceAssignment.get_roleDefinitionBindings());
else if (principal.get_principalType() == 8){
var group = ctx.get_web().get_siteGroups().getById(principal.get_id());
targetItemPermissions.add(group,sourceAssignment.get_roleDefinitionBindings());
}
});
targetListItem.update();
ctx.executeQueryAsync(
function(){
success();
},
error
);
},
error
);
}
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 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.
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