How To Handle 'No Results Found' in a Search Feature - javascript

I am trying to display "No Results Found" in the case where someone uses my search feature to search for something that returns no results from the database. The problem I'm running into is that "No Results Found" prints to the screen right away - then disappear when a search query is actually being evaluated - and then reappears if no results were found. In other words, it's working as it should EXCEPT that it should wait till a query is actually triggered and evaluated before printing "No Results Found" to the screen. Conceptually what's the best way to do this? On the one hand it occurs to me that I could just use a timeout. But that's not really solving the problem directly. So what would be the best way to approach this? This is the code I have for the function:
public get noResultsFound(): boolean
{
if (this.query && !this._isSearching && !this.hasResults) {
return true;
}
}
Here's my view code:
<div *ngIf="inputHasFocus && noResultsFound" class="no-results-found">No Results Found</div>

Promises sound like what you need :D
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Something along the lines of this.
search = (paramObj) : Promise<SomeDataClass> => {
// Some search logic
}
requestSearch() {
this.search(null).then((result) => {
if (result.length === 0)
{
//logic for showing 0 results here
}
else{
// Show result?
}
})
}
A more complete example would look a bit like this.
class example {
public someBool = false;
public search = (paramObj): Promise<Array<string>> => {
this.someBool = !this.someBool;
return new Promise<Array<string>>((resolver) => {
// This just simulates something taking time.
setTimeout(() => {
if (this.someBool) {
resolver([]);
} else {
resolver(["hi","there"]);
}
}, 1000)
});
}
public requestSearch() {
this.search(null).then((result) => {
if (result.length === 0) {
console.log("empty")
}
else {
console.log(result)
}
})
}
}

So, in the end I was able to resolve this not by using some kind of debounce timeout, but by changing what the function was paying attention to - so to speak. By handling it this way the "No Results Found" never triggers before it's supposed to. This is what I ended up using:
public get noResultsFound(): boolean
{
if (!Object.isNullOrUndefined(this._results) && this._results.length < 1) {
return true;
}
}

Related

Break For Each Loop in a Custom Cypress Command

I have a custom Cypress command to scroll through a list until you reach the item passed into the command. In my command I have $.each so I can compare the name of the item to the item name passed into the function. If they match then I send a click command which is "ENTER" in my environment.
I am able to successful scroll through the list and find the the item I am looking for and click on it but the loop continues to execute. I added return false which is what Cypress says should break the loop but it is now working for me. Any ideas why that is the case?
listItems.getChildren('OneLineTextElement', 'type').each(($elm) => {
let labelAndValue = cy.wrap($elm).getChildren('LabelAndValue')
let label = labelAndValue.getChild('Label')
label.getProperty('texture-text').then(val => {
if (val == subject) {
cy.action('ENTER')
return false
}
else {
cy.action('DOWN')
}
})
})
You can try adding a control variable in the the scope of the .each() command.
listItems.getChildren('OneLineTextElement', 'type').each(($elm) => {
let done = false;
let labelAndValue = cy.wrap($elm).getChildren('LabelAndValue')
let label = labelAndValue.getChild('Label')
label.getProperty('texture-text').then(val => {
if (val == subject) {
cy.action('ENTER').then(() => { // is cy.action a custom command?
// Likely you may need to wait
done = true;
})
} else {
cy.action('DOWN')
}
})
if (done) {
return false;
}
})
However there are some method calls that look like custom commands inside .each() so you may not get the flow of execution you expect (Cypress commands and test javascript can run asynchronously).
It looks like the code may be refactored to avoid "scrolling through the list". The only thing this does not do is cy.action('DOWN') on the non-subject list items.
listItems.getChildren('OneLineTextElement', 'type')
.getChildren('LabelAndValue')
.getChild('Label')
.getProperty('texture-text')
.should(val => {
expect(val).to.equal(subject);
cy.action('ENTER');
})
You have to use promise after finding your match within the loop, and then apply the assertion. Below an example searching value '23' within the column 8 which corresponds to age field.
cy.get('.column-8')
.each(ele => {
if (ele.text() === '23') {
isValuePresent = true
if (isValuePresent) return false
}
}).then(() => {
expect(isValuePresent).to.be.true
})

How to break for loop inside Cypress's then() function?

