@רפאל וואו וואו... ממש מצויין בשבילי, אני ינסה ללמוד את החידושים שאני לא מכיר בקוד שלך.
@רפאל אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:
נאלצתי לנחש חלק מהקוד
מתנצל... עזרת לי מאוד.
@רפאל וואו וואו... ממש מצויין בשבילי, אני ינסה ללמוד את החידושים שאני לא מכיר בקוד שלך.
@רפאל אמר בTS: הגדרת interface לאובייקט המכיל מופעים של קלאס גנרי:
נאלצתי לנחש חלק מהקוד
מתנצל... עזרת לי מאוד.
יש לי מחלקה בשם Instructions
, שיש לה מתודה סטטית עם השם operate
.
המתודה הנ"ל מקבלת שני ארגומנטים וודאים, ועוד שנים אפשריים: הראשון הוא פונקציית callback שהיא צריכה להעביר לה ארגומנטים ולקרוא לה, ושאר הארגומנטים הם אותם הארגומנטים שיועברו לפונקציית ה-callback.
פונקציית ה-callback יכולה להיות אחת מתוך שלושה סוגי פונקציות: הסוג הראשון מקבל ארגומנט אחד ומבצע עליו פעולה מסוימת, הסוג השני מקבל שני ארגומנטים, והשלישי מקבל שלושה ארגומנטים. הספק שלי הוא איך להתייחס ל-Type של ה-callback הנ"ל.
בהתחלה חשבתי ליישם את זה בערך בצורה כזו:
interface Operand {
// ...
}
interface OperationFunc {
(dstOperand: Operand, srcOperand?: Operand, powerEvaluation?: number): void;
}
class Instructions {
static operate (callback: OperationFunc, dstOperand, srcOperand?, powerEvaluation?) {
callback(dstOperand, srcOperand, powerEvaluation);
}
}
מה שהתברר כלא אפשרי מכמה סיבות. הסיבה המרכזית היא, שפונקציית ה-callback כפי שהזכרתי היא לא אותה פונקציה שפעם מפעילים אותה עם ארגומנט אחד ופעם עם שני ארגומנטים. אלא - אלו פונקציות שונות, שכל פעם אני מעביר פונקציה אחרת כ-callback בהתאם לצורך. לפעמים מדובר בפונקציה שמקבלת ארגומנט בודד, לפעמים שני ארגומנטים ולפעמים שלושה. כמובן שכשאני קורא למתודה operate אני מעביר את שאר הארגומנטים בהתאמה ל-callback המועבר איתם.
בעקבות הבעיה הנ"ל, חשבתי להפוך את המתודה operate
לגנרית, שמקבלת את ה-Type של ה-callback, ואת שאר הארגומנטים היא מקבלת כאובייקט נפרד (בשביל האובייקט הזה היא מקבלת Type גנרי נוסף). משהו כזה:
interface Operand {
// ...
}
interface UnaryOperation {
(dstOperation: Operand): void;
}
interface BinaryOperation {
(dstOperand: Operand, srcOperand: Operand): void;
}
interface SignBinaryOperation {
(dstOperand: Operand, srcOperand: Operand, powerEvaluation: number): void;
}
interface UnaryOperationArgs {
dstOperand: Operand;
}
interface BinaryOperationArgs extends UnaryOperationArgs {
srcOperand: Operand;
}
interface SignBinaryOperationArgs extends BinaryOperationArgs {
powerEvaluation: number;
}
class Instructions {
static operate<OperationType = UnaryOperation | BinaryOperation | SignBinaryOperation, OperationArgs = UnaryOperationArgs | BinaryOperationArgs | SignBinaryOperationArgs> (callback: OperationType, operationArgs: OperationArgs) {
callback(operationArgs);
}
}
הייתי צריך כמובן להמיר את כל הפונקציות שמתוכננות להישלח בתור callback, שיקבלו את הארגומנטים כאובייקט.
אבל אני עדיין לא מרוצה מכמה סיבות. הראשונה היא, שאני לא יכול להיות מרוצה כל עוד TSC לא מרוצה, ומכיוון שהוא לא יכול לדעת מראש שה-callback שיועבר לו אמור לקבל את אותו אובייקט ארגומנטים, הוא זורק שגיאה על כך.
והסיבה השנייה היא שהקוד (לטעמי לפחות) נראה זוועה ככה, וחייבת להיות דרך יותר אלגנטית לעשות דבר כזה...
תכלס השאלה שלי היא, מהי הדרך המומלצת ליישם כזאת מתודה? אודה מאוד למי שיוכל לעזור לי בעניין.
@נהראל אתה בטוח שלא הצליח? לפי הפלט בתמונה זה נראה שהצליח.
@חייםיודלביץ כמו ש @OdedDvir כבר כתב, הפונקציה parseEmailHeader
מחזירה אובייקט בעל שתי ערכים: name, email. כאשר name הוא השם של השולח, ו email הוא הכתובת מייל שלו.
הדוגמה שהראיתי לך למימוש למעלה, היא במסגרת ה Logger. אני משער שמה שאתה עשית זה לשלוח מייל שמכיל את הערך שהפונקציה הזאת מחזירה, מה שגרם לשגיאה המוכרת objectObject.
ובמילים פשוטות, אם אתה רוצה להציג את האובייקט הזה ב HTML כלשהו, תצטרך לקחת ממנו את הערכים שלו כמו ש @OdedDvir הראה בפוסט הקודם.
@חייםיודלביץ אתה צריך לעשות return מהפונקציה ש- @OdedDvir הביא.
דוגמה למימוש אצלי:
function newmail() {
let newmessages = GmailApp.search('from: example@gmail.com')
for (let newmessage of newmessages) {
let message = newmessage.getMessages()[0]
let senderEmail = parseEmailHeader(message)
Logger.log(senderEmail)
}
}
function parseEmailHeader(message) {
var header = message.getFrom().trim();
var extract = { name: "", email: "" };
var emails = header.match(/[^@<\s]+@[^@\s>]+/g);
if (emails) {
extract.email = emails[0];
}
var names = header.split(/\s+/);
if (names.length > 1) {
names.pop();
extract.name = names.join(" ").replace(/"/g, "");
}
return extract;
}
כרגע הוא עושה פשוט Logger.log
, מה שאומר שהוא לא מחזיר שום ערך.
@chagold האמת שלא לגמרי הבנתי את הסיטואציה... אבל ממה שאני כן (מקווה ש)הבנתי, לכאורה watch
יכולה לעזור לך, משהו כזה:
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
user: JSON.parse(localStorage.getItem('user'))
},
getters: {
getUser(state) {
return state.user;
}
},
mutations: {
selectAccount(state, account) {
if (account) state.user.account = account;
localStorage.setItem('user', JSON.stringify(state.user));
}
}
});
const vm = new Vue({
el: '#app',
store,
watch: {
'$store.state.user': function () {
console.log(this.$store.state.user.account)
}
}
})
// אפשר גם ככה
vm.$watch('$store.state.user', function (val, oldVal) {
console.log(val.account)
})
// ואח"כ באיזשהו שלב אתה מבצע את המוטציה
vm.$store.commit('selectAccount', 'admin')
עוד על watch
פה
@שאול-נ-י אם הבנתי אותך נכון, אתה רוצה לקבל רק הודעה ספציפית מתוך שרשור (מה שנקרא Thread בג'ימייל API).
הרעיון הוא כזה. נניח שקיבלת Thread מסויים בדרך זו או אחרת, אני ישתמש לצורך הדוגמה במתודה getThreadById שמחזירה Thread לפי המזהה שלו.
אחרי שקיבלת את אובייקט ה-Thread, תשתמש במתודה getMessages, שמחזירה מערך של ההודעות בשרשור.
ומכאן הדרך קצרה לקבל את ההודעה האחרונה, בצורה של מערך[מערך.אורך - 1]:
const thread = GmailApp.getThreadById('some string id');
const messages = thread.getMessages();
const lastMessage = messages[messages.length - 1];
@davidnead אם אתה כותב בפריימוורקים כדוגמת vue.js כמו שראיתי שכתבת בשרשור כאן, אתה יכול להשתמש בספריות מוכנות של קומפוננטות מעוצבות, כדוגמת Vuetify, buefy, ועוד רבות וטובות. בצורה כזאת אתה לא צריך לדאוג ל-CSS מאפס.
אני מנסה ליצור פרוייקט קטן באקספרס לצורך תרגול בלבד, בלי DB ושמירת נתונים בכלל (אני יודע שזה מגוחך אבל מה לעשות, לא מבין בדטהבייס עדיין).
כתבתי class לניהול המשתמשים (כמובן שברגע שנופל השרת מכל סיבה שהיא לא נשאר כלום מהמידע), ה class נראית בערך ככה:
class Users {
constructor() {}
createUser() {}
editUserProfile() {}
deleteUser() {}
}
זה בגדול המתודות של ה class, ואני עושה לו export singleton ככה:
module.exports = new Users();
כמובן שיש בקלאס מערך של המשתמשים, שאליו הם נוספים.
בנוסף יש לי תקייה שאחראית על הניתוב בשם 'routes', בתיקייה הזאת שמתי ראוטר להתחברות 'login', וראוטר להרשמה 'signup'.
בקובץ app.js אני צורך את הראוטרים עם require, ואני רוצה שכל הראוטרים של ה users כמו התחברות והרשמה, יקבלו את אותו קלאס דרך app.js לצורך העניין, ויעבדו מולו. כי מה שקורה אם אני מייבא את הקלאס לתוך כל אחד מהראוטרים, זה בעייתי - כי הוא מקבל קלאס חדש שאין בו את המערך של היוזרים.
בכל מקרה השאלה שלי היא - איך אני יכול לקבל לתוך הראוטרים את הקלאס הזה בשביל לעבוד איתו?
@ivrtikshoret אוקיי. דבר ראשון, אני רואה שכן צריך את הנתיב לקובץ ה-JS.
דבר שני, צריך להוציא את run_at מה-content_script, רק אז זה עובד (האמת שלא ברור לי עדיין למה זה ככה),
דבר שלישי, בסקריפט עצמו, אם נשאר בקוד שכתבת זה לא יספיק כי התפיסה של האלמנטים נעשית כשהמייל נטען, ומה שקורה זה שלוקח כמה שניות עד שבכלל האלמנט עם הקלאס "aQw" קיים בדף, והסקריפט הספיק כבר להיטען לפני.
אז מה שצריך לעשות זה לעשות setTimeout לכמה שניות, אבל אני חושב שזה לא חכם.
לדעתי עדיף להוסיף eventListener או משהו כזה, שברגע שנכנסים להודעה כל שהיא, רק אז הסקריפט ירוץ. לא יודע צריך לחשוב מה אפשר לעשות עם זה.
@נ-נח לא נראה לי שאפשר, בכל מקרה זו שאלה מעניינת
@dovid קוד מצויין! רק אם יורשה לי הקטן הערה קטנה..
כשקיימים כמה אלמנטים תחת אלמנט אב אחד וצריך להאזין לאותו אירוע אצל כולם, לפי מה שידוע לי עדיף יותר להאזין לאירוע אצל אלמנט האב, ולא להצמיד אותו בלולאה לכל אותם אלמנטים. בכל אופן זה מה שיצא לי (עושה בדיוק את אותו דבר, רק באמצעות האזנה לאלמנט האב body).
<script>
document.body.addEventListener('change', onCheckboxChange)
function onCheckboxChange(e){
console.log(e.target)
document.getElementById('response').innerText = e.target.dataset.content + ": " + e.target.checked;
}
</script>
@חגי אמר בהודעת ספאם | האם יש לחשוש כשלחצתי על הקישור?:
מעניין אותי לדעת אם הם אי פעם הצליחו לעבוד על מישהו.
יש סטאטיסטיקות של זה איפשהו?
וודאי שנופלים בזה, יש אחוזים לא מבוטלים שנופלים. זכור לי שהממוצע הוא בערך 15% (זה המון).
עכשיו מצאתי סטטיסטיקות מאוד מעניינות על התקפות פישינג באתר הזה, הם טוענים שבכל התקפת פישינג נופלים קורבן בממוצע 12% נמענים (נתונים סטטיסטיים של 2020).
@nigun בעיקרון בהדרכה המוסרטת מופיעה אישה, אבל אפשר לעבור על המדריך הזה בנטפרי לצורך העניין ולראות את הטקסט ללא הסרטונים.
נ.ב. שכחתי לציין שהמדריך הזה חינמי בנוסף לכל.
@yossiz האמת שבבואי כעת לשחזר את הבעיה, אני קולט את דברי @רפאל שבאמת לא פרטתי מספיק את הקוד שהשתמשתי בו. השמטתי בשאלתי את הפרמטר השני שפונקציית ה-perform מקבלת, משום שסברתי שאינה קשורה לבעיה המדוברת.. כעת מסתבר לי שזוהי סיבת הבעיה.
להלן הקישור ל-playground המשחזר את הבעיה.
לאחר העזרה של @רפאל התותח כאן, בסופו של דבר ביצעתי את האימפלמנטציה של זה בצורה גנרית לאחר שעשיתי refactoring לקוד והרכבתי אותו בצורה יותר טובה.
כעת השאלה שלי היא כזאת:
יש בידי מחלקה גנרית בשם Instruction
, שמקבלת Type גנרי שיכול להיות או UnaryOperands
או BinaryOperands
- מתוך האינטרפייסים הבאים:
interface UnaryOperands {
dst: number;
}
interface BinaryOperands extends UnaryOperands {
src: number;
}
קיימת מחלקה נוספת הנקראת בשם Processor
, שמחזיקה property שנקרא instructions
המחזיק אובייקט של מופעים של המחלקה הגנרית Instruction
.
שאלתי היא - מהי הדרך המומלצת להגדיר את ה-Type של האובייקט הנ"ל.
חשבתי בתחילה להשתמש ב-Union type ככה:
interface Instructions {
[index: string]: Instruction<UnaryOperands | BinaryOperands>;
}
אך התברר לי שזאת דרך בעייתית, לאחר שכתבתי מתודה במחלקה Processor
שמקבלת instruction מתוך האובייקט instructions שב-Processor. את המתודה יישמתי כמו הדרכתו של @רפאל בצורה של Overloading כך:
private perform (instruction: Instruction<{ dst: number }>): void;
private perform (instruction: Instruction<{ dst: number, src: number }): void;
private perform (instruction: Instruction<{dst: number, src?: number }): void { } // implementation
אבל כשהשתמשתי במתודה הזאת והעברתי לה instruction, מיד TS הקפיץ לי את השגיאה הבאה:
Argument of type 'Instruction<UnaryOperands | BinaryOperands>' is not assignable to parameter of type 'Instruction<{ dst: number; src: number; }>'.
Type 'UnaryOperands | BinaryOperands' is not assignable to type '{ dst: number; src: number; }'.
Property 'src' is missing in type 'UnaryOperands' but required in type '{ dst: number; src: number; }'
המסקנה שהגעתי אליה היא (ייתכן שאני טועה) - שההגדרה שביצעתי ל-Type של Instructions
היא לא נכונה, וצריכה להיות דרך יותר טובה להגדיר את ה-Type שלו.
אשמח לעזרתכם.
@צדיק-תמים תסתכל בהודעה שב-popup. הוא מציע לך להתייחס לקובץ כמודול ES במקום CommonJS שזה סטנדרט של מודולים שפועל בנוד (לדוג' require
ו- module.exports
).
הסיבה לכאורה היא שהסטנדרט החדיש שפועל גם בפלטפורמות השונות ובדפדפנים הוא ES מודול שפועל בצורה קצת שונה import
, export
וכו'.
תסתכל כאן הוא טוען שיש הבדל בין שני צורות העבודה בנוגע לצורת הטעינה של המודולים, שב Commonjs הם נטענים באופן סינכרוני, וב ES באופן אסינכרוני (כך שזו אמורה להיות מעלה על פניו).
@chagold איך הגדרת את ה - getter של getUser? אם תוכל להעלות קוד.