I would like to make a collection of all elements that are selected and not.
The dom element consists of several multiple select.
Each of them have the same users.
My goal is to create a collection of all users and for the user which are selected add an attribute with a specific value.
Here is my code js code (1) and here is the link http://jsfiddle.net/vxRtb/9/.
My code works, but I would like to dry the code because, maybe, lopping just on the first select to get all the user is not required.
Any hints how to dry the following js code?
Please read the comments on the js code for more info; thanks
P.S.:
1) I am using jQuery and underscore
2) From the server I get the html code, the same as in jsfiddle.net/vxRtb/9
$(function () {
var $selectElements = $('form .controls select');
var userCollection = [];
// Subroutine_1
// TODO Subroutine_1 and Subroutine_2 seems too close; any idea how to dry this code?
$.each($($selectElements[0]), function (i, teamElement) {
var $users = $(teamElement).find('option')
$.each($users, function (i, user) {
userCollection.push({
id: $(user).val(),
name: $(user).text(),
});
});
});
// Subroutine_2
$.each($selectElements, function (i, teamElement) {
var $teamElement = $(teamElement);
//var teamId = $teamElement.attr('id');
var teamName = $teamElement
.parent().parent().closest('.controls')
.find('input').val();
var $users = $teamElement.find('option:selected');
$.each($users, function (i, user) {
_.where(userCollection, {id: $(user).val()})[0].team = teamName;
});
});
console.log(userCollection);
});
This is a fairly old question and you've probably moved on from it, however, I took a try at this and here's what I came up with.
Analysis
The loops are very similar with the key difference of one loop builds up the team members and the other loop figures out their team. I think the DRY option is to only use one loop and test if a member has a team or not. There will be repetition as the user id's are non-unique, so some logic needs to be applied.
Pseudo Code
Create User Object Collection based off of the <option> tag.
Use getMyTeam to generate the team name
Group the User Objects by ID.
Filter User Objects taking only those with a team names
Filter User Objects without team names to show only uniq records
Code
I'm not sure if this is any better. Performance-wise, this should be fairly poor due to all the nesting. However, I guess the advantage is that the code becomes much more modular and you can easily change the rules if needed. jsFiddle
$(function () {
// Helper to find a team based on an options element
function getMyTeam() {
if (this.is(':selected')) {
var select = this.parent();
var teamId = select.attr('id').replace(/_participations$/, '_name');
return $('#' + teamId).val();
}
return undefined;
};
// Helper to return an object's id
function getId(obj) {
return obj.id;
};
// Helper to filter the team members by team name.
function filterMembersByTeam(members) {
var result = _.filter(members, function(record) {
return !_.isUndefined(record['team']);
});
if (result.length === 0) {
result = _.uniq(members, true, function(member) {
return JSON.stringify(member);
})
};
return result;
};
// 1. Select the users
var options = $('form .controls select option');
// 2. Build up the user data
var userCollection = _.map(options,
function(option) {
return {
id: $(option).val(),
name: $(option).text(),
team: getMyTeam.apply($(option))
};
});
// 3. Clean & filter the user data
userCollection =
_.flatten(
_.map( _.groupBy(userCollection, getId), filterMembersByTeam)
);
console.log(userCollection);
});
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'm in need of some expert JavaScript advice. I'm coding using Electron.
The issue: I'm trying to capture the value selected from the second of two dropdown lists and pass it back into a JavaScript file. The code below is ordered as it would run. The dropdown code is not shown as it is simply populated by the viewProvinces function below. The first dropdown's id is "country-select" while the second is "province-select".
In this case, a link is clicked in Index.html which calls the anonymous function in Data.js. The anonymous function calls viewProvinces that populates the parray/data variables from the anonymous function which produces the alert showing the value returned.
(FYI) viewProvinces also populates the second dropdown (id province-select) by filtering based on the values produced in the first dropdown (id country-select). For example, if Afghanistan is selected as a country in the first, then only provinces from Afghanistan are shown in the second.
Moving on, viewProvinces calls Provinces which is an array populated when it calls getProvinces after querying a SQLite database for the source data.
ViewProvinces, Provinces, and getProvinces all work correctly. The link and the anonymous function are the issue and technically work in that they produce a result, but not the correct result. When the link is clicked it produces "object Object". I believe I understand why it is doing this, though I am not skilled enough (yet) to know how to fix it. I do not know how to adjust the code so that it returns the actual value(s) selected from the second (provinces) dropdown.
Put simply, the data is gathered from a SQL query that populates a series of arrays that further populates the dropdown list. The value of this list, once selected, should be returned back to the source JavaScript file into a variable (it fails here).
Apologies if this sounds convoluted, but I'm trying to be thorough. Any help in this matter would be greatly appreciated! Thank you!!
Index.html:
<a id="test-select" href="#">test</a>
Data.js:
$( "#test-select" ).click(function(e) {
e.preventDefault();
var parray = viewProvinces($("#country-select").val());
var data = $('#test-select').data(parray);
alert(data);
});
View.js:
function viewProvinces(ccode) {
var viewPro = Provinces(function(results) {
// Code only gets triggered when Provinces() calls return done(...);
var container = document.getElementById('province-select');
var fragment = document.createDocumentFragment();
results.filter(function(el) {
return el.ccode === ccode;
}).forEach(function(loc, index) {
var opt = document.createElement('option');
opt.textContent = loc.pname;
opt.value = loc.pcode;
fragment.appendChild(opt);
});
container.appendChild(fragment);
});
}
Model.js:
function Provinces(done) {
//Pull location values from data
return getProvinces(done);
}
Data.js:
function getProvinces(done) {
var sqlite3 = require('sqlite3').verbose();
var file = 'db/locations.sqlite3';
var db = new sqlite3.Database(file);
var stmt = 'SELECT Country.CountryId, Country.CountryCode, Country.CountryName, Province.ProvinceName, Province.ProvinceCode FROM Province INNER JOIN Country ON Province.CountryId = Country.CountryId'
var larray = [];
db.all(stmt, function(err, rows) {
// This code only gets called when the database returns with a response.
rows.forEach(function(row) {
larray.push({
ccode: row.CountryCode,
cname: row.CountryName,
pname: row.ProvinceName,
pcode: row.ProvinceCode
});
})
return done(larray);
});
db.close();
}
I have tried to answer your question via a fiddle based on your code created here but I had some trouble understanding a couple of things in your code, so I might have missed something. The main change I made was to make the Provinces(function(results) {.. function return the array of filtered provinces:
function viewProvinces(ccode) {
return Provinces(function(results) {
var provinces = [];
// Code only gets triggered when Provinces() calls return done(...);
var container = document.getElementById('province-select');
var fragment = document.createDocumentFragment();
results.filter(function(el) {
return el.ccode === ccode;
}).forEach(function(loc, index) {
var opt = document.createElement('option');
opt.textContent = loc.pname;
opt.value = loc.pcode;
fragment.appendChild(opt);
provinces.push(loc);
});
container.appendChild(fragment);
return provinces;
});
Once this is done, the parray is correctly setup:
var parray = viewProvinces($("#country-select").val());
However, I was confused when I read this code:
var data = $('#test-select').data(parray);
alert(data);
I assumed you were trying to save the provinces data in the link's store, so modified the code as follows to demo that it works:
$('#test-select').data({
provinces: parray
}); // Save the provinces array
var data = $('#test-select').data(); // Retrieve the provinces array
//Dump it on the console
$.each(data.provinces, function(index, province) {
console.log("Province[" + index + "].name: " + province.cname);
});
I'm trying to represent multiple selects with its selected values from backend JSON to knockout view model.
And it's needed to retrieve this JSON when each select is changed, first time - all is ok, but if I apply mapping again (ko.mapping.fromJS(test_data, ViewModel)), all subscriptions are lost does anyone know how to avoid this situation?
jsfiddle (I don't know why selects don't have its values, without jsfiddle - all is ok):
http://jsfiddle.net/0bww2apv/2/
$(ViewModel.attributes()).each(function(index, attribute) {
attribute.attribute_value.subscribe(function(name) {
console.log('SUBSCRIBE', name);
var send_data = {};
$(ViewModel.attributes()).each(function (index, attribute) {
send_data[attribute.attribute_name.peek()] = attribute.attribute_value.peek();
if (attribute.attribute_value() === null) {
send_data = null;
return false;
}
});
if (send_data) {
console.log('REQUEST TO BACKEND: ', ko.toJSON(send_data));
ko.mapping.fromJS(test_data, ViewModel);
// subscriptions is lost here !
}
});
});
At last I've solved my own question with knockout.reactor plugin,
If we remove all auxiliary constructions, it will look like:
var ViewModel = ko.mapping.fromJS(test_data);
ko.applyBindings(ViewModel);
ko.watch(ViewModel, { depth: -1 }, function(parents, child, item) {
// here we need to filter watches and update only when needed, see jsfiddle
ko.mapping.fromJS(test_data2, {}, ViewModel);
});
This way we update selects and don't have troubles with subscription recursions.
full version (see console output for details): http://jsfiddle.net/r7Lo7502/
I'm using Parse's Cloud Code to write a method that will return and array containing two sub arrays. With on array being an array of teams and the other being an array containing arrays of those team members. With the purpose of getting a list of teams and their team members. The arrays will be passed to an iOS front end to display.
My data is currently structured so that there is a Team object that can have multiple Users but a User can only have one team.
Here's my cloud function as it lives now:
var arrayOfUsers = [];
var arrayOfTeamMembers = [];
var arrayOfTeams = [];
Parse.Cloud.define("GetTopTeams", function(request, response) {
var query = new Parse.Query("Teams");
arrayOfTeams = [];
arrayOfTeamMembers = [];
fullArray = [];
query.find().then(function(teams) {
//returns a raw list of teams
return teams;
}).then(function(teams) {
arrayOfUsers = [];
teams.forEach(function(team) {
//add team to subArray1
arrayOfTeams.push(team);
//searches for all users within the team
var memberQuery = new Parse.Query(Parse.User);
memberQuery.equalTo('team', team);
memberQuery.find().then(function(tMember) {
//adds the team member to initial array
arrayOfUsers.push(tMember);
});
}).then(function(){
//pushes the finished array to the team member array
return arrayOfTeamMembers.push(arrayOfUsers);
}).then(function() {
return arrayOfTeams;
});
}).then(function() {
return response.success([arrayOfTeams, arrayOfTeamMembers]);
});
});
I'm able to populate the arrayOfTeams but getting the arrayOfTeamMembers is proving to be difficult. Is there a better way to nest them? I tried promises but am unfamiliar with Javascript promises. I've found similar posts but none that address the issue I'm having. Any suggestions?
You could use Underscore. It is already available in Cloud Code. Using it you will need only one query. And you could write something like this:
var memberQuery = new Parse.Query(Parse.User);
memberQuery.exists("team");
memberQuery.include("team");
memberQuery.find().done(function(users){
var usersGroupedByTeam = _.groupBy(users, function(user) {
return user.get("team").id;
});
var arrayOfTeams = _.chain(users)
.map(function(user) { return user.get("team"); })
.uniq(function(team) { return team.id; })
.value();
response.success({
arrayOfTeams: arrayOfTeams,
usersGroupedByTeam: usersGroupedByTeam
});
}).fail(function(error){
//error handle
response.error(error.message);
});
I suggest you to return your result as an object, instead of an array. In your final result you will have the Teams (arrayOfTeams) and your users grouped by Team (usersGroupedByTeam).
And to retrieve specific team's users you will use usersGroupedByTeam["A_TEAM_ID"].
NOTE: remember that, by default, Parse.Query brings only 100 records. You could change it using Parse.limit, but don't forget to take this into account.
Is it possible to automatically run populate() for referenced subdocuments for a particular model?
I have a City model that has referenced Region and Country documents that I would like to be automatically populated when a city/cities are fetched.
Well, there aren't docs for this in the Mongoose website; what I do is something like this:
schema.statics.createQuery = function( populates ) {
var query = this.find();
populates.forEach(function( p ) {
query.populate( p );
});
return query;
};
Of course, there is validation and some other stuff in this method, but essentially it's what I do with my models.
In your case, you could hard code the populates in such a method, if you strictly need them in every find call.
AFAIK there's no way to auto-populate all references to another model out of the box (there are plugins though). In a similar fashion to #gustavohenke's answer you can use a static, along with a small change to your find query.
Here's what I'd do:
citySchema.statics.fieldsToPopulate = function() {
return ['regionField', 'countryField'];
};
Where regionField and countryField are the fields which reference the models Region and Country respectively.
Then in your query you could populate accordingly:
var populate = city.fieldsToPopulate ? city.fieldsToPopulate() : [];
city.findById(id)
.populate(populate)
.exec(function(err, data) {
if (err) {
return next(err);
} else {
res.render('template', { city: data });
}
});