Value update on the same tick Vue.js - javascript

I have a password field on my Vue.JS form that should update a progress bar, the policy is quite simple, I have 5 conditions, each should add 20% to the progress bar. what I have done is 5 computed checks for each condition which looks something like this:
hasLower() {
return this.password == null
? null
: /[a-z]/.test(this.password);
}
thenI trigger a function at keyPress to count how many trues am I getting from the 5 checks and update the value of a counter that is later mapped to the progress bar, like this:
checkPassword() {
this.trueCounter = 0;
this.progressBar.value = 0;
if (this.hasLower) this.trueCounter++;
if (this.hasUpper) this.trueCounter++;
if (this.hasDigit) this.trueCounter++;
if (this.hasSChar) this.trueCounter++;
if (this.has8) this.trueCounter++;
console.log("Probe: " + this.trueCounter)
}
My problem is that values are updated by the following key press! For example, if I type "a" in the password field, I get 0, if I type it again I get a 1 and it stays like this. then if I type in a character that triggers a different condition "A" for example, I get the last value again and it's changed only by the following Key press.
Is there a way to overcome this one keypress delay so I can always get the updated value immedietly?

I would probably set that as a computed value so that it is updated every time the password is updated. Maybe something like below would work better for you?
new Vue({
data: {
password: ""
},
computed: {
hasLower: function() {
return this.password && /[a-z]/.test(this.password);
},
hasUpper: function() {
return this.password && /[A-Z]/.test(this.password);
},
hasDigit: function() {
return this.password && /[0-9]/.test(this.password);
},
hasSChar: function() {
return false; // User yours here. I don't know the regex off the tope of my head.
},
has8: function() {
return this.password && this.password.length >= 8;
},
password_strength: function() {
var points = 0;
if (this.hasLower) points++;
if (this.hasUpper) points++;
if (this.hasDigit) points++;
if (this.hasSChar) points++;
if (this.has8) points++;
return points;
}
}
});
EDIT: Just realized the original post does not have () at the end of those, so I added them. They may well be properties in your code, so no parentheses makes sense that way. I just made them methods.
EDIT2: I take that back, they'll probably be better off as computed properties. Taking a second look, it looks like I've got something very similar to what you have, but as far as I can tell, the password strength is calculated correctly on my end. When the password hits the appropriate checks, the password strength variable is updated appropriately.

Related

firebase realtime database transaction not working

I followed the tutorial pretty closely and am testing a "like" feature with a transaction. However, when I test it with 2 devices, the count doesn't seem to hold up well.
The error is when I click the like-button on both devices, there are times that the counter goes up by two, but that are times that the counter goes up then down making it increase by 0 (when in fact there were two like buttons). Similar problem when there is two un-liking buttons pressed at the same time.
Unliking both buttons (at the same time) could also cause the counters to increase by two instead... when it should be decreasing it by two.
var liked; // global variable to check if button has been liked
document.getElementById("like-button").onclick = function () {
console.log("click");
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
console.log("post:" , post);
if (post) {
if (post.likes && liked) {
post.likes--;
liked = false;
}
else {
post.likes++;
liked = true;
}
}
return post;
});
}
Wondering what is the problem here given I followed this transaction pretty closely.
https://firebase.google.com/docs/database/web/read-and-write
Edit: JSON I have
Brand3
Brand3ID
impressions: 0
likes: 16
views: 0
Update: I noticed that the post logs 3 times when i click the button simultaneously on 2 devices - which could possibly explain the failure in handling the global flag, but am still unable to resolve why. Usually post should only log twice, one null and one when it detects the post (Firebase realtime database transaction handler gets called twice most of the time)
I believe I found the answer.
I've learnt that firebase transaction will run multiple times until successful (references: https://firebase.google.com/docs/database/web/read-and-write, Firebase transactions in NodeJS always running 3 times?), hence my liked flag was always changing depending on the number of times the transaction ran.
I fixed it by extracting the flag out so that it is not dependent on the number of transactions ran.
var liked; // global variable
document.getElementById("like-button").onclick = function () {
if (liked){
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
if (post) {
if (post.likes) {
post.likes--;
}
}
console.log("post:" , post);
console.log("liked: ", liked);
return post;
});
liked = false;
}
else{ // not liked
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
if (post) {
if (post.likes) {
post.likes++;
}
}
console.log("post:" , post);
console.log("liked: ", liked);
return post;
});
liked = true;
}
}
Feel free to let me know if there's a more elegant way.

How to add group or role based restriction to some action in YouTrack workflow?

I have created a workflow to restrict the changes in the estimation once it has been added. However, I want to give this permission to the Admins but restrict all other groups such as Developers.
I have tried isInGroup function but it's not working correctly and I always get an exception.
var entities = require('#jetbrains/youtrack-scripting-api/entities');
var workflow = require('#jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Protect_estimations',
guard: function(ctx) {
if(!ctx.current.isInGroup('Admin')) {
return (ctx.issue.fields.oldValue(ctx.Estimation) !== null && ctx.issue.fields.isChanged(ctx.Estimation) === true); }
return false; },
action: function(ctx) {
workflow.check(false,'Sorry, you cannot update estimates'); },
requirements: {
Estimation: {
type: entities.Field.periodType,
name: 'Estimation'
} } });
The condition !ctx.current.isInGroup('Admin') does not work whereas rest of the code is running perfect. Do I need to add any rule in the 'requirements' section in order to get this done? Please suggest.
To resolve the issue, I suggest you use the currentUser property instead:
if(!ctx.currentUser.isInGroup('Admin'))...
I hope this helps.

