Call method overridden in child class, from the parent class [PHP -> JS] - javascript

In my Javascript there is a parent Base class that will be extended by others.
I'd like to:
define in it a method getSubject() that could be common to all children, when it is not overridden.
make getSubject() rely on a Base property, that eventually could be overridden as well.
always call the getSubject() method in the context of the caller (the children classes or the Base class)
To clarify (hopefully) what I want to do..
I wrote (non-valid) PHP code as an example.
<?php
class Base
{
const SUBJ_SELECTOR = 'input';
public function init()
{
$this->wrapper = ....;
$this->subject = $this->getSubj();
if ($this->subject.attr('data-active')) {
// ... do stuff
}
}
public function getSubj() // One definition in parent
{
return $this->wrapper.find(self::SUBJ_SELECTOR);
}
}
class Select extends Base
{
const SUBJ_SELECTOR = 'select' // Override just the selector
}
class Textarea extends Base
{
const SUBJ_SELECTOR = 'textarea[name=foo]';
public function getSubj() // Eventual overriding
{
$subjs = $this->wrapper.find(self::SUBJ_SELECTOR);
foreach ($subjs as $subj) {
if ($subj.attr('multiline')) {
return $subj;
}
}
return $subjs;
}
}
I'd like to achieve the same result with Javascript (and JQuery eventually).
Actually I wrote some code (that I still didn't test) as a sketch:
var Base = function() {
this.options = {};
this.subject_selector = 'input';
this.wrapper = $('.container');
};
Base.prototype.getSubject = function() {
return this.wrapper.find(this.subject_selector);
}
Base.prototype.init = function() {
subj = this.getSubject();
if(subj.attr('data-active')) {
// ... do stuff
}
}
var Select = function() {
this.subject_selector = 'select';
}
Select.prototype = new Base();
Select.prototype.constructor = Select;
var Textarea = function() {
this.subject_selector = 'textarea';
}
Textarea.prototype.getSubject = function() {
subjs = this.wrapper.find(this.subject_selector);
for (var i = subjs.length - 1; i >= 0; i--) {
if(subjs[i].attr('multiline')) {
return subjs[i];
}
};
return subjs;
}
Textarea.prototype = new Base();
Textarea.prototype.constructor = Textarea;
Would it work correctly? Is this a proper use of the inheritance model?
Am I callling the method in the right way and will I get the expected result when executing the init() method?

Related

I want to modify all elements with the same query

I want to modify all elements that use the same query via a function. (The function is not changed.)
r is read, w is write, and a is write appended to existing content. When using this as get(query).r , .w , .a , I want to make all elements corresponding.
For example, if the class of 3 p tags is html, if you write get(".html").w("hello") in js, all tags with the class of 3 html should be changed to 'hello'.
function get(id){
if (id) {
if (window === this) {
return new get(id);
}
var query = document.querySelectorAll(id)
for(var i = 0; i < query.length; i++) {
this.e = query[i];
return this;
}
} else {
return "getError : The attribute and value of a tag such as id or class specified by get does not exist. ";
}
}
get.prototype = {
r: function () {
return this.e.innerText;
},
w: function (writing) {
return this.e.innerText = writing;
},
a: function (writing) {
return this.e.innerText = this.e.innerText += writing;
}
};
js
<p href="#" class="test">html</p>
test
<script>
get(".test").w("hello")
</script>
For this line
for(var i = 0; i < query.length; i++) {
this.e = query[i];
return this;
}
You are only returning the first element. Thus, it only changes the inner text of the first element.
It seems that you are trying to assign a prototype to HTML elements, which is not recommended, so I modified your code.
I changed it so that the get function will return the whole HTMLCollection instead. Then I used the prototype of the get function to loop through the collection and set the text of the HTML elements.
js
<p href="#" class="test">html</p>
test
<script>
function get(id){
if (id) {
if (window === this) {
return new get(id);
}
var query = document.querySelectorAll(id)
return query
} else {
return "getError : The attribute and value of a tag such as id or class specified by get does not exist. ";
}
}
get.prototype = {
r: function () {
return this.e.innerText;
},
w: function (elems, writing) {
return elems.forEach(x => x.innerText = writing);
},
a: function (writing) {
return this.e.innerText = this.e.innerText += writing;
}
};
var elems = get(".test")
get.prototype.w(elems, "html")
</script>
You can also do this with a newer ES2015 class method, that uses static methods. Static methods are called directly on the class, without creating an instance/object of the class. That way, you can call the static method and pass the class object back into those methods.
More info here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
https://www.w3schools.com/JsrEF/jsref_class_static.asp
See snippet below:
`use strict`
class Get {
constructor(selector) {
if (selector) return Array.from(document.querySelectorAll(selector));
return [];
}
static Read(elements) {
return elements.map(e => e.innerText)
};
static Write(elements, string) {
elements.forEach(e => e.innerText = string)
};
static Append(elements, string) {
elements.forEach(e => e.innerText = `${e.innerText}${string}`)
};
}
console.log(Get.Read(new Get(`.test`)));
Get.Append(new Get(`.test`), `: Appended Text`);
js
<p href="#" class="test">html</p>
test

I have a problem with 2 Classes to create element on page and display it

So I made 2 classes,first is the create element one:
class CElement {
constructor(value,elementType,elementStyle){
this.element = document.createElement(elementType);
this.element.innerHTML = value;
this.element.style = elementStyle;
}
display(displayTo) {
document.getElementsByClassName(displayTo).appendChild(element);
}
}
The second is the create mob and diplay to:
class Mob {
constructor(level) {
this.mobLvl = level;
}
display(elementClass) {
ele = new CElement(this.mobLvl + "<br>",'p',"color:red;");
ele.display(elementId);
}
}
I checked my code online for syntax errors , and I don't have any?????
So why doesn't it work when I call:
var mob = new Mob(1,"div","color:red;");
mob.display("someClassName");
You have a couple of issues in your code:
You forgot this in the display() function.
getElementsByClassName() returns an array, so you can't use appendChild() directly, you have to loop over your array or use another selector, for example querySelector().
ele is not defined in your display() function.
You use elementClass as argument of the display() function but then use elementId inside your function.
Finally, a working version could be something like this, that you can of course adapt to your need:
class CElement {
constructor(value, elementType, elementStyle) {
this.element = document.createElement(elementType);
this.element.innerHTML = value;
this.element.style = elementStyle;
}
display(displayTo) {
document.querySelector(displayTo).appendChild(this.element);
}
}
class Mob {
constructor(level) {
this.mobLvl = level;
}
display(elementClass) {
var ele = new CElement(this.mobLvl + "<br>", 'p', "color:red;");
ele.display(elementClass);
}
}
var mob = new Mob(1, "div", "color:red;");
mob.display(".someClassName");
<div class="someClassName"></div>
Looks like you forgot the this. in your CElement.display function. This makes it so that the element variable is undefined, and thus it doesn't append anything to the document.

Ways to access an object that triggered a breakpoint?

In chrome console, I have set some breakpoints in code of the sources. Those are set in a class for which an instance of this class was instantiated at loadtime. :
var SearcherBlock = function(n) {
function t() {
n.apply(this, arguments);
this.CurrentData = null;
this.onInput = null;
this.SearchInput = document.getElementById(this.BlockUniqueID + "_SearchInput");
this.SearchResults = document.getElementById(this.BlockUniqueID + "_Search_Results");
this.SearchResultsTitle = document.getElementById(this.BlockUniqueID + "_ResultsTitle");
this.onDocumentClick = this.onDocumentClickHandler.bind(this);
this.MinSearchStringLength = SearchController.getMinSearchStringLength(this.Configuration.LanguagesFor2SymbolSearch)
}
var i = {
destroyed: !0
};
return $.extend(t.prototype, n.prototype),
t.prototype.activate = function() {
n.prototype.activate.call(this);
this.onInput = this.textChange.bind(this);
this.SearchInput && this.SearchInput.addEventListener("input", this.onInput, !1);
this.tooltipsInit()
}
t.prototype.textChange = function(n) {
var t = n.srcElement || n.target;
if (this.SearchResultsTitle.innerHTML) {
... }
}
}
So now when I am at a breakpoint in this class whose instance I would like to achieve is triggered in this class. I would like to figure out a way to access this instance, like window.something.something - how can I do this if possible?
The reason is that when I call the function hasText() by window.hasText(), it complains because all properties of the this that is being used in the function are undefined. Therefore, I would like to have a way of accessing the instantiated object.

How to dynamically set initial value for a vanilla javascript MVC component?

I'm trying out a MVC approach to using vanilla JS components.
However, with the following setup I can't update the view depending on the model.
Everything is going well until the code reaches the view constructor. There, the view will have to be updated depending on a value specified in the model, as pushed through via the controller.
I do get the value through. However, the HTML template isn't updated.
Now the following code example shows a lot of boilerplate code. That's necessary to get a functionally working example. The pain point seems to be mainly in the TestView class.
// unrelated helper function
const stringToHTMLCollection = function(string) {
const template = document.createElement('template');
string = string.trim(); // Never return a text node of whitespace as the result
template.innerHTML = string;
return template.content.children;
}
class TestModel {
constructor(data) {
this._firstValue = data.first;
}
get firstValue() {
return this._firstValue;
}
}
class TestController {
constructor(model) {
this.model = model;
}
get firstValue() {
return this.model.firstValue;
}
}
class TestView {
static htmlString() {
return `<div>
<ul>
<li id="first-list-item">0</li>
<li>0</li>
</ul>
</div>`
}
constructor(controller) {
this.controller = controller;
this.testView = TestView.htmlString();
this.list = stringToHTMLCollection(this.testView)[0];
this.listItems = this.list.getElementsByTagName('li');
this.init();
}
init() {
// this is going well, output: 100
console.log(this.controller.firstValue);
Object.entries(this.listItems).forEach(([key, price]) => {
if (price.id === 'first-list-item') {
price.innerHTML = this.controller.firstValue;
}
});
// this is going well, output element with innerText of 100
console.log(this.listItems[0]);
}
renderInto(targetNode) {
if(!targetNode) return;
targetNode.insertAdjacentHTML('beforeend', this.testView);
}
}
const resultElement = document.getElementById('result');
function testComponent(pricing) {
const testModel = new TestModel(pricing),
testController = new TestController(testModel);
return new TestView(testController);
}
const pricing = {
first: 100,
second: 200
}
testComponent(pricing).renderInto(result);
<div id="result"></div>
Ah, I guess the stringToHTMLCollection actually creates a new instance. Using it as a definition in the constructor and then returning that one after the init solves it.
Example:
// unrelated helper function
const stringToHTMLCollection = function(string) {
const template = document.createElement('template');
string = string.trim(); // Never return a text node of whitespace as the result
template.innerHTML = string;
return template.content.children;
}
class TestModel {
constructor(data) {
this._firstValue = data.first;
}
get firstValue() {
return this._firstValue;
}
}
class TestController {
constructor(model) {
this.model = model;
}
get firstValue() {
return this.model.firstValue;
}
}
class TestView {
static htmlString() {
return `<div>
<ul>
<li id="first-list-item">0</li>
<li>0</li>
</ul>
</div>`
}
constructor(controller) {
this.controller = controller;
this.testView = TestView.htmlString();
this.html = stringToHTMLCollection(this.testView);
this.list = this.html[0];
this.listItems = this.list.getElementsByTagName('li');
this.init();
}
init() {
// this is going well, output: 100
console.log(this.controller.firstValue);
Object.entries(this.listItems).forEach(([key, price]) => {
if (price.id === 'first-list-item') {
price.innerHTML = this.controller.firstValue;
}
});
// this is going well, output element with innerText of 100
console.log(this.listItems[0]);
}
renderInto(targetNode) {
if(!targetNode) return;
targetNode.insertAdjacentElement('beforeend', this.html[0]);
}
}
const resultElement = document.getElementById('result');
function testComponent(pricing) {
const testModel = new TestModel(pricing),
testController = new TestController(testModel);
return new TestView(testController);
}
const pricing = {
first: 100,
second: 200
}
testComponent(pricing).renderInto(result);
<div id="result"></div>

Can not use class in a function as a parameter (JScript)

I am trying to do a small JavaScript Lab. In the lab, first I created an Animal object :
function Animal(species, nature) {
this.species = species;
this.nature = nature;
var preys = new Array();
Animal.prototype.getSpecies = function() {
return species;
}
Animal.prototype.getNature = function() {
return nature;
}
Animal.prototype.getPreys = function () {
return preys;
}
Animal.prototype.setNature = function (newNature) {
nature = newNature;
}
Animal.prototype.setSpecies = function (newSpecies) {
species = newSpecies;
}
Animal.prototype.setPrey = function (newPreys) {
preys = newPreys;
}
}
Then, I created a World object which will basically store a number of animal object and separate them according to their nature.
/// <reference path="Animal.js" />
function World() {
var animals = new Array();
animals.push(new Animal("Wolf", "Carnivore"));
animals.push(new Animal("Crocodile", "Carnivore"));
animals.push(new Animal("Sheep", "Omnivore"));
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores());
}
function getOmnivores(animal) {
}
}
In my getOmnivors function, I can not use the Animal class as a variable. It is a little bit complicated for me cause I am new in JavaScript and regardless of their types we are using var keyword (Or not using in some places such as parameters in functions).
What did I do wrong and how can I fix it? I could not reach the Animal class in my private function getOmnivores. I think program does not understand that it is the class called Animal
I hope I explained well. Have a nice day!
EDIT
Error picture :
Animal is the Class name, you don't need it there. When using filter each element of the array is automatically passed on to the callback function as the first parameter of that function.
Since each element of the array is an instance of the class Animal you can use it straight away.
Also, the syntax {ClassName}.Prototype.{functionName} should not be used within that same Class, because by the time the interpreter reaches that line the Animal Class has not yet been defined. That syntax is used on already existing and defined classes. Use this.{functionName} instead.
function Animal(species, nature) {
this.species = species;
this.nature = nature;
this.preys = new Array();
this.getSpecies = function() {
return this.species;
}
this.getNature = function() {
return this.nature;
}
this.getPreys = function () {
return this.preys;
}
this.setNature = function (newNature) {
this.nature = newNature;
}
this.setSpecies = function (newSpecies) {
this.species = newSpecies;
}
this.setPrey = function (newPreys) {
this.preys = newPreys;
}
}
function World() {
var animals = new Array();
animals.push(new Animal("Wolf", "Carnivore"));
animals.push(new Animal("Crocodile", "Carnivore"));
animals.push(new Animal("Sheep", "Omnivore"));
this.getOmnivores = function () {
return animals.filter(this.filterOmnivores);
}
this.filterOmnivores= function(animal) {
return animal.getNature()=='Omnivore';
}
}
myworld = new World();
console.log(myworld.getOmnivores());
A working fiddle at https://jsfiddle.net/47dyg1q9/
The filter method takes a function as a paramter.
You must provide the function but in your code you are instantly calling the function:
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores());
}
Remove the parentheses to provide just the function without calling it, or insert an anonymous function:
World.prototype.getOmnivores = function () {
return animals.filter(getOmnivores);
}
// or
World.prototype.getOmnivores = function () {
return animals.filter(function (animal) {
return animal.nature === "omnivore";
});
}
You need to pass the function as an argument, not what it returns.
return animals.filter(isOmnivore);
And isOmnivore becomes
function isOmnivore(animal) {
animal.nature == 'Omnivore';
}

Categories