How can i divide my code using MVC pattern? - javascript

I need to create a simple like counter. My solution to this problem i wrote in class View. But I don't understand how can i divide my code into three parts - Model, View, Controller.
class View {
constructor(model) {
this.count = 0;
this.renderInitialTodoForm();
this.likeButton.addEventListener('click', () => {
this.count++;
this.likeCount.innerHTML = this.count;
})
}
renderInitialTodoForm = () => {
this.app = this.getElement('#root');
this.likeButton = this.createElement('span');
this.likeButton.textContent = '👍';
this.likeButton.classList.add('like')
this.likeCount = this.createElement('span');
this.likeCount.textContent = 0;
this.app.append(this.likeButton, this.likeCount);
};
}

Related

JavasScript Show first two images, and then 1 random images from an array using a class method

I have all of the components for this project with the exception of a class method to show the first two images in the array (the Pokeball, and the default eevee) then randomly choose from an array one more image(eevee evolutions) and then stop.
Please be patient I am very new to this.
class Pokemon {
constructor() {
this.listOfCharmander = [
"./images/pokeball.png",
"./images/charmander/charmander0.png",
"./images/charmander/charmander1.png",
"./images/charmander/charmander2.png",
];
this.index = 0;
this.pokemon = document.createElement("img");
}
handleClick = () => {
if (this.index < 3) {
++this.index;
this.pokemon.src = this.listOfCharmander[this.index];
}
};
buildPokemon = () => {
this.pokemon.src = this.listOfCharmander[0];
this.pokemon.classList.add("pokemon");
this.pokemon.addEventListener("click", this.handleClick);
main.appendChild(this.pokemon);
};
}
const pokemon = new Pokemon();
const pokemon1 = new Pokemon();
pokemon.buildPokemon();
pokemon1.buildPokemon();
class Eevee {
constructor() {
this.eeveeEvolutions = [
"/images/pokeball.png",
"images/eevee/eevee0.png",
"images/eevee/eevee1.png",
"images/eevee/eevee2.png",
"images/eevee/eevee3.png",
"images/eevee/eevee4.png",
"images/eevee/eevee5.png",
"images/eevee/eevee6.png",
"images/eevee/eevee7.png",
"images/eevee/eevee8.png",
];
this.index = 0;
this.eevee = document.createElement("img");
}
handleClick = () => {
if (this.index < 9) {
++this.index;
this.eevee.src = this.eeveeEvolutions[this.index];
}
};
buildEevee = () => {
this.eevee.src = this.eeveeEvolutions[0];
this.eevee.classList.add("eevee");
this.eevee.addEventListener("click", this.handleClick);
main.appendChild(this.eevee);
};
}
const eevee = new Eevee();
const eevee1 = new Eevee();
eevee.buildEevee();
eevee1.buildEevee();
...
I apologize if my question isnt exactly clear. I need to add to my handClick method. A way display the first image (the Pokeball) and then the 2nd image (default eevee) And then randomly choose (including having it stay the same there could be no evolution at all) one more evolution from the remaining array.
if you always need first two images in your array then you can add extra attribute to your Eevee class to store those images path, and for random image you can add this code to your handleClick()
handleClick = () => {
this.index=Math.floor(Math.random(2,9)*10)
this.eevee.src = this.eeveeEvolutions[this.index];
};

How to push all objects in class into one array javascript?