Safari extension - How do I set the badge number on specific tab only?

How can I set the badge number on a specific tab only? So far I have a code that sets the badge number on all the tabs.. I've been reading around A LOT, but there doesn't seem to be a whole lot of information about this, so perhaps I will find a solution for this here.
I would like something like Adblock Plus, which sets the badge number for a specific tab. This is pretty easy in Chrome etc, but doesn't seem to be the case in Safari.
Does anyone know how extensions like Adblock plus shows the badge number on a specific tab?
So far I only have this code, but as mentioned, it sets the badge on all the tabs, which is not the result I want.
safari.extension.toolbarItems[0].badge = 2;
Edit:
I have been looking at the source code of Adblock plus, and a few other extensions that had this function. And it seems it is using some prototype.
Adblock plus background snippet:
BrowserAction.prototype = {
_set: function(name, value)
{
var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
if (!toolbarItem)
{
return;
}
var property = toolbarItemProperties[name];
if (!property)
{
property = toolbarItemProperties[name] = {
pages: new ext.PageMap(),
global: toolbarItem[name]
};
}
property.pages.set(this._page, value);
if (isPageActive(this._page))
{
toolbarItem[name] = value;
}
},
setIcon: function(path)
{
this._set("image", safari.extension.baseURI + path.replace("$size", "16"));
},
setBadge: function(badge)
{
if (!badge)
{
this._set("badge", 0);
}
else if ("number" in badge)
{
this._set("badge", badge.number);
}
}
};
Content script (adblockplus.js)
FilterNotifier.on("filter.hitCount", function(filter, newValue, oldValue, page)
{
if (!(filter instanceof BlockingFilter) || !page)
{
return;
}
Prefs.blocked_total++;
var blocked = blockedPerPage.get(page) || 0;
blockedPerPage.set(page, ++blocked);
if (Prefs.show_statsinicon)
{
page.browserAction.setBadge(
{
color: badgeColor,
number: blocked
});
}
});
It seems this is how Adblock plus does it, but so far I haven't been able to replicate it. Still trying though..
Okay, so I finally found a solution for this, and thought I would share what I did, in case somebody else is in the same situation.
This morning I got the idea of storing the data in an array, when the user visits one of the websites I want to display the badge number on (doesn't store all websites the user visits), only if it matched one of the websites I wanted to target. I stored the following data in the array: root domain (example.com) and the badgeNumber.
For this to work, you need to make an array of the root domain of the websites you want to target, and then only execute the following when it matches, otherwise the array would fill up very quickly, and we don't want too much data in it.
In the global page, start by making an empty array to store the data
var badgeUpdateArray = [];
You then need to set up message handling in your global page as well.
safari.application.addEventListener('message', handleMessage, false);
function handleMessage(event) {
if(event.name === "setBadgeText"){
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var found = badgeUpdateArray.some(function (el) {
if(el.identifier === event.message.identifier){
// Was found
isFound = 1;
}
});
if (isFound == 0) {
// Not found, add to the array
badgeUpdateArray.push({identifier:event.message.identifier,badgeNumber:event.message.badgeNumber});
}
// Set the badge number
safari.extension.toolbarItems[0].badge = event.message.badgeNumber;
}
}
Now we need to send the message from the content script to the global page. You need to get the root domain (example.com), I'm not getting into that here, as it's pretty easy. You will also need the badgeNumber value, this can be gathered from wherever (GET request, or elsewhere..)
Remember, only execute this code if the website matches your target domains.
var message = {
identifier: domain,
badgeNumber: rows.length
}
safari.self.tab.dispatchMessage("setBadgeText", message);
This will send the message, and store the data in the array, it will also set the badge number.
Now, for this to be working on different tabs, you will need to make an event handler for "activate" on the global page, this will run whenever a tab is active.
safari.application.addEventListener("activate", updateBadge, true);
function updateBadge(){
var cDomain = safari.application.activeBrowserWindow.activeTab.url;
cDomain = cDomain.replace("www3.","");
cDomain = cDomain.replace("www2.","");
cDomain = cDomain.replace("www1.","");
cDomain = cDomain.replace("www.","");
cDomain = new URL(cDomain);
cDomain = cDomain.hostname;
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var badgeNumber = 0;
var found = badgeUpdateArray.some(function (el) {
badgeNumber = el.badgeNumber;
if(el.identifier === cDomain){
// Was found, set the badge number
isFound = 1;
safari.extension.toolbarItems[0].badge = el.badgeNumber;
}
});
if (isFound == 0) {
// Was not found
safari.extension.toolbarItems[0].badge = 0;
}
}
Hopefully I've got it all in here, and at least something that works, though I have to say that I would prefer an easier way of storing it.. like Chrome etc does it, with the tab API.

How to implement automatic achievements rule

I ran across a challenge today. What I want to do is an automatic achievement system for my website. I want to check daily if users meet the requirements to get new achievements. Those requirements would not be stocked in the database, to add new achievements, I want to be able to only define a new javascript object/prototype, something along those lines.
I am working with sails and I don't know if my approach is right. I am making a service with a method "checkForAchievements" and I want it to check for all users and if they meet the requirements for every achievement defined. If they do, I would add a record in my table user_achievement. Here is what I tried so far :
exports.checkForAchievements = function(callback) {
var AchievementsValidator = function(achievement, user) {
this.achievement = achievement;
this.user = user;
this.validation = function() {
return true;
};
this.giveAchievement = function() {
UserAchievement.findOrCreate({ user: this.user, achievement: this.achievement}, { user: this.user, achievement: this.achievement })
.exec(function(err, userAchievment) {});
}
};
//Example badge code, validation returns true if the user has at least 50 posts on the website
var FiftyPosts = function() {
this.validation = function() {
Post.count({ user: this.user }).exec(function(err, count) {
if (count >= 50) {
return true;
}
else {
return false;
}
})
};
};
//Loop in users and execute this code for each
FiftyPosts.prototype = new AchievementsValidator(22, user); // 22 would be the achivement in database
if (FiftyPosts.validation) {
FiftyPosts.giveAchievement();
}
callback();
};
Is it possible to get somewhere with this code? I feel like it would messy because I would need to write down each achivement in my final loop. Also, the code above is not working properly I can't get the validation to execute on the badge.
Note: Performance is not an issue as this code will not run often. I only want this to be scalable, so that I can add new achievements without too much hassle.
Thank you.
EDIT: It seems like the questions weren't clear enough so here you go :
How can I get FiftyPosts.validation to use the right method in the above code? It is not using any of the above (AchievementsValidator, FiftyPosts).
If you know sails.js enough can you tell me if doing this in a service is a good idea?

Meteor method reactivity doesn't work

I'm not experienced in Javascript but I've read a ton of articles about Meteor reactivity but still can't figure out why it is not working in my case.
When a new product is added, I want to be recalculated total cost and use it in the totalCost helper so it's almost real time visible in the browser.
Can someone please take a look at my code and try to figure out some logic error? Everything except the reactivity is working on my computer.
I have got this method in /models/Product.js :
Meteor.methods({
totalProductCost: function() {
var pipeline = [
{$match: {owner: Meteor.userId()}},
{$group: {_id: null, cost: {$sum: "$cost"}}}
];
var data = Products.aggregate(pipeline)["0"].cost;
return (data === undefined) ? 0 : data;
}
});
Then I've got layout.js in client folder:
if (Meteor.isClient) {
var handle = Meteor.subscribe("Products", Meteor.userId());
ProductManager = {
_productItems: null,
_dep: new Tracker.Dependency(),
getProducts: function () {
this._dep.depend();
return this._productItems;
},
setProducts: function (value) {
if (value !== this._productItems) {
this._productItems = value;
this._dep.changed();
}
},
getTotalCost: function () {
return ReactiveMethod.call('totalProductCost');
}
}
// TRACKER
Tracker.autorun(function () {
if (handle.ready()) {
ProductManager.setProducts(Products.find().fetch());
}
});
// HELPERS
Template.boxOverview.helpers({
"totalCost" : function () {
return ProductManager.getTotalCost();
},
});
}
It seems that you used a collection.aggregate in a method. If you need reactivity, you need to use a publication rather than a method (or you need to call the method each time you want to refresh). However, if you use your aggregation inside your publication (I assume you use a package for it) you will loose reactivity as well.
What I would advise you is to use a publication without aggregate function. You calculate your product cost by creating a new field and adding it to your cursor. Once, you do that, if you want to keep reactivity, it is necessary to use to use cursor.observeChanges() or just cursor.observe().
Have a look at this example:
var self = this;
// Modify the document we are sending to the client.
function filter(doc) {
var length = doc.item.length;
// White list the fields you want to publish.
var docToPublish = _.pick(doc, [
'someOtherField'
]);
// Add your custom fields.
docToPublish.itemLength = length;
return docToPublish;
}
var handle = myCollection.find({}, {fields: {item:1, someOtherField:1}})
// Use observe since it gives us the the old and new document when something is changing.
// If this becomes a performance issue then consider using observeChanges,
// but its usually a lot simpler to use observe in cases like this.
.observe({
added: function(doc) {
self.added("myCollection", doc._id, filter(doc));
},
changed: function(newDocument, oldDocument)
// When the item count is changing, send update to client.
if (newDocument.item.length !== oldDocument.item.length)
self.changed("myCollection", newDocument._id, filter(newDocument));
},
removed: function(doc) {
self.removed("myCollection", doc._id);
});
self.ready();
self.onStop(function () {
handle.stop();
});
This is taken from here.

Categories

Resources