TFS 2012 Web Access Work Item Custom Control - custom-controls

I´m developing a TFS 2012 web access custom control and I need to change some workitem field values when the save workitem button is clicked.
I’ve just developed the same custom control for Visual Studio and I’ve performed these changes in the IWorkItemControl.FlushToDatasource method but I don't know how to achieve the same at web access control.
I’ve tried to change the workitem field values in the invalidate function when the workitem is being saved,
invalidate: function (flushing) {
if (this._workItem.isSaving()) {
this._workItem.getField("FieldName").setValue("newValue");
}
},
But it does not work, although the changes made while saving the workitem are included in the list of updated fields, they are not saved.
Any idea how can it be implemented by using the Javascript API?
Thanks.
Oscar

Can you try this:
_control: null,
_init: function() {
alert("_init() called!");
debugger;
if (this._workItem) {
var originalEstimate = this._workItem.getField("Microsoft.VSTS.Scheduling.OriginalEstimate").getValue();
alert('OriginalEstimate value is ' + originalEstimate);
console.log('this in _init');
console.log(this);
} else {
alert('_workItem is null or undefined!');
console.log('this in _init');
console.log(this);
}
this._base();
this._control = $("<div style='width:100%;height:100%;background-color:lightblue;'><button type='submit'>CLICK ME!</button></div>").appendTo(this._container).bind("click", delegate(this, this._onClick));
},
invalidate: function(flushing) {
alert("invalidate(flushing) called!");
var value = this._getField().getValue();
debugger;
if (this._workItem) {
var originalEstimate = this._workItem.getField("Microsoft.VSTS.Scheduling.OriginalEstimate").getValue();
alert('OriginalEstimate value is ' + originalEstimate);
console.log('this in _init');
console.log(this);
} else {
alert('_workItem is null or undefined!');
console.log('this in _init');
console.log(this);
}
alert(value);
},
clear: function() {
alert("clear() called!");
},
_onClick: function() {
alert("Butona tıklandı!");
}

Related

Pass DataTable reference to the callback function on load

My current code is:
var CommissionLogs = $("#CommissionLogs").DataTable({
ajax: {
url: ajaxurl + '?action=pos&post_action=get_commissions'
},
'initComplete': function (settings, json){
//possible to access 'this'
this.api().columns(1);
}
});
I improved the code above as below with help :
var CommissionLogs = $("#CommissionLogs").DataTable({
ajax: {
url: ajaxurl + '?action=pos&post_action=get_commissions'
},
'initComplete': function(settings, json){
callbackFunction(settings);
}
});
function callbackFunction(settings){
var api = new $.fn.dataTable.Api( settings );
// api is accessible here.
}
Update :
Now I can access api from callback function. But I want use same callback with load() as below code.
CommissionLogs.ajax.url( newAjaxURL ).load( callbackFunction(), true);
But settings param is not accessible in load function.
I can clear and destroy datatable and re initialize always. But what will be the right way.
I think you need settings:
https://datatables.net/reference/type/DataTables.Settings
$('#example').dataTable( {
"initComplete": function(settings, json) {
myFunction(settings);
}
});
function myFunction(settings){
var api = new $.fn.dataTable.Api( settings );
// Output the data for the visible rows to the browser's console
// You might do something more useful with it!
console.log( api.rows( {page:'current'} ).data() );
}
Other option is re-use your var CommissionLogs variable throughout the code without using this, I recommend strongly this last option.
The dataTable.ajax.url().load() has not access to settings.
So can not call a callback function with settings.
But possible to use callback function without settings.
So here is an alternative way to use settings.
CommissionLogs.clear();// clear the table
CommissionLogs.destroy();// destroy the table
CommissionLogs = $("#CommissionLogs").DataTable({
ajax: {
url: newAjaxUrl
},
'initComplete': function (settings, json){
callbackDatatableFunciton(settings);
}
});

Vue.js Retrieving Remote Data for Options in Select2

