How to use ranges saved to React state - Microsoft Word javascript API? - reactjs

I am using the Microsoft Word Javascript API. I have used the .search() function to retrieve an array of ranges and then have saved them to state.definitions in my App.js React component state. This part works. When I try to print out the state using console.log(JSON.stringify(this.state.definitions)), I see the ranges that I just saved.
In a separate function, I want to retrieve those ranges and highlight them in a new color. This part does not work. I don't get any errors, but I don't see any highlight changes in the document.
Interestingly, if I try to highlight the ranges BEFORE saving them to state, it works. This makes me think that the ranges that I am retrieving from state are not actually the ranges understood by Word.
Any help would be much appreciated.
var flattenedTerms contains an array of range items that were retrieved from Word a few lines above. This code successfully changes the font
for (var i = 0; i < flattenedTerms.length; i++) {
console.log('flattenedTerms: ', flattenedTerms[i]);
flattenedTerms[i].font.color = 'purple';
flattenedTerms[i].font.highlightColor = 'pink';
flattenedTerms[i].font.bold = true;
}
return context.sync().then(function () {
return resolve(flattenedTerms);
})
})
Now the flattenedTerms array, which contains the range items, has been saved to state.definitions using this.setState. This fails to change the font. All of the console.logs do print.
highlightDefinedTerms = () => {
var self = this;
return Word.run(
function (context) {
var definitions = self.state.definitions;
console.log('Highlighting ', definitions.length, ' terms.');
for (var i = 0; i < definitions.length; i++) {
console.log('Highlighting definition: ', JSON.stringify(definitions[i]));
definitions[i].font.color = 'blue';
definitions[i].font.highlightColor = 'red';
definitions[i].font.bold = true;
}
return context.sync();
}
)
}

You need to pass a first parameter to “Word.run” to specify the object whose context you want to resume.
Word.run(self.state.definitions, function(context) ...)

Related

Highlight text as you type in textarea Reactjs

I need to perform a behavior in FrontEnd but I don't know how to do it: Inside the textarea I have to put a background on certain keywords like "+project", "#context", while the user types, as if it were a markup text similar to testing tools for Regex.
Its not the complete solution, but you can adapt this example:
https://jsfiddle.net/julmot/hdyLpy37/
It uses the markjs library:
https://markjs.io/
Here is the javascript code:
// Create an instance of mark.js and pass an argument containing
// the DOM object of the context (where to search for matches)
var markInstance = new Mark(document.querySelector(".context"));
// Cache DOM elements
var keywordInput = document.querySelector("input[name='keyword']");
var optionInputs = document.querySelectorAll("input[name='opt[]']");
function performMark() {
// Read the keyword
var keyword = keywordInput.value;
// Determine selected options
var options = {};
[].forEach.call(optionInputs, function(opt) {
options[opt.value] = opt.checked;
});
// Remove previous marked elements and mark
// the new keyword inside the context
markInstance.unmark({
done: function(){
markInstance.mark(keyword, options);
}
});
};
// Listen to input and option changes
keywordInput.addEventListener("input", performMark);
for (var i = 0; i < optionInputs.length; i++) {
optionInputs[i].addEventListener("change", performMark);
}

React Push One State Array into Another

