Get index of array is not working - javascript

I am learning Vue and I am trying to access a string in an array by it's index, but I always get an error when trying to read the string. Here's my code:
var vm = new Vue({
el: '#top',
data: {
Cars: [],
DefaultCarList: [],
AddedCars: [],
SelectedCar: ''
},
methods: {
addCar: function(car) {
var addedCarCount = this.AddedCars.length;
var defaultCarCount = this.DefaultCarList.length;
var containsCar = function () {
for (var i = 0; i < addedCarCount; i++)
{
if (this.AddedCars[i] === car) // error here
{
return true;
}
}
return false;
}
var carIsValid = function() {
for(var i = 0; i < defaultCarCount; i++)
{
if(this.DefaultCarList[i] === this.SelectedCar) // error here
{
return true;
}
}
return false;
}
if (containsCar() === false && carIsValid){
this.AddedCars.push(car);
}
}
}
})
HTML:
<label for="carsId">Cars</label>
<select id="carsId" name="cars" v-model="SelectedCar">
<option disabled value="">Select</option>
<option v-for="car in DefaultCarList" :value="flavor">{{car}}</option>
</select>
<div>
<button type="button" class="hollow button success small"
v-on:click="addCar(SelectedCar)">Add Flavor</button>
</div>
Is it valid to iterate over an array like this in Vue and access the property by it's index? What is the correct way to do this?

Problem is with 'this' keyword it uses inner this where it doesn't have DefaultCarList variable, should use () => {} syntax .Error in this code
var carIsValid = function() {
for(var i = 0; i < defaultCarCount; i++)
{
if(this.DefaultCarList[i] === this.SelectedCar) // error here
{
return true;
}
}
return false;
}
should be
var carIsValid = () => {
for(var i = 0; i < defaultCarCount; i++)
{
if(this.DefaultCarList[i] === this.SelectedCar) // error here
{
return true;
}
}
return false;
}
and
var containsCar = () => {
for (var i = 0; i < addedCarCount; i++)
{
if (this.AddedCars[i] === car) // error here
{
return true;
}
}
return false;
}

The problem is that this it's not a reference to your model.
In your example this is a reference to window object.
Have a look here in order to understand the scope of this keyword in javascript.
You should use arrow functions.
var containsCar = () => {
for (var i = 0; i < addedCarCount; i++)
{
if (this.AddedCars[i] === car) // error here
{
return true;
}
}
return false;
}
or you could just define a self variable.
var self=this;
var containsCar = function() {
for (var i = 0; i < addedCarCount; i++)
{
if (self.AddedCars[i] === car) // error here
{
return true;
}
}
return false;
}
Further more, I recommand you to use native javascript functions in order to have a clean code.
var containsCar = function () {
for (var i = 0; i < addedCarCount; i++)
{
if (this.AddedCars[i] === car) // error here
{
return true;
}
}
return false;
}
var carIsValid = function() {
for(var i = 0; i < defaultCarCount; i++)
{
if(this.DefaultCarList[i] === this.SelectedCar) // error here
{
return true;
}
}
return false;
}
can be achieved using some method :
The some() method tests whether at-least one element in the array
passes the test implemented by the provided function.
var containsCar = () => {
return this.AddedCars.some(a=>a==car);
}
var carIsValid = () => {
return this.DefaultCarList.some(a=>a === this.SelectedCar);
}

Related

JavaScript: searching for a simple way of returning 'this' instead of 'val=x'