I'm trying to make language learn app and i have a problem. I have class "Word"
class Word {
constructor(englishWord, polishWord){
this.englishWord = englishWord
this.polishWord = polishWord
this.displayTranslation = () =>{
console.log(`${englishWord} = ${polishWord}`)
}
}
}
and lots of objects like
const intimate = new Word('intimate', 'intymny/prywatny')
const insurance = new Word('insurance', 'ubezpieczenie')
and I honestly don't have idea how to push all objects into one array. Can I use 'foreach' on every class object? Or is there a better solution for this?
You have to declare a global array where all instances will be pushed to
const instances = [];
class Word {
constructor(englishWord, polishWord){
this.englishWord = englishWord;
this.polishWord = polishWord;
this.displayTranslation = () =>{
console.log(`${englishWord} = ${polishWord}`);
};
instances.push(this);
}
static GetWords() {
instances.forEach( x => {
x.displayTranslation();
});
}
}
new Word('intimate', 'intymny/prywatny');
new Word('insurance', 'ubezpieczenie');
Word.GetWords();
Let's build up your problem in natural language before we write some code:
A Word has its nativ and translation. A Word is stored in a Dictionary. You can add translations to a Dictionary and so on..
For that the array would be hide in a Dictionary like
class Dictionary {
constructor() {
this.words = []
}
addTranslation(word) {
this.words.push(word)
}
// more ..
}
Code Snippet
class Word {
constructor(englishWord, polishWord) {
this.englishWord = englishWord
this.polishWord = polishWord
this.displayTranslation = () => {
console.log(`${englishWord} = ${polishWord}`)
}
}
}
class Dictionary {
constructor() {
this.words = []
}
addTranslation(word) {
this.words.push(word)
}
print() {
for (let i = 0; i < this.words.length; i++) {
this.words[i].displayTranslation()
}
}
}
const dictionary = new Dictionary()
const intimate = new Word('intimate', 'intymny/prywatny')
const insurance = new Word('insurance', 'ubezpieczenie')
dictionary.addTranslation(intimate)
dictionary.addTranslation(insurance)
dictionary.print()
Improvement
I suggest to use a Map instead of an Array. If the Dictionary will be extended by methods for finding words than you have to find the words in an Array by your self..
class Word {
constructor(englishWord, polishWord) {
this.englishWord = englishWord
this.polishWord = polishWord
this.displayTranslation = () => {
console.log(`${englishWord} = ${polishWord}`)
}
}
}
class Dictionary {
constructor() {
this.words = {}
}
addTranslation(word) {
this.words[word.englishWord] = word.polishWord
}
getTranslation(english) {
return this.words[english]
}
print() {
for (let i = 0; i < this.words.length; i++) {
this.words[i].displayTranslation()
}
}
}
const dictionary = new Dictionary()
const intimate = new Word('intimate', 'intymny/prywatny')
dictionary.addTranslation(intimate)
console.log(dictionary.getTranslation('intimate'))
You can push class objects into an Array without any issue:
// using your class declared above
const intimate = new Word('intimate', 'intymny/prywatny')
var array = [];
array.push(intimate);
But depending on your needs, you could put something like this directly into the constructor and have it collect all of the items it's constructed for you:
const instances = [];
class Word {
constructor(englishWord, polishWord){
this.englishWord = englishWord
this.polishWord = polishWord
this.displayTranslation = () =>{
console.log(`${englishWord} = ${polishWord}`)
}
Word.addInstance(this);
}
static addInstance(item){
instances.push(item);
}
static getInstances(){
return instances;
}
static clearInstances(){
instances.length = 0;
}
}
With this every time you construct an instance it's added to the external array. If you need to get everything from the array you can call Word.getInstances() or Word.clearInstances() if you want to empty it.

Is using Eval a good idea?