In essence, I have this working, I am building out a game component to allow users to select items they want to purchase, they get pushed into a pending array and then when they wish to check out I am pushing those objects from the pending array into the purchased array.
But something very odd is happening after that, the arrays look like it worked properly the pending array is empty and the purchased array has the correct items within it. The problem comes once I try and click a new item to put into the pending array if its the same item that is in the purchased array it alters the counter there.
I have been toying with different ways to try and fix this, but have no idea how it's affecting the purchased array without me setting the state again.
This is my code so far for operating this vendor functionality.
handleMerchantEquipment(item) {
let pending = this.state.merchantPending;
let found = Equipment.find(el => item === el.Name);
let check = pending.find(el => el.Name === found.Name);
if (!check) {
pending.push(found);
}
else {
var foundIndex = pending.findIndex(el => el.Name === check.Name);
check.Count +=1;
pending[foundIndex] = check;
}
let CP = [0];
let SP = [0];
let GP = [0];
let PP = [0];
pending.map(item => {
let cost = item.Cost * item.Count;
if (item.Currency === "CP") {
CP.push(cost);
}
else if (item.Currency === "SP") {
SP.push(cost);
}
else if (item.Currency === "GP") {
GP.push(cost);
}
else if (item.Currency === "PP") {
PP.push(cost);
}
});
let purchased = [];
this.state.merchantPending.map(item => {
purchased.push(item)
});
this.setState({
yourCP: totalCP,
yourSP: totalSP,
yourEP: totalEP,
yourGP: totalGP,
yourPP: totalPP,
purchased: purchased,
merchantPending: [],
});
Some of the code is related to the currency exchange which his working fine.
I have also tried the ... to update the array but the issue persisted with it updating the Count in the purchased state.
You are mutating state. You should not mutate state in React. You should always immutably change state in React.
I think the problem lies in this line.
let pending = this.state.merchantPending;
Change it to
let pending = [...this.state.merchantPending.map(obj => ({...obj}))];
Also, if you want to iterate an array, use forEach instead of map. map returns you a new array.
pending.forEach(item => {
...
}
What is that you want the purchasedArray to do when it encounters a duplicate entry or item? May you clarify that in your question?
If you want it to not allow duplicate items you have to first check the array to see if it contains that item. Pseudo code
if(contains) {
render alert
//or do something else
} else {
purchased.push(item)
}
I am not able to comment so I will delete this when you clarify.

React change state in array (for loop)

I have a State with flights, and I have a slider to change the max price to change visibility of the flight elements.
maxpriceFilter() {
var flightOffer = this.state.flightOffer;
var sliderPrice = this.state.sliderPrice;
for (var i = 0; i < flightOffer.length; i++) {
if( flightOffer[i].price > sliderPrice) {
this.setState(
{[flightOffer[i].hiddenprice] : true}
);
};
}
This code is adding a undefined field with status true in the root of the state though.. I cant find any best practice on this, other then using computed fields. But I cant get the computed field working either..
Could someone please help me out here?
You don't want to do a setState call in a loop, that will have the react component render multiple times. Build a new state object and call the setState once. You also don't want to filter it out by an if statement, but set previous values to hidden:
maxpriceFilter() {
var flightOffer = this.state.flightOffer;
var sliderPrice = this.state.sliderPrice;
var newState = {};
for (var i = 0; i < flightOffer.length; i++) {
newState[flightOffer[i].hiddenprice] = flightOffer[i].price > sliderPrice;
}
this.setState(newState);
// ...
}
If you're still having issues, it could be that the hiddenprice property isn't what you expect? Might need to post your render() function as well.
Instead of doing your looping when you're updating the state, why not just do the switch on render?
You're probably already looping over all flightOffers in render(). So, just add the check there. Inside your render, pass hiddenPrice={offer.price > sliderPrice} as a prop, or use it directly where you control the visibility.
Why? Because the visibility of a specific item in this case is not state. It is a result of the state.

How to check if value already exists?

I have a small app that users can use to search for a movie, and then add it to their watchlist. Currently it is possible to add 1 movie multple times for the same user. Which ofcourse isn't expected behaviour.
My solution would be to find the unique id of the movie that's being added and crosscheck that with my movies_users data. If the movie_id value exists, do this, else do this.
At the moment I do have the unique movie id of the movie that's being added,
$scope.movieListID = response;
console.log ($scope.movieListID.id)
Which gets ouputted like a string, like so,
314365
And I got the movie records from the current user,
$scope.moviesCheck = response.data;
console.log ($scope.moviesCheck)
Which looks like this,
[{"id":2,"title":"Black Panther", "movie_id":"284054"}]
So what would be a good way to check if the result from $scope.movieListID.id already exists in the $scope.moviesCheck data?
* update *
Trying a suggestion below does not give the expected result.
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
console.log (movie.movie_id)
console.log (id)
if (movie.movie_id === id)
console.log ('duplicate')
else
console.log ('not duplicate')
});
}
exists($scope.movieListID.id);
The console.log output from this is,
312221
312221
not duplicate
Which clearly are duplicate results.
You can add a function in your controller to check if the movie exists in the list
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
if (movie.id === id)
return true;
});
return false;
}
// and call it
exists($scope.movieListID.id); // true or false
I'm not 100% if this is a good way to do this, but for me it works and I think it's pretty low on performance,
movieService.loadMovies().then(function(response) {
$scope.moviesCheck = response.data;
var arr = $scope.moviesCheck
function myIndexOf(o) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].movie_id == o.exisitingMovie_id) {
return i;
}
}
return -1;
}
var checkDuplicate = (myIndexOf({exisitingMovie_id:movie.id}));
if (checkDuplicate == -1) {
From your question I've understood that, based on the object exists using id in the array of object, you have to do different action.
You can use $filter for this. Inject the filter for your controller and assign it to the scope. So this will be available whenever you want in this controller.
$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
$sope.movies - is the list of movies passed to the filter. You can
send any list based on your need.
{movie_id:$scope.existingMovie.movie_id} - This one is the object
which one we need to find. This can be based on your need. Since we
are searching movie_id, we need to send the object with property
and value. {movie_id:$scope.existingMovie.movie_id}, Here movie_id is
the property and followed by the value with the colon.
true: This indicates that, to search exact matched values. By default
this is false. If this is set to false, then if we want to search 54
in the movie id, this will returns the objects whichever contains 54
as part of the value.
app.controller('controller', ['$filter',function($filter){
$scope.cFilter= $filter;
$scope.Exists=function(){
$scope.movies=[{"id":2,"title":"Black Panther", "movie_id":"284054"},{"id":3,"title":"Black Panther", "movie_id":"32343"},{"id":4,"title":"Black Panther", "movie_id":"98863"}]
$scope.existingMovie={"id":3,"title":"Black Panther", "movie_id":"32343"};
var _obj=$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
if(_obj && _obj[0])
{
Console.log('object exists')
}
else
{
Console.log('Object is not found')
}
}
}])
Many Thanks Jeeva Jsb. This got me on the right track, however I thought I would clarify with a practical example that seems to work as expected.
So I have a function called getData which get the AJAX array but we need to check if the record exist before added to scope (else we get duplicates)
if (d.data.length) {
for (var i = 0; i < d.data.length; i++) {
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
if (doesExist.length == 0){
$scope.notifications.push(d.data[i]);
}
}
}
This should look familier...
when we are iterating through the returned AJAX object we need to check the ID of the (in my case notificiation)
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
This line creates a new array by filtering the existing array in scope ($scope.notifications) and passing in the same value from you interation.
If the value exists the object will be copied to the new array called doesExist.
A simple check of length will determine if the record needs to be written.
I hope this helps someone.

Sharing data between functions in angularjs

I am trying to implement two functions in an an angular app but as soon as I implement the filter (start with letters from to), the code stops working. On their own, the (add/delete) functions work but as soon as I turn the data into a factory and try to access with the filter functions it fails.
Working functions:
$scope.items = items;
$scope.deleteItem = function (index) {
items.data.splice(index, 1);
}
$scope.addItem = function (index) {
items.data.push({
Name: $scope.newItemName
});
}
What causes the whole thing to break:
//filtering letters _ NOT WORKING
function setLetters (from, to){
this.fromLetter = from;
this.toLetter = to;
}
//----
$scope.filter.startsWithLetter = function () {
return function (items, fromLetter, toLetter) {
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
var firstLetter = item.Name.substring(0, 1).toLowerCase();
if ((!fromLetter || firstLetter >= fromLetter)
&& (!toLetter || firstLetter <= toLetter)) {
filtered.push(item);
}
}
return filtered;
};
});
//--filtering letters
Full code here: fiddle
There's a few issues in the fiddle. First I'm seeing an "Unexpected token )" error due to an extra ) on line 58.
Then when I fix that there is an issue on line 45 as you are trying to assign a value to $scope.filter.startsWithLetter, when $scope.filter is undefined. I think you want to assign the value to $scope.startsWithLetter.
There is still a problem with the filtering. When filtering with ng-repeat you can specify a filter or simply a predicate function. In each case the arguments passed to the function will be different - please read the docs. The function as-is is designed to be used in a filter created with angular.module('myApp', []).filter(). It doesn't work when you set it on the scope and pass it to filter: as a predicate function. If you prefer to filter using a function on the scope, rather than creating a reusable custom filter, you need to change it to accept the correct arguments - see fiddle.
Your page is trying to access setLetters in $scope.items.data but you are not setting $scope.items.data.setLetters. I don't think it makes sense to set it there inside items.data anyway. Perhaps set it directly on the scope? I also would set fromLetter and toLetter directly on the scope.
I also moved the setLetter buttons inside a <div ng-controller="ItemsController" >
Fiddle with those fixes

Resources