I'm working on a project that is using Vue.js and Vue Router as the frontend javascript framework that will need to use a select box of users many places throughout the app. I would like to use select2 for the select box. To try to make my code the cleanest I can, I've implemented a custom filter to format the data the way select2 will accept it, and then I've implemented a custom directive similar to the one found on the Vue.js website.
When the app starts up, it queries the api for the list of users and then stores the list for later use. I can then reference the users list throughout the rest of the application and from any route without querying the backend again. I can successfully retrieve the list of users, pass it through the user list filter to format it the way that select2 wants, and then create a select2 with the list of users set as the options.
But this works only if the route that has the select2 is not the first page to load with the app. For example, if I got to the Home page (without any select2 list of users) and then go to the Users page (with a select2), it works great. But if I go directly to the Users page, the select2 will not have any options. I imagine this is because as Vue is loading up, it sends a GET request back to the server for the list of users and before it gets a response back, it will continues with its async execution and creates the select2 without any options, but then once the list of users comes back from the server, Vue doesn't know how to update the select2 with the list of options.
Here is my question: How can I retrieve the options from an AJAX call (which should be made only once for the entire app, no matter how many times a user select box is shown) and then load them into the select2 even if the one goes directly to the page with the select2 on it?
Thank you in advance! If you notice anything else I should be doing, please tell me as I would like this code to use best practices.
Here is what I have so far:
Simplified app.js
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
globals: {
users: {
data: []
},
}
};
},
methods: {
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
});
},
}
});
Sample response from API
{
"data": [
{
"id": 1,
"first_name": "John",
"last_name": "Smith",
"active": 1
},
{
"id": 2,
"first_name": "Emily",
"last_name": "Johnson",
"active": 1
}
]
}
User List Filter
Vue.filter('userList', function (users) {
if (users.length == 0) {
return [];
}
var userList = [
{
text : "Active Users",
children : [
// { id : 0, text : "Item One" }, // example
]
},
{
text : "Inactive Users",
children : []
}
];
$.each( users, function( key, user ) {
var option = { id : user.id, text : user.first_name + ' ' + user.last_name };
if (user.active == 1) {
userList[0].children.push(option);
}
else {
userList[1].children.push(option);
}
});
return userList;
});
Custom Select2 Directive (Similar to this)
Vue.directive('select', {
twoWay: true,
bind: function () {
},
update: function (value) {
var optionsData
// retrive the value of the options attribute
var optionsExpression = this.el.getAttribute('options')
if (optionsExpression) {
// if the value is present, evaluate the dynamic data
// using vm.$eval here so that it supports filters too
optionsData = this.vm.$eval(optionsExpression)
}
var self = this
var select2 = $(this.el)
.select2({
data: optionsData
})
.on('change', function () {
// sync the data to the vm on change.
// `self` is the directive instance
// `this` points to the <select> element
self.set(select2.val());
console.log('emitting "select2-change"');
self.vm.$emit('select2-change');
})
// sync vm data change to select2
$(this.el).val(value).trigger('change')
},
unbind: function () {
// don't forget to teardown listeners and stuff.
$(this.el).off().select2('destroy')
}
})
Sample Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.user_ids"
options="globals.users.data | userList"
>
</select>
I may have found something that works alright, although I'm not sure it's the best way to go about it. Here is my updated code:
Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.reporting_type_ids"
options="globals.types.data | typeList 'reporttoauthorities'"
class="select2-users"
>
</select>
Excerpt from app.js
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
this.$nextTick(function () {
var optionsData = this.$eval('globals.users.data | userList');
console.log('optionsData', optionsData);
$('.select2-users').select2({
data: optionsData
});
});
});
},
This way works for me, but it still kinda feels hackish. If anybody has any other advice on how to do this, I would greatly appreciate it!
Thanks but I'm working on company legacy project, due to low version of select2, I encountered this issue. And I am not sure about the v-select syntax is from vue standard or not(maybe from the vue-select libaray?). So here's my implementation based on yours. Using input tag instead of select tag, and v-model for v-select. It works like a charm, thanks again #bakerstreetsystems
<input type="text"
multiple="multiple"
style="width: 300px"
v-model="supplier_id"
options="suppliers"
id="select2-suppliers"
>
</input>
<script>
$('#app').ready(function() {
var app = new Vue({
el: "#app",
data: {
supplier_id: '<%= #supplier_id %>', // We are using server rendering(ruby on rails)
suppliers: [],
},
ready: function() {
this.fetchSuppliers();
},
methods: {
fetchSuppliers: function() {
var self = this;
$.ajax({
url: '/admin_sales/suppliers',
method: 'GET',
success: function(res) {
self.suppliers = res.data;
self.$nextTick(function () {
var optionsData = self.suppliers;
$('#select2-suppliers').select2({
placeholder: "Select a supplier",
allowClear: true,
data: optionsData,
});
});
}
});
},
},
});
})
</script>