I want to break for loop if .contacts element's style doen't equal display: none, but I can't find a way to do this inside then() fuction.
Use case: I want to click through table elements until contacts panel appears and then fill contacts. But if I don't stop the loop after panel appears it will disappear and I will get an error.
cy.get('#count').then($count => {
for (let i = 1; i <= $count - 1; i++) {
if (i == 1) {
cy.get(`:nth-child(${i}) > .room-type`).eq(1).click()
}
else {
cy.get(`:nth-child(${i}) > .room-type`).click()
}
cy.get('.spinner').should('not.exist')
cy.get('.contacts').then($contacts => {
if ($contacts.attr('style') != 'display: none;') {
//I want to break the loop here if condition is met
}
})
}
})
I believe the best way to go about about is the recursive approach. Using recursive approach your problem of synchronous and Asynchronous code will be resolved automatically.
So you can approach this problem with something like this below. The below code might not work as it is, you may have to do some changes accordingly. The main Idea is try going recursive approach if you want to avoid the sync-async code problem
cy.get("#count").then(($count) => {
test_recusive($count);
});
export const test_recusive = (count) => {
if (i == 1) {
cy.get(`:nth-child(${i}) > .room-type`).eq(1).click();
} else {
cy.get(`:nth-child(${i}) > .room-type`).click();
}
cy.get(".spinner").should("not.exist");
cy.get(".contacts").then(($contacts) => {
if ($contacts.attr("style") != "display: none;") {
//I want to break the loop here if condition is met
return;
} else {
return test_recusive(count - 1);
}
});
Now all the calls in your code will be synchronous ones.

Refreshing a Firebase object manually in ReactJS

I have a small app where a user can input a persons name and add them to a board and then users can up or down vote them (for fantasy football). Currently, the only thing that happens immediately without need for refreshing the page is when a player is added via the text input box. This does a little fade-in for the object and it comes right to the screen:
addPlayer(player) {
this.state.user ?
this.database.push().set({ playerContent: player, votes: 0})
:
console.log("Not Logged In")
}
Is this done in real time because it is a push? How could I have the board of players update after a user casts a vote? Right now, when say Jane Doe has two votes, and John Doe has 2 votes and you vote John Doe up to 3, he doesn't jump up ahead of Jane Doe until after you refresh the page. Is there a way to refresh each individual object as you cast a vote? Here's my voting code
upvotePlayer(playerId) {
if(this.state.user) {
let ref = firebase.database().ref('/players/' + playerId + '/voters');
ref.once('value', snap => {
var value = snap.val()
if (value !== null) {
ref.child(this.uid).once('value', snap => {
if (snap.val() === 0 || snap.val() === -1 || snap.val() == null){
ref.child(this.uid).set(1);
} else if (snap.val() === 1) {
ref.child(this.uid).set(0);
}
else {
console.log("Error in upvoting. snap.val(): " + snap.val())
}
})
} else {
console.log("Doesn't exist")
ref.child(this.uid).set(1);
}
});
}
else {
console.log("Must be logged in to vote.")
}
}
If I'm missing relevant code please let me know and I'll supply it. I tried calling componentDidMount after the vote is cast and this.forceUpdate() (even if it is discouraged) just to see if it was a solution, which it didn't seem to be.
Heres the player component which is rendered in App.js
{
orderedPlayersUp.map((player) => {
return (
<Player
playerContent={player.playerContent}
playerId={player.id}
key={player.id}
upvotePlayer={this.upvotePlayer}
downvotePlayer={this.downvotePlayer}
userLogIn={this.userLogIn}
userLogOut={this.userLogOut}
uid={this.uid}
/>
)
})
}
Edit
When I change the upvotePlayer function's database call to .on rather than .once and proceed to upvote a player, it spews out an infinite amount of my console.logs from this componentWillMount function:
App.js line 51 is the console.log below.
this.database.on('child_changed', function (snapshot) {
var name = snapshot.val();
console.log("Player: " + name.playerContent + " has " + name.votes + " votes.")
})
After 20 seconds or so when it overloads itself, an error page comes up and says RangeError: Maximum call stack size exceeded
Changing the componentWillMount functions database call to this.database.once instead of .on cuts down on the infinite loop, but still throws RangeError: Maximum call stack size exceeded
The error:
A gist with the App.js (where the voting occurs (upvotePlayer)) and Player.jsx. Gist with App.js and Player.jsx
Edit2
Votes started registering immediately to the screen when I changed from .once to .on in the Player.jsx file in the comondentDidMount() as seen below. To clarify, this solves my issue of the voting icons not changing in real-time when I change from upvote to downvote. I have yet to test whether or not it registers one player overtaking another with more votes as I originally asked. Just wanted to clarify for anyone who may look through this code to help themselves.
componentDidMount() {
let ref = firebase.database().ref('/players/' + this.playerId + '/voters');
ref.on('value', snap => {
var value = snap.val()
if (value !== null && this.props.uid !== null) {
ref.child(this.props.uid).on('value', snap => {
if (snap.val() === 1){
this.setState({ votedUp: true, votedDown: false })
}
else if (snap.val() === 0) {
this.setState({ votedUp: false, votedDown: false })
}
else if (snap.val() === -1) {
this.setState({ votedDown: true, votedUp: false })
}
else {
console.log("Error calculating the vote.")
}
})
} else {
//Error
}
});
}
Your firebase call is a .once() call at the moment. That will only call the database once. If you want your component to continue to listen for changes then you need to switch to a .on() call. So that would be ref.child(this.uid).on(...) instead of ref.child(this.uid).once(...)
ref.child(this.uid).on('value', snap => {
if (snap.val() === 0 || snap.val() === -1 || snap.val() == null){
ref.child(this.uid).set(1);
} else if (snap.val() === 1) {
ref.child(this.uid).set(0);
}
else {
console.log("Error in upvoting. snap.val(): " + snap.val())
}
})

How to prevent Dynamics CRM 2015 from creating an opportunity when a lead is qualified?

Requirements when clicking the Qualify button in the Lead entity form:
Do not create an Opportunity
Retain original CRM qualify-lead JavaScript
Detect duplicates and show duplicate detection form for leads
Redirect to contact, either merged or created version, when done
The easiest approach is to create a plugin running on Pre-Validation for message "QualifyLead". In this plugin you simply have to set CreateOpportunity input property to false. So it would look like:
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
context.InputParameters["CreateOpportunity"] = false;
}
Or you can go with more fancy way:
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var qualifyRequest = new QualifyLeadRequest();
qualifyRequest.Parameters = context.InputParameters;
qualifyRequest.CreateOpportunity = false;
}
Remember that it should be Pre-Validation to work correctly. Doing it like that allows you to remain with existing "Qualify" button, without any JavaScript modifications.
So Pawel Gradecki already posted how to prevent CRM from creating an Opportunity when a Lead is qualified. The tricky part is to make the UI/client refresh or redirect to the contact, as CRM does nothing if no Opportunity is created.
Before we begin, Pawel pointed out that
some code is not supported, so be careful during upgrades
I don't have experience with any other versions than CRM 2015, but he writes that there are better ways to do this in CRM 2016, so upgrade if you can. This is a fix that's easy to implement now and easy to remove after you've upgraded.
Add a JavaScript-resource and register it in the Lead form's OnSave event. The code below is in TypeScript. TypeScript-output (js-version) is at the end of this answer.
function OnSave(executionContext: ExecutionContext | undefined) {
let eventArgs = executionContext && executionContext.getEventArgs()
if (!eventArgs || eventArgs.isDefaultPrevented() || eventArgs.getSaveMode() !== Xrm.SaveMode.qualify)
return
// Override the callback that's executed when the duplicate detection form is closed after selecting which contact to merge with.
// This callback is not executed if the form is cancelled.
let originalCallback = Mscrm.LeadCommandActions.performActionAfterHandleLeadDuplication
Mscrm.LeadCommandActions.performActionAfterHandleLeadDuplication = (returnValue) => {
originalCallback(returnValue)
RedirectToContact()
}
// Because Opportunities isn't created, and CRM only redirects if an opportunity is created upon lead qualification,
// we have to write custom code to redirect to the contact instead
RedirectToContact()
}
// CRM doesn't tell us when the contact is created, since its qualifyLead callback does nothing unless it finds an opportunity to redirect to.
// This function tries to redirect whenever the contact is created
function RedirectToContact(retryCount = 0) {
if (retryCount === 10)
return Xrm.Utility.alertDialog("Could not redirect you to the contact. Perhaps something went wrong while CRM tried to create it. Please try again or contact the nerds in the IT department.")
setTimeout(() => {
if ($("iframe[src*=dup_warning]", parent.document).length)
return // Return if the duplicate detection form is visible. This function is called again when it's closed
let leadId = Xrm.Page.data.entity.getId()
$.getJSON(Xrm.Page.context.getClientUrl() + `/XRMServices/2011/OrganizationData.svc/LeadSet(guid'${leadId}')?$select=ParentContactId`)
.then(r => {
if (!r.d.ParentContactId.Id)
return RedirectToContact(retryCount + 1)
Xrm.Utility.openEntityForm("contact", r.d.ParentContactId.Id)
})
.fail((_, __, err) => Xrm.Utility.alertDialog(`Something went wrong. Please try again or contact the IT-department.\n\nGuru meditation:\n${err}`))
}, 1000)
}
TypeScript definitions:
declare var Mscrm: Mscrm
interface Mscrm {
LeadCommandActions: LeadCommandActions
}
interface LeadCommandActions {
performActionAfterHandleLeadDuplication: { (returnValue: any): void }
}
declare var Xrm: Xrm
interface Xrm {
Page: Page
SaveMode: typeof SaveModeEnum
Utility: Utility
}
interface Utility {
alertDialog(message: string): void
openEntityForm(name: string, id?: string): Object
}
interface ExecutionContext {
getEventArgs(): SaveEventArgs
}
interface SaveEventArgs {
getSaveMode(): SaveModeEnum
isDefaultPrevented(): boolean
}
interface Page {
context: Context
data: Data
}
interface Context {
getClientUrl(): string
}
interface Data {
entity: Entity
}
interface Entity {
getId(): string
}
declare enum SaveModeEnum {
qualify
}
TypeScript-output:
function OnSave(executionContext) {
var eventArgs = executionContext && executionContext.getEventArgs();
if (!eventArgs || eventArgs.isDefaultPrevented() || eventArgs.getSaveMode() !== Xrm.SaveMode.qualify)
return;
var originalCallback = Mscrm.LeadCommandActions.performActionAfterHandleLeadDuplication;
Mscrm.LeadCommandActions.performActionAfterHandleLeadDuplication = function (returnValue) {
originalCallback(returnValue);
RedirectToContact();
};
RedirectToContact();
}
function RedirectToContact(retryCount) {
if (retryCount === void 0) { retryCount = 0; }
if (retryCount === 10)
return Xrm.Utility.alertDialog("Could not redirect you to the contact. Perhaps something went wrong while CRM tried to create it. Please try again or contact the nerds in the IT department.");
setTimeout(function () {
if ($("iframe[src*=dup_warning]", parent.document).length)
return;
var leadId = Xrm.Page.data.entity.getId();
$.getJSON(Xrm.Page.context.getClientUrl() + ("/XRMServices/2011/OrganizationData.svc/LeadSet(guid'" + leadId + "')?$select=ParentContactId"))
.then(function (r) {
if (!r.d.ParentContactId.Id)
return RedirectToContact(retryCount + 1);
Xrm.Utility.openEntityForm("contact", r.d.ParentContactId.Id);
})
.fail(function (_, __, err) { return Xrm.Utility.alertDialog("Something went wrong. Please try again or contact the IT-department.\n\nGuru meditation:\n" + err); });
}, 1000);
}
There is a fully functional and supported solution posted over at our Thrives blog: https://www.thrives.be/dynamics-crm/functional/lead-qualification-well-skip-that-opportunity.
Basically we combine the plugin modification as mentioned by Pawel with a Client Side redirect (using only supported JavaScript) afterwards:
function RefreshOnQualify(eventContext) {
if (eventContext != null && eventContext.getEventArgs() != null) {
if (eventContext.getEventArgs().getSaveMode() == 16) {
setTimeout(function () {
Xrm.Page.data.refresh(false).then(function () {
var contactId = Xrm.Page.getAttribute("parentcontactid").getValue();
if (contactId != null && contactId.length > 0) {
Xrm.Utility.openEntityForm(contactId[0].entityType, contactId[0].id)
}
}, function (error) { console.log(error) });;
}, 1500);
}
}
}

AngularJS check if promise is empty or not

Hi I have this code for the purpose of checking if there are users in the database, if it finds shows a list of users on a view, otherwise shows a view with form of user creation, but didn't work the check expression what I'm doing wrong
users = Users.query();
users.$promise.then(
function (data) {
if (!data) {
$location.url('/NewUser')
} else {
$location.url('/UsersList')
}
});
It is probably returning an empty array in case nothing is found. Try checking the data length.
if (data.length == 0) {
$location.url('/NewUser')
} else {
$location.url('/UsersList')
}

Categories

Resources