Autocomplete with multiple value - autocomplete

I am using autocomplete which returns ID and Value, when i am selecting the item from autocomplete drop down the ID part, i am saving in hidden field, but suppose if item is there but i am not selecting from drop down simply i am writing whole text into autocomplte text box the ID is not saved into hidden field(it should not be) but i want that so please provide some hint to accomplish that.
function AutoComplete(id, url) {
$(item).focus().autocomplete(url, {
dataType: 'json',
parse: function (data) {
var rows = new Array();
for (var i = 0; i < data.length; i++) {
rows[i] = { data: data[i], value: data[i].ID, result: data[i].Name };
}
return rows;
},
formatItem: function (row, i, n) {
return row.Name;
}
}).result(function (evt, data, formatted) {
$(hiddenfield).val(data.ID)
});
}

Try this demo here with Autcomplete with multiple selection.
[http://jsfiddle.net/jcxbgomd/][1]

Related

Word Addin is getting crashed when trying to delete a row which contains a content control in a table

I'm developing a Word Add-in (Word API + Office.js) where i am working with content controls, I am trying to read the table content inside a content control where I need remove the empty rows
Sample: I have this table inside a content control I have to remove the blank rows
i am able to achieve this functionality with this code, but if the table contains a content control which is blank then when i try to delete that row the addin itself is getting crashed.
function checktable(element) {
Word.run(function (context) {
// Queue a command to get the current selection and then
// create a proxy range object with the results.
var contentControl = context.document.contentControls.getByTag('control1').getFirst();
var table = contentControl.tables.getFirst();
context.load(contentControl, 'tables');
table.load('values');
return context.sync()
.then(function () {
// Get the longest word from the selection.
if (contentControl.tables.items.length === 0) {
document.getElementById('lblstatus').innerText += "No Tables found";
}
else {
document.getElementById('lblstatus').innerText += " Tables found";
var Tablevaules = table.values;
for (var i = 0, len = Tablevaules.length; i < len; i++)
{
var nullcheck = "";
var inner=Tablevaules[i];
// inner loop applies to sub-arrays
for (var j = 0, len2 = inner.length; j < len2; j++) {
// accesses each element of each sub-array in turn
if (inner[j] == "") {
if (nullcheck != "False") {
nullcheck = "True";
}
}
else {
nullcheck = "False";
}
}
if (nullcheck == "True") {
table.deleteRows(i);
}
}
}
})
.then(context.sync)
.then(function () {
// Queue a command to highlight the search results.
document.getElementById('lblstatus').innerText += element + ":" + "Successs";
});
})
.catch(errorHandler);
}
Please let me know whether i am missing something or its a known bug!!

Find and Replace Header/Footer text Office JS

I'm having trouble with office js and processing a list of items with lookup codes and replacement values for the header and footer. I've got the body working just not the header/footer. I'm getting this error:0x800a139e - JavaScript runtime error: The property 'items' is not available. Before reading the property's value, call the load method on the containing object and call "context.sync()" on the associated request context. As you can see I do call load and sync before trying to access the results.
function mergeHeader(documentFieldKeys) {
if (documentFieldKeys.length > 0)
Word.run(function(context) {
var key = documentFieldKeys.shift();
var mySections = context.document.sections;
context.load(mySections, 'body/style');
return context.sync().then(function() {
for (var i = 0; i < mySections.items.length; i++ ) {
findAndReplace(key, context, mySections.items[i].getHeader("primary"));
}
return context.sync().then(function() {
return mergeHeader(documentFieldKeys);
})
.then(context.sync);
});
});
}
function findAndReplace(key, context, body) {
var results = body.search(key.Code, { matchWholeWord: false, matchCase: false });
context.load(results);
return context.sync().then(function() {
if (results.items.length > 0 && key.Value === "") {
missingFields.push(key.Description);
} else {
for (var i = 0; i < results.items.length; i++) {
results.items[i].insertText(key.Value, "replace");
}
}
})
.then(context.sync);
}
Any help would be appreciated.
Add
context.load(mySections, 'items');
or
mySections.load('items');

How can a range be used across different Word.run contexts?

I have created a taskpane addin for word that runs a search and displays information about the results as a list to the user.
When the user clicks on an item in the list I want to select the range in word to show the user the location of the item.
The addin will then allow the user to perform additional tasks on the range, for example change the font colour.
I am able to run the search and get ranges for display using the function below:
function runSearch(textToFind) {
var items = [];
return Word.run(function(context) {
var options = Word.SearchOptions.newObject(context);
options.matchWildCards = false;
var rangesFind = context.document.body.search(textToFind, options);
context.load(rangesFind, 'text, font, style');
return context.sync().then(function() {
for (var i = 0; i < rangesFind.items.length; i++) {
items.push(rangesFind.items[i]);
context.trackedObjects.add(rangesFind.items[i]);
}
return context.sync();
});
})
.then(function() {
return items;
});
};
However I am having difficulty selecting the range on user click.
I have tried using the ranges context:
function selectRange(range){
range.select();
return range.context.sync();
}
Or using the range in a new Word.run context:
function selectRange(range){
return Word.run(function(context) {
context.load(range);
return context.sync().then(function(){
range.select();
return context.sync();
});
});
}
I have come across a potential method that involves creating a content control for each search result and then reloading all the content controls in the selectRangefunction in the new context and finding the matching control, but that seems very inefficient when I have the range already.
What is the best method for reusing a range across different Word.run contexts?
You cannot use an object across Word.run invocations. Word.run creates a new context every time that it's invoked, whereas the original object is tied to its own context, creating a mismatch.
That being said, you absolutely can, from within a Word.run, add the objects you desire to context.trackedObjects.add(obj), and they will remain as working objects even after Word.run finishes executing. By "working objects" I mean that their path will not get invalidated (think something similar to garbage collection, but for remote objects).
Once you have such object (and it looks above like you do), you should be able to call
range.select();
range.context.sync().catch(...);
If it's not working for you, can you provide an example of the error you're getting?
For completeness sake, I should note that once you add objects to the trackedObjects collection, you're effectively taking memory management of those objects into your own hands. This means that if you don't properly release the memory, you will be slowing down Word by bogging down its memory / range-adjustment chain. So once you're done using the tracked object(s), you should call obj.context.trackedObjects.remove(obj), followed by obj.context.sync(). Don't forget the last part - if you don't do a sync, your request to remove the tracked objects will not be dispatched, and you'll continue to use up the memory.
======= Update 1 =======
Tom, thanks for providing the error message. It looks like this might be a bug in the Word implementation of the APIs -- I'll follow up on that, and someone might reach out to you if there's more questions.
From a conceptual standpoint, you are absolutely on the right path -- and the following does work in Excel, for example:
var range;
Excel.run(function (ctx) {
var sheet = ctx.workbook.worksheets.getActiveWorksheet();
range = sheet.getRange("A5");
range.values = [[5]];
ctx.trackedObjects.add(range);
return ctx.sync();
})
.then(function(){
setTimeout(function() {
range.select();
range.context.trackedObjects.remove(range);
range.context.sync();
}, 2000);
})
.catch(function (error) {
showMessage("Error: " + error);
});
======= Update 2 =======
It turns out there is indeed a bug in the product. However, the good news is that it's easy to fix with a JavaScript-only fix, and in fact we'll do so in the next couple of weeks, updating the CDN.
With the fix, the following code works:
var paragraph;
Word.run(function (ctx) {
var p = ctx.document.body.paragraphs.first;
paragraph = p.next;
ctx.trackedObjects.add(paragraph);
return ctx.sync();
})
.then(function(){
setTimeout(function() {
paragraph.select();
paragraph.context.trackedObjects.remove(paragraph);
paragraph.context.sync()
.then(function() {
console.log("Done");
})
.catch(handleError);
}, 2000);
})
.catch(handleError);
function handleError (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
}
Want even better news? Until the CDN is updated, you can use the code below to "patch" the JavaScript library and make the code above run. You should run this code some time after Office.js has already loaded (i.e., within your Office.initialize function), and before you do a Word.run.
var TrackedObjects = (function () {
function TrackedObjects(context) {
this._autoCleanupList = {};
this.m_context = context;
}
TrackedObjects.prototype.add = function (param) {
var _this = this;
if (Array.isArray(param)) {
param.forEach(function (item) { return _this._addCommon(item, true); });
}
else {
this._addCommon(param, true);
}
};
TrackedObjects.prototype._autoAdd = function (object) {
this._addCommon(object, false);
this._autoCleanupList[object._objectPath.objectPathInfo.Id] = object;
};
TrackedObjects.prototype._addCommon = function (object, isExplicitlyAdded) {
if (object[OfficeExtension.Constants.isTracked]) {
if (isExplicitlyAdded && this.m_context._autoCleanup) {
delete this._autoCleanupList[object._objectPath.objectPathInfo.Id];
}
return;
}
var referenceId = object[OfficeExtension.Constants.referenceId];
if (OfficeExtension.Utility.isNullOrEmptyString(referenceId) && object._KeepReference) {
object._KeepReference();
OfficeExtension.ActionFactory.createInstantiateAction(this.m_context, object);
if (isExplicitlyAdded && this.m_context._autoCleanup) {
delete this._autoCleanupList[object._objectPath.objectPathInfo.Id];
}
object[OfficeExtension.Constants.isTracked] = true;
}
};
TrackedObjects.prototype.remove = function (param) {
var _this = this;
if (Array.isArray(param)) {
param.forEach(function (item) { return _this._removeCommon(item); });
}
else {
this._removeCommon(param);
}
};
TrackedObjects.prototype._removeCommon = function (object) {
var referenceId = object[OfficeExtension.Constants.referenceId];
if (!OfficeExtension.Utility.isNullOrEmptyString(referenceId)) {
var rootObject = this.m_context._rootObject;
if (rootObject._RemoveReference) {
rootObject._RemoveReference(referenceId);
}
delete object[OfficeExtension.Constants.isTracked];
}
};
TrackedObjects.prototype._retrieveAndClearAutoCleanupList = function () {
var list = this._autoCleanupList;
this._autoCleanupList = {};
return list;
};
return TrackedObjects;
}());
OfficeExtension.TrackedObjects = TrackedObjects;
Hope this helps!
~ Michael Zlatkovsky, developer on Office Extensibility team, MSFT
In addition to the TrackedObjects fix the runSearch method needed updating to get the range of the searchResult rather than using the searchResult directly.
function runSearch(textToFind) {
var items = [];
return Word.run(function(context) {
var options = Word.SearchOptions.newObject(context);
options.matchWildCards = false;
var rangesFind = context.document.body.search(textToFind, options);
context.load(rangesFind);
return context.sync().then(function() {
for (var i = 0; i < rangesFind.items.length; i++) {
var range = rangesFind.items[i].getRange();
context.load(range, 'text');
items.push(range);
context.trackedObjects.add(items[items.length-1]);
}
return context.sync();
});
})
.then(function() {
return items;
});
};

How to check if text is found in column in Protractor

I'm trying to assert that a name is displayed in a column of a table. I've written an inResults function that will iterate through a column's text to see if a name exists. Here's what I'm trying:
Page object:
this.names = element.all(by.repeater('row in rows').column('{{row}}'));
this.inResults = function(nameString) {
var foundit = '';
this.names.each(function(name) {
name.getText().then(function(it) {
console.log(it); // each name IS printed...
if(it == nameString) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
});
});
return foundit; // returns '' but should be true?
};
Spec expect:
expect(friendPage.inResults('Jo')).toBeTruthy();
Both console statements print as expected... but my expect fails as foundit's value is still ''. I've tried this a number of ways and none are working. What am I missing?
I've devised what I think is a better/cleaner way to solve this. It's less complex and doesn't require locator/css code in the method.
friend.page.js
// locator
this.friendName = function(text) { return element.all(by.cssContainingText('td.ng-binding', text)) };
// method
this.inResults = function(name) {
return this.friendName(name).then(function(found) {
return found.length > 0;
});
};
friend.spec.js
expect(friendPage.inResults('Jo')).toBeTruthy();
I've added this to my protractor_example project on GitHub...
I would recommend you to use filter: http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.filter
this.inResults = function(nameString) {
return this.names.filter(function(name) {
return name.getText().then(function(text) {
return text === nameString;
});
}).then(function(filteredElements) {
// Only the elements that passed the filter will be here. This is an array.
return filteredElements.length > 0;
});
});
// This will be a promise that resolves to a boolean.
expect(friendPage.inResults('Jo')).toBe(true);
Use map to do this.This will return a deferred that will resolve with the values in an array, so if you have this:
this.mappedVals =element.all(by.repeater('row in rows').column('{{row}}')).map(function (elm) {
return elm.getText();
});
It will resolve like this:
this.inResults = function(nameString) {
var foundit = '';
mappedVals.then(function (textArr) {
// textArr will be an actual JS array of the text from each node in your repeater
for(var i=0; i<textArr.length; i++){
if(it == textArr[i]) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
}
return foundit;
});
}
And Use that in Spec file like,
friendPage.inResults('Jo').then(function(findIt){
expect(findIt).toBeTruthy();
});