I'd come to a situation where I had to get the object value dynamically from an array having object keys coming from an api. I came to this approach by using eval.
class App extends React.Component {
constructor() {
super();
this.state = {
title: 'Developers',
descp: 'They are just amazing! JK',
names: ['title', 'descp']
}
}
getVal(objKey) {
let { title, descp } = this.state;
return eval(objKey);
}
render() {
let {names} = this.state;
return (
<div>
<h2>{this.getVal(names[0])}</h2>
<div>{this.getVal(names[1])}</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The above code works correctly.
Another approach I found later:
render() {
let {names} = this.state;
return (
<div>
<h2>{this.state[names[0]]}</h2>
<div>{this.state[names[1]]}</div>
</div>
)
}
Outputs the same result. But my question is that if I use eval with the following case, is it a good approach to do so?
Eval should be avoided as it can be very dangerous. You can safely replace your eval call with accessing property via bracket notation.
getVal(objKey) {
if(this.state.hasOwnProperty(objKey)){
return this.state[objKey];
} else {
// handle missing property
}
}
Eval is generally avoided as it allows the client to insert and evaluate their own expressions in your code.
That being said, JavaScript being a client side language already allows full access to the user, so there isn't really a good reason not to use it.
As long as the user can only mess with their own session, i wouldn't worry. Security should be handled server side anyway so: Beware but don't simply ignore Eval.
EDIT 1 - Defending Eval
The comments pointed out some issues, mainly Performance/Optimization impact, which this answer explains in depth. Basically, since it's Just-In-Time compiling anyway, you don't really lose that much in terms of performance.
As for an example on a use case, here is a template example i whipped up, which also uses the controversial with statement:
var Template = /** #class */ (function () {
function Template(html) {
this.html = html;
}
Template.prototype.apply = function (params, returnDOMObject) {
if (params === void 0) { params = {}; }
if (returnDOMObject === void 0) { returnDOMObject = false; }
with (params) {
var html = eval('`' + this.html.replace(Template.regexes.encapsulated, function (n) {
return n
.replace(Template.regexes.start, '${')
.replace(Template.regexes.end, '}');
}) + '`');
}
if (returnDOMObject) {
return document.createRange().createContextualFragment(html);
}
return html;
};
Template.regexes = {
encapsulated: new RegExp('{{.*?}}', 'igm'),
start: new RegExp('\{{2,}', 'igm'),
end: new RegExp('\}{2,}', 'igm')
};
return Template;
}());
//TEST
var persons = [
{ name: "Peter", age: 25 },
{ name: "Ole", age: 55 },
];
var templates = [];
var container = document.body.appendChild(document.createElement("div"));
var leftBox = container.appendChild(document.createElement("div"));
var rightBox = container.appendChild(document.createElement("div"));
leftBox.style.width = rightBox.style.width = "50%";
leftBox.style.height = rightBox.style.height = "500px";
leftBox.style.cssFloat = rightBox.style.cssFloat = "left";
var leftList = leftBox.appendChild(document.createElement("select"));
leftBox.appendChild(document.createElement("br"));
var leftText = leftBox.appendChild(document.createElement("textarea"));
leftText.style.width = "100%";
leftText.style.resize = "vertical";
var rightOutput = rightBox.appendChild(document.createElement("div"));
function updateLists() {
leftList.innerHTML = '';
for (var i = 0; i < templates.length; i++) {
var template = templates[i];
var option = document.createElement("option");
option.value = option.innerHTML = template.name;
leftList.appendChild(option);
}
}
var h1Template = new Template("<h1>{{name}}</h1>");
var h2Template = new Template("<h2>{{age}} is no age!</h2>");
var pTemplate = new Template("<p>{{name}} may be {{age}}, but is still going strong!</p>\n<p>(When he's {{age*2}} though...)</p>");
var personTemplate = new Template("<p>\n{{ h1Template.apply(params) }}\n{{ h2Template.apply(params) }}\n{{ pTemplate.apply(params) }}\n</p>");
templates.push({ name: "personTemplate", template: personTemplate });
templates.push({ name: "h1Template", template: h1Template });
templates.push({ name: "h2Template", template: h2Template });
templates.push({ name: "pTemplate", template: pTemplate });
function updateOutput() {
rightOutput.innerHTML = '';
for (var pi = 0; pi < persons.length; pi++) {
var person = persons[pi];
rightOutput.appendChild(personTemplate.apply(person, true));
}
}
function leftTextChange() {
templates.find(function (val) { return val.name === leftList.value; }).template.html = leftText.value;
updateOutput();
}
function leftListChange() {
leftText.value = templates.find(function (val) { return val.name === leftList.value; }).template.html;
}
updateLists();
leftList.onchange = leftList.onkeyup = leftListChange;
leftText.onchange = leftText.onkeyup = leftTextChange;
leftListChange();
updateOutput();
Here the users input text is being interpreted live, while the user is watching. No security concerns, since it's all client side.

Passing references of a class to another class and using its methods

Let's say you're making a game. You want to try and not pollute the global scope and possibly limit the user's ability to easily alter the game (doubtful with client-side). You feel like modules might be unnecessary for your purposes. Is it bad practice to pass references to a class to another class during instantiation to access its methods?
Contrived example:
//game.js
var Game = (function () {
function Game() {
this.currentLevel = null;
this.score = 0;
}
Game.prototype.addScore = function (num) {
this.score += num;
};
Game.prototype.goToLevel = function (diff) {
this.currentLevel = new Level(this, diff);
};
Game.prototype.returnHome = function (level) {
this.currentLevel = null;
};
return Game;
})();
//level.js
var Level = (function () {
function Level(game, difficulty) {
this.game = game; //reference to game
this.difficulty = difficulty;
this.entities = [];
this.load();
}
Level.prototype.load = function () {
this.addEntity({name: 'tim', power: 23, difficulty: this.difficulty});
};
Level.prototype.leave = function () {
this.game.returnHome();
};
Level.prototype.addEntity = function (options) {
this.entities.push(new Entity(this, options));
};
Level.prototype.removeEntity = function (entity) {
for(var x = 0; x < this.entities.length; x++) {
if(this.entities[x] === entity) this.entities.splice(x, 1);
}
};
return Level;
})();
//level.js
var Entity = (function () {
function Entity(level, options) {
this.level = level; //reference to level
this.options = options;
}
Entity.prototype.kill = function () {
this.level.removeEntity(this); // anti-pattern?
this.level.game.addScore(34.53); // too closely coupled?
};
return Entity;
})();
//main.js
var Main;
(function (Main) {
var game = null;
function documentIsReady() {
start(); // Start the game
}
function start() {
game = new Game();
game.goToLevel('hard');
}
return {
documentIsReady: documentIsReady
}
})(Main || (Main = {}));
$(document).ready(function () {
Main.documentIsReady();
});
Forgive the half-baked example. If you end up with many instances of the 'Entity' class, do all the references to 'Level', though the same instance, start taking more memory? Are there other pitfalls? Another method would be to implement some kind of interface that you can access that allow classes to talk to each other.

Angular Scope in javascript function with Typescript

I use Typescript with Angular and Breezejs.
class CounterController {
count: number = 0;
static $inject = ['$scope'];
constructor($scope) {
$scope.vm = this;
}
setCount14(): void {
this.count = 14; // works
}
getQuestions(): void {
var manager = new breeze.EntityManager('/breeze/dbentities');
var query = breeze.EntityQuery.from("Corporations").where("Name", "startsWith", "Zen");
manager.executeQuery(query)
.then(querySucceeded);
function querySucceeded(data) {
this.count= 1; // works not!
}
}
}
How can i access the count property in the querySucceeded function correct?
Edit: better: there must be a way to pass a typescript function to executeQuery(query).then ?
Solution: Pass Typescript function as a Javascript function
Then calling the scope.$apply() does apply the bindings.
use (data) => { this.count = 1; } instead. or your "this" won't have the correct scope. OR as an alternative:
var me = this;
function querySucceeded(data) {
me.count= 1; // works not!
}
e.g:
getQuestions(): void {
var manager = new breeze.EntityManager('/breeze/dbentities');
var query = breeze.EntityQuery.from("Corporations").where("Name", "startsWith", "Zen");
manager.executeQuery(query)
.then((data) => { this.count= 1; });
}

Categories