The following function returns val=ret instead of this. It is complicated and not clear:
getElement: function (nodeId) {
var ret = null;
if (nodeId === this._nodeId) {
ret = this;
} else {
for (var i = 0; i < this._selects.length; i++) {
ret = this._selects[i].getElement(nodeId);
if (ret) {
break;
}
}
}
return (ret);
},
Could you suggest an easier way for that? I tried the following, but you can't
do true/false with if(this._pages[i].getElement(nodeId):
getElement: function (nodeId) {
for (var i = 0; i < this._pages.length; i++) {
if(this._pages[i].getElement(nodeId){
return this;
}
}
return null;
},
I think you should return
this._pages[i].getElement(nodeId)
The second is not the same, because the first check is missing. (No need for an else part if a return is in the then part.)
getElement: function (nodeId) {
var i, ret;
if (nodeId === this._nodeId) {
return this;
}
for (i = 0; i < this._selects.length; i++) {
ret = this._selects[i].getElement(nodeId);
if (ret) {
return ret;
}
}
return null;
},

Custom Javascript library: Add event listener to dynamically added html

I'm trying to build a Javascript library like jQuery just to learn Javascript more. So far, I've developed this:
window.jsLib = function (selector) {
var about = {
Version: 0.1
};
if (selector) {
if (window === this) {
return new jsLib(selector);
}
if (typeof selector === 'string') {
var nodes = document.querySelectorAll(selector);
for (var i = 0; i < nodes.length; i++) {
this[i] = nodes[i];
}
this.length = nodes.length;
} else if (typeof selector === 'object') {
this[0] = selector;
this.length = 1;
}
return this;
} else {
return about;
}
};
And for methods, I've developed some like:
jsLib.fn = jsLib.prototype = {
css: function (key, value) {
if (value !== undefined) {
for (var i = 0; i < this.length; i++) {
this[i].style[key] = value;
}
return this;
} else {
for (var i = 0; i < this.length; i++) {
return this[i].style[key];
}
}
},
html: function (value) {
if (value !== undefined) {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = value;
}
return this;
} else {
for (var i = 0; i < this.length; i++) {
return this[i].innerHTML;
}
}
},
on: function (type, callback) {
console.log(window.event);
for (var i = 0; i < this.length; i++) {
this[i].addEventListener(type, callback, false);
}
return this;
},
trigger: function (type) {
var event = new Event(type);
for (var i = 0; i < this.length; i++) {
this[i].dispatchEvent(event);
}
return this;
},
append: function(value) {
var old = this.html();
this.html(old + '' + value);
return this;
}
};
You may have noted that I've defined a method on like jQuery.
Whenever I'm calling like jsLib('div#foo').on('click', someFunc);, it is working fine.
But, suppose I have appended some html like jsLib('body').append('<a id="#bar" href="#">Click</a>');
And then I want to provide an API to add event listener to #bar like jsLib('body').on('click', '#bar', someOtherFunc);.
But I'm not sure how to implement this listener.
Kindly help.
From your comments I suppose you request a live implementation?
If that is the case, i wold suggest you to add a data method to your object, remember all events to be registered and register them from append method, when content is appended to the current element.
I extended your library with the .data and .live methods and queued an event registration for the next span to be added in body. See the modified code snippet and check out the console to validate.
window.jsLib = function (selector) {
var about = {
Version: 0.1
};
if (selector) {
if (window === this) {
return new jsLib(selector);
}
if (typeof selector === 'string') {
var nodes = document.querySelectorAll(selector);
for (var i = 0; i < nodes.length; i++) {
this[i] = nodes[i];
}
this.length = nodes.length;
this._selector = selector;
} else if (typeof selector === 'object') {
this[0] = selector;
this.length = 1;
}
return this;
} else {
return about;
}
};
jsLib.fn = jsLib.prototype = {
css: function (key, value) {
if (value !== undefined) {
for (var i = 0; i < this.length; i++) {
this[i].style[key] = value;
}
return this;
} else {
for (var i = 0; i < this.length; i++) {
return this[i].style[key];
}
}
},
html: function (value) {
if (value !== undefined) {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = value;
}
return this;
} else {
for (var i = 0; i < this.length; i++) {
return this[i].innerHTML;
}
}
},
on: function (type, callback) {
for (var i = 0; i < this.length; i++) {
this[i].addEventListener(type, callback, false);
}
return this;
},
trigger: function (type) {
var event = new Event(type);
for (var i = 0; i < this.length; i++) {
this[i].dispatchEvent(event);
}
return this;
},
append: function(value) {
var old = this.html(),
pendingEvents = this.data('jsLib_Future_Events') || [],
registered = {};
this.html(old + '' + value);
// Attach pending events to newly added childs (if any match found)
pendingEvents.forEach(function (evConf, i) {
[].slice.call(jsLib(this._selector + ' ' + evConf.selector), 0).forEach(function (el) {
jsLib(el).on(evConf.type, evConf.callback);
registered[i] = true;
});
}.bind(this));
// Clear list of pending requests of any registered events
this.data('sLib_Future_Events', pendingEvents.filter(function (evConf, i) { return !!registered[i]; }));
return this;
},
_data: {},
data: function (key, value) {
if (arguments.length > 1) this._data[key] = arguments[1]; // Setter
return key ? this._data[key] : this._data; // Getter of key or all
},
live: function (type, selector, callback) {
this.data('jsLib_Future_Events', (this.data('jsLib_Future_Events') || []).concat({type: type, selector: selector, callback: callback}));
return this;
}
};
jsLib('body').live('click', 'span', function () { console.debug('event triggered on appendend content after live registration of event handle'); });
jsLib('body').append('<br><span>dynamic content</span>');
<div>existing content</div>
Considerations:
you will have to make a similar implementation for html method
if you want to make a real live clone you will have to keep pre-registered event definitions and register any new elements matching the selector (at this moment once an element matches the selector and the event is registered the event definition is removed in append - to make it universal you will have to mark your bound elements, use data for this)