How to add Handler on Cell in Google Visualization Table in GWT?

Google Visualization API for GWT provides control over rows only.
How to get control over a particular cell in Visualization Table?
selection.isCell() doesn't give true result in any case.
private SelectHandler createSelectHandler(final PieChart chart) {
return new SelectHandler() {
#Override
public void onSelect(SelectEvent event) {
String message = "";
// May be multiple selections.
JsArray<Selection> selections = chart.getSelections();
for (int i = 0; i < selections.length(); i++) {
// add a new line for each selection
message += i == 0 ? "" : "\n";
Selection selection = selections.get(i);
if (selection.isCell()) {
// isCell() returns true if a cell has been selected.
// getRow() returns the row number of the selected cell.
int row = selection.getRow();
// getColumn() returns the column number of the selected cell.
int column = selection.getColumn();
message += "cell " + row + ":" + column + " selected";
} else if (selection.isRow()) {
// isRow() returns true if an entire row has been selected.
// getRow() returns the row number of the selected row.
int row = selection.getRow();
message += "row " + row + " selected";
} else {
// unreachable
message += "Pie chart selections should be either row selections or cell selections.";
message += " Other visualizations support column selections as well.";
}
}
Window.alert(message);
}
};
}
Google Table has 4 events: ready,select,page,sort.
When you sort or paginate, it stops listening the ready event.
To have the cell click working after pagination or sort you need to add the click listener on all of them.
You can use click instead of mouseover.
On select event I use getSelection to be able to get and set the selected row properties.
var colIndex;
google.visualization.events.addListener(table, 'ready', function () {
$("#table").find("td").each(function() {
$(this).mouseover(function(){
colIndex=$(this).index();
});
});
});
google.visualization.events.addListener(table, 'sort', function () {
$("#table").find("td").each(function() {
$(this).mouseover(function(){
colIndex=$(this).index();
});
});
});
google.visualization.events.addListener(table, 'page', function (event) {
$("#tableGoogle").find("td").each(function() {
$(this).mouseover(function(){
colIndex=$(this).index();
});
});
});
google.visualization.events.addListener(table, 'select', function () {
var selection = table.getSelection();
var item;
if(selection.length!=0){
lastSelection=selection;
}
for (var i = 0; i < lastSelection.length; i++) {
item = lastSelection[i];
}
switch (colIndex){
case 0:
data.setValue(item.row,index,true);
// YOUR CODE FOR COLUMN 0
break;
case 1:
var id=data.getRowProperty(item.row, 'id');
// YOUR CODE FOR COLUMN 1
break;
}
});
The Table Visualization does not pass column information in the selection event, so you cannot identify an individual cell this way. You will need to register a click event handler on the cells of the table and then determine the cell's row and column indices. Here's one way to do it using jQuery:
google.visualization.events.addListener(table, 'ready', function () {
$('#table_div td').click(function () {
var column = $(this).index();
var row = $(this).parent().index() - 1; // subtract 1 for the table header
console.log(row, column);
});
});
You'll have to adapt the event handler to the method used in the GWT Viz API package, but the jQuery code should work.
var rowIndex;
var colIndex;
google.visualization.events.addListener(table, 'ready', function () {
jQuery("#table").on("click", "td:not(.google-visualization-table-th)", function() {
colIndex = jQuery(this).index();
rowIndex = jQuery(this).parent().index() - 1;
alert("row "+rowIndex+" col "+colIndex);
//put rest of function here
});
This gets rowindex based on the row of the html. To get the rowindex based on the data (where the row's index won't change even if you sort the table and it's position changes) use
google.visualization.events.addListener(table, 'select', function() {
var selected=table.getChart().getSelection();
var item=selected[0];
rowIndex=item.row;
});
This will run before the code in the .on("click", ...) function in the ready function.