getjson does not work on production server but works on development machine

I have following javascript that is using a selection changed to fill in a select list.
$(function () {
$("#bedrijvenauto").each(function () {
var target = $(this);
var dest = target.attr("data-autocomplete-destination");
target.autocomplete({
source: target.attr("data-autocomplete-source"),
select: function (event, ui) {
alert('selected bedrijf');
event.preventDefault();
target.val(ui.item.label);
$(dest).val(ui.item.value);
$("#projectenauto").val("");
alert('selected bedrijf');
alert($('#BEDRIJF_ID').val());
$.getJSON("/Project/GetListViaJson", { bedrijf: $('#BEDRIJF_ID').val() }, function (data) {
alert('selected bedrijf');
alert(data);
$("#PROJECT_ID").empty();
$("#PROJECT_ID").append(new Option("Maak een selectie", 0));
for (var i = 0; i < data.length; ++i) {
alert(data[i].value + ' ' + data[i].label);
$("#PROJECT_ID").append(new Option(data[i].label, data[i].value));
}
});
},
focus: function (event, ui) {
event.preventDefault();
target.val(ui.item.label);
}
});
target.val($("#BEDRIJF_NAAM").val());
});
It works like a charm on my development pc. The alert are all coming out even the data is returning results. That is the difference with the development pc that does not give any results after the call to getJSON
I have the feeling I am missing a detail here.
I am not used to debugging on a webserver because I usually create GUI applications in WPF, and this is a student's work for his vacation and I now got to get it working without him being around anymore. Vacation is done :-(
But not for me.
The 404 error indicated in your comments means the url your creating is incorrect. Always make use of the #Url.Action() method to ensure they are correctly generated. In your script
var url = '#Url.Action("GetListViaJson", "Project")';
$.getJSON(url, { bedrijf: $('#BEDRIJF_ID').val() }, function (data) {
....
}
or if this is an external script, then add the var url = '#Url.Action(...)'; in the main view (razor code is not evaluated in external script files), or add it as a data- attribute to the element your handling
data-url = "#Url.Action(...)"
and get it again using var url = $(someElement).data('url');

Can't get meteor FileCollection package to work

I am (unfortunately) working on a Windows machine so I had to manually add the FileCollection package to my app, but when I run my app, I can access the file collection and file collection methods from the browser console. However, I can't seem to get the event listeners set up on an actual page. (FYI, I am using iron-router for my templating architecture.)
It seems like the code that needs to be called is just not coming in the right order, but I've experimented with where I place the code and nothing seems to make a difference.
The server side code:
// Create a file collection, and enable file upload and download using HTTP
Images = new fileCollection('images',
{ resumable: true, // Enable built-in resumable.js upload support
http: [
{ method: 'get',
path: '/:_id', // this will be at route "/gridfs/images/:_id"
lookup: function (params, query) { // uses express style url params
return { _id: params._id }; // a mongo query mapping url to myFiles
}
}
]
}
);
if (Meteor.isServer) {
// Only publish files owned by this userId, and ignore
// file chunks being used by Resumable.js for current uploads
Meteor.publish('myImages',
function () {
return Images.find({ 'metadata._Resumable': { $exists: false },
'metadata.owner': this.userId });
}
);
// Allow rules for security. Should look familiar!
// Without these, no file writes would be allowed
Images.allow({
remove: function (userId, file) {
// Only owners can delete
if (userId !== file.metadata.owner) {
return false;
} else {
return true;
}
},
// Client file document updates are all denied, implement Methods for that
// This rule secures the HTTP REST interfaces' PUT/POST
update: function (userId, file, fields) {
// Only owners can upload file data
if (userId !== file.metadata.owner) {
return false;
} else {
return true;
}
},
insert: function (userId, file) {
// Assign the proper owner when a file is created
file.metadata = file.metadata || {};
file.metadata.owner = userId;
return true;
}
});
}
The client side code (currently in main.js at the top level of the client dir):
if (Meteor.isClient) {
// This assigns a file upload drop zone to some DOM node
Images.resumable.assignDrop($(".fileDrop"));
// This assigns a browse action to a DOM node
Images.resumable.assignBrowse($(".fileBrowse"));
// When a file is added via drag and drop
Images.resumable.on('fileAdded', function(file) {
// Create a new file in the file collection to upload
Images.insert({
_id : file.uniqueIdentifier, // This is the ID resumable will use
filename : file.fileName,
contentType : file.file.type
}, function(err, _id) {// Callback to .insert
if (err) {throwError('Image upload failed');}
// Once the file exists on the server, start uploading
Images.resumable.upload();
});
});
Images.resumable.on('fileSuccess', function(file) {
var userId = Meteor.userId();
var url = '/gridfs/images/' + file._id;
Meteor.users.update(userId, {
$set : {
"profile.$.imageURL" : url
}
});
Session.set('uploading', false);
});
Images.resumable.on('fileProgress', function(file) {
Session.set('uploading', true);
});
}
I think the issue might be with using IronRouter. I'll assume you are using some layout.html via Iron router and inside it you've added your template for your file table to be shown. (I'm guessing your are following the sampleApp that came with fileCollection.). I had a problem when I did this and it had to do with where I had the code that attached the listeners. The problem is where you have the code "Images.resumable.assignDrop($(".fileDrop"));" in your client file. The way you have it now, that line of code is running before your template is rendered within the layout.html. So the code can not find the DOM element ".fileDrop". To fix this create a layout.js file and use the rendered method like this...
Template.layout.rendered = function(){
Images.resumable.assignDrop($(".fileDrop"));
}

jplayer+Ajax inserted content

I am using jPlayer to play audio files.
If I use the player on content, which is privided, when the page gets loaded, it works without any problems.
I also need it for HTML which is inserted by AJAX. Here it does not work. It seems, that the ready event is not triggered.
I wrote a function, which can be executed by click(). In that way, I can click it manually, when the HTML which contains the player is fully loaded. Here I have the same problem: The ready event is not triggered.
This is my function which works on non ajax inserted players fine:
$('.jp-jplayer').each(function () {
var src = $(this).attr('data-src');
var id = $(this).attr('id');
var post_id = $(this).attr('data-id');
alert('beg');
$('#' + id).jPlayer({
ready: function () {
$(this).jPlayer('setMedia', {
mp3: "/prelisten/_lofidl/change_of_heart_full_lofi.mp3",
});
alert('#' + id);
},
swfPath: "/wp-content/themes/Dark_3Chemical_DE_mit_Pagenavi/Dark_3Chemical_DE/audioplayer/js",
//////ERRRROOOOOR
solution: "flash, html",
supplied: "mp3",
wmode: "window",
cssSelectorAncestor: "#jp_container_" + post_id,
play: function () { // To avoid both jPlayers playing together.
$(this).jPlayer("pauseOthers");
},
repeat: function (event) { // Override the default jPlayer repeat event handler
if(event.jPlayer.options.loop) {
$(this).unbind(".jPlayerRepeat").unbind(".jPlayerNext");
$(this).bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function () {
$(this).jPlayer("play");
debug($(this));
});
} else {
$(this).unbind(".jPlayerRepeat").unbind(".jPlayerNext");
$(this).bind($.jPlayer.event.ended + ".jPlayer.jPlayerNext", function () {
//$("#jquery_jplayer_4858").jPlayer("play", 0);
});
}
},
});
$("#jplayer_inspector").jPlayerInspector({
jPlayer: $('#' + id)
});
});
Currently I am setting the src manually to exclude any possible errors here.
How can I get this function running on AJAX inserted content?
EDIT:
This is the code, which fetches the html including the players:
$.get('/query_posts.php', {
paged: _page,
cats: cols
}, function(data) {
$('#search-results').append(data).fadeIn(300);
//create_player_scripts();
//set_players();
$('#search-results').find('input[name="cartLink"]').each(function() {
$(this).val($(this).closest('.post1').find('.post_headl a').attr('href'));
});
});
To make an AJAX page reload work I had to first destroy all jplayer instances. So I wrote a little function that grabs all instances of a jplayer on the site (by looking for jp-audio classes) and calls jplayer('destroy'); and jplayer('clearMedia'). This function gets called in the $.ajax({ beforeSend: destroyJplayerInstances(); })
UPDATE:
Here is a statement from the developer of jPlayer, Mark Panaghiston:
https://groups.google.com/forum/#!topic/jplayer/Q_aRhiyYvQo
Hope that helps!

Resources