Infinite loop in list iterator

I created a list iterator but when trying traverse a list backward the loop runs infinitely. What I did wrong?
function List() {
this.listSize=0;
this.pos=0;
this.dataStore =[];
this.append = append;
this.currPos = currPos;
this.end = end;
this.front = front;
this.length = length;
this.moveTo = moveTo;
this.next = next;
this.prev = prev;
}
function append(element) {this.dataStore[this.listSize++]=element;}
function currPos() {return this.pos;}
function end() {this.pos = this.listSize-1;}
function front() {this.pos =0;}
function length() {return this.listSize;}
function moveTo(position) {this.pos = position;}
function prev() {if(this.pos > 0) --this.pos;}
function next() {if(this.pos < this.listSize) ++this.pos;}
var names = new List();
names.append("A"); names.append("B"); names.append("C");
for(names.end(); names.currPos() >= 0; names.prev()) {console.log(names.getElement());}
Your loop only terminates when the current list position is less than zero, but your .prev() function won't allow that to happen.
To fix it? Well, that's a matter of opinion, but if you're going to the trouble of implementing a list class you might as well make a native .forEach function:
function forEach(callback) {
for (var i = 0; i < this.listSize; ++i)
callback(this.dataStore[i], i);
}
Then you can do:
names.forEach(function(name) { console.log(name); });
I ran into a similar problem when trying to implement a list ADT from the book "Data Structures and Algorithms" and came to find out that the author re-wrote that section in later versions to look like this:
module.exports = List;
function List() {
this.listSize = 0;
this.pos = 0;
this.dataStore = [];
this.clear = clear;
this.find = find;
this.toString = toString;
this.insert = insert;
this.append = append;
this.remove = remove;
this.front = front;
this.end = end;
this.prev = prev;
this.next = next;
this.length = length;
this.currPos = currPos;
this.moveTo = moveTo;
this.getElement = getElement;
this.length = length;
this.contains = contains;
this.hasNext = hasNext;
this.hasPrevious = hasPrevious;
this.insertIf = insertIf;
}
function append(element) {
this.dataStore[this.listSize++] = element;
}
function find(element) {
for (var i = 0; i < this.dataStore.length; ++i) {
if (this.dataStore[i] === element) {
return i;
}
}
return -1;
}
function remove(element) {
var foundAt = this.find(element);
if (foundAt > -1) {
this.dataStore.splice(foundAt, 1);
--this.listSize;
return true;
}
return false;
}
function length() {
return this.listSize;
}
function toString() {
return this.dataStore;
}
function insert(element, after){
var insertPos = this.find(after);
if(insertPos > -1){
this.dataStore.splice(insertPos+1, 0, element);
++this.listSize;
return true;
}
return false;
}
function clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
function contains(element) {
for (var i = 0; i < this.dataStore.length; ++i) {
if(this.dataStore[i] === element) {
return true;
}
}
return false;
}
function front() {
this.pos = 0;
}
function end() {
this.pos = this.listSize-1;
}
function prev() {
return this.dataStore[--this.pos];
}
function next(){
return this.dataStore[this.pos++];
}
function hasNext(){
if (this.pos > this.listSize -1) {
return false;
} else {
return true;
}
}
function hasPrevious() {
if(this.pos <= 0) {
return false;
} else {
return true;
}
}
function currPos() {
return this.pos;
}
function moveTo(position) {
this.pos = position;
}
function getElement() {
return this.dataStore[this.pos];
}
function insertIf(element) {
var greaterThan = true;
for(this.front(); this.hasNext(); ){
if(this.next() > element) {
greaterThan = false;
break;
}
}
console.log(element);
if(greaterThan){
this.append(element);
return true;
} else {
return false;
}
}
Your loops will then look like this:
for (list.front(); list.hasNext();) {
var listItem = list.next();
if(listItem instanceof Customer) {
console.log(listItem.name + ", " + listItem.movie);
} else {
console.log(listItem);
}
}
This has proven to be a much more reliable implementation so you may want to consider switching to this.
You need to change your for loop to:
for(names.end(); names.currPos() > 0; names.prev())

Javascript Array indexFirst

I build a prototype that handle pages, I successfully add (push), but can get the data, I failed:
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
Here the javascript page handler:
var Pages = new Array();
PageContainer = function () //constructor for the proxy
{
// this._baseURL = url;
};
PageContainer.prototype =
{
AddPage: function (data) {
if (data == null) return;
Pages.push({ PageID: data.PageID, SegmentID: data.SegmentID });
},
GetPage: function (PageID) {
alert('getPage('+PageID+')=' + JSON.stringify(Pages));
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
var dt = { PageID: Pages[foundImageIndex].PageID, SegmentID: Pages[foundImageIndex].SegmentID };
return dt;
}
};
I call from other js as following:
var gPageContainer = new PageContainer();
for (var i = 0; i < SegStruct.SegmentsCount; i++) {
var segRClass = //get from webservice
gPageContainer.AddPage({ PageID: i, SegmentID: segRClass.SegmentID });
}
I trying to call: gPageContainer.GetPage(1); but it failed in GetPage: function (PageID) it returns -1 in:
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
foundImageIndex always -1
why?
Simply add the following before the constructor:
if (typeof Array.prototype.indexFirst == 'undefined') {
Array.prototype.indexFirst = function (validator) {
for (var i = 0; i <= this.length - 1; i++) {
if (validator(this[i])) {
return i;
}
}
return -1;
};
}

Dyanamic Execution of javascript code

I have the following JavaScript:
var djs = function (ob) {
return {
remove: function () { //removes element
if (is_array(ob)) {
for (var i = 0; i < ob.length; i++)
ob[i].parentNode.removeChild(ob[i]);
} else {
ob.parentNode.removeChild(ob);
}
},
empty: function () { //makes element empty
if (is_array(ob)) {
for (var i = 0; i < ob.length; i++)
ob[i].innerHTML = "";
} else {
ob.innerHTML = ""
}
},
html: function (str) { //gets or sets innerHTML
if (str) {
if (is_array(ob)) {
for (var i = 0; i < ob.length; i++)
ob[i].innerHTML = str;
} else {
ob.innerHTML = str;
}
} else {
if (is_array(ob)) {
for (var i = 0; i < ob.length; i++)
rob += ob[i].innerHTML;
return rob;
} else {
return ob.innerHTML;
}
}
}
}
}
Here every time I am checking whether ob is an array or not and executing code. I want to minimize this, like instead of:
if (is_array(ob)) {
for (var i = 0; i < ob.length; i++)
ob[i].parentNode.removeChild(ob[i]);
} else {
ob.parentNode.removeChild(ob);
}
I want to use a function like, doEval(ob,code,return), in this case,
doEval(ob,"parentNode.removeChild("+ob+")",NULL);
"return" parameter will return if I specify any like innerHTML. Can any one help?
Don't repeat is_array check:
var djs=function(ob) {
if (!is_array(ob)) ob = [ob];
#SHiNKiROU is right of course, but just to provide an example of how to solve your problem with higher-order functions:
function doToAll(ob, callback) {
if(is_array(ob)) {
for (var i = 0; i < ob.length; i++) {
callback(ob[i]);
}
} else {
callback(ob);
}
}
...
remove:function(){ //removes element
doToAll(ob, function(actualOb) { actualOb.parentNode.removeChild(actualOb); });
},
...
But again, use #SHiNKiROU:s answer for this particular case.
Try this:
function doEval(a, b, c) {
if(is_array(a)) {
eval(b);
} else {
eval(c);
}
}
NULL doesn't exist by the way, it is null.

Categories