I have a problem changing the value of a bound variable in Vue (I am not sure whether this is due to my lack of knowledge of Vue, or of JavaScript in general, probably both).
The initialization of the Vue instance is done via the code below
var vm = new Vue({
delimiters: ['<<', '>>'],
el: "#index-container",
data: {
posts: [],
tags: {},
},
methods: {
switchAll: function(what) {
console.log(what)
console.log(JSON.stringify(this.tags))
_.forEach(this.tags, function (v, k) {
console.log(k)
this.tags[k] = false
})
console.log(JSON.stringify(this.tags))
}
},
(...)
Further down the script, values of tags are set with a constuction like Vue.set(vm.tags, tag, true) to ensure reactivity. They are then used in constrictions such as
<div class="tag on" v-for="(content, name) in tags" v-show="tags[name]" v-on:click="tags[name] = false">
Clicking on this div does change the value of the relevant element of the Object tags (initialized previously).
So far everything works as expected.
The problem is in the method switchAll. When I use it with a similar v-on:click() element (switchAll(false) for instance), it gets fired up but the console output is the following:
false
{"hello":true,"world":true}
hello
world
{"hello":true,"world":true}
My problem is that the last line should be {"hello":false,"world":false}.
I tried to use this.tags[k] = false and this.tags.k = false (though both are supposed to be equivalent) and I believe that the issue I have is that (apologies for the wording, I am new to JS) the keys in this.tags are "bare" in my case, and not "quoted". What my operation does is to add a new key, this time "quoted" (but then this.tags.k = false should be fine, as k is either hello or world (without quotes).
I am lost - what is wrong in my code, and why the inline code in the first (working) example of the div is fine?
Not an expert in lodash, but it seems to be a problem with callback function creating new this. There are several ways around this problem, like:
If you're OK with ES6, use lambda functions - they won't create new this:
_.forEach(this.tags, (v, k)=>{
console.log(k)
this.tags[k] = false
})
It can be useful to hold your this inside some method-wide variable:
var vm = this;
_.forEach(vm.tags, (v, k)=>{
console.log(k)
vm.tags[k] = false
})
Use vanillaJS object loop:
for (var k in this.tags) {
vm.tags[k] = false
}
Related
New to Js, sorry if this is an obvious one.
I have some strings in my code that correspond to the names of variables. I'd like to put them into a function and have the function be able to make changes to the variables that have the same names as the strings.
The best example is where this 'string' is passed through from a data tag in html, but I have some other situations where this issue appears. Open to changing my entire approach too is the premise of my question is backwards.
<html>
<div data-location="deck" onClick="moveCards(this.data-location);">
</html>
var deck = ["card"];
function moveCards(location){
location.shift();};
Thanks!
A script should not depend on the names of standalone variables; this can break certain engine optimizations and minification. Also, inline handlers are nearly universally considered to be pretty poor practice - consider adding an event listener properly using Javascript instead. This will also allow you to completely avoid the issue with dynamic variable names. For example:
const deck = ["card", "card", "card"];
document.querySelector('div[data-location="deck"]').addEventListener('click', () => {
deck.shift();
console.log('deck now has:', deck.length + ' elements');
});
<div data-location="deck">click</div>
I think this can technically be done using eval, but it is good practice to think more clearly about how you design this so that you only access objects you directly declare. One example of better design might be:
container = {
obj1: //whatever this object is
...
objn:
}
function applyMethodToObject(object_passed){
container[object_passed].my_method();
}
I'm not sure I 100% follow what you're trying to do, but rather than trying to dynamically resolve variable names you might consider using keys in an object to do the lookup:
const locations = {
deck: ['card']
}
function moveCards (location) {
// if 'deck' is passed to this function, this is
// the equivalent of locations['deck'].shift();
locations[location].shift();
};
Here's a working demo:
const locations = {
deck: ['card 1', 'card 2', 'card 3', 'card 4']
};
function move (el) {
const location = el.dataset.location;
const item = locations[location];
item.shift();
updateDisplay(item);
}
// update the display so we can see the list
function updateDisplay(item) { document.getElementById('display').innerHTML = item.join(', ');
}
// initial list
updateDisplay(locations['deck']);
#display {
font-family: monospace;
padding: 1em;
background: #eee;
margin: 2em 0;
}
<div data-location='deck' onclick="move(this)">click to shift deck</div>
<div id="display">afda</div>
When you assign a value to an object in javascript you can access with dot or array notation. IE
foo = {};
foo.bar = "bar";
console.log(foo.bar);
console.log(foo["bar"]);
Additionally, global variables are added to the window object, meaning deck is available at window["deck"] or window[location] in your case. That means your moveCards function could do:
function moveCards(location) {
// perform sanity checks since you could change data-location="foo"
// which would then call window.foo.shift()
if (window[location]) {
window[location].shift();
}
}
That said, this probably isn't a great approach, though it's hard to say without a lot more context.
In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();
I have an object containing an array, which gets incremented after some amount of logic has completed.
Vue.js doesn't seem to be capturing this increment and displaying it to the view.
HTML:
<div id="demo">
<p>{{points}}</p>
</div>
JS:
function Board()
{
this.team1 = {pointsMade:[0]};
}
var newBoard = new Board();
newBoard.team1.pointsMade[0]++
new Vue({
el: '#demo',
data: {
points: newBoard.team1.pointsMade
}
})
setTimeout(newBoard.team1.pointsMade[0]++,1000)
I have a JSFiddle that outlines the problem.
You can see that after setTimeout(newBoard.team1.pointsMade[0]++,1000) runs, the value should be '2', but is only displayed at '1'. What am I missing here?
When you define
data: {
points: newBoard.team1.pointsMade[0]
}
the points variable is just assigned current value of newBoard.team1.pointsMade[0], which is 1 at this moment. There is no magic here. JS primitives works by value, not by references.
So, after updating the newBoard.team1.pointsMade[0] variable, the point variable is not updating of course.
To make this work, use object instead of primitive value. Objects work by reference in JS.
From Properties and Methods example:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// setting the property also affects original data
vm.a = 2
data.a // -> 2
// ... and vice-versa
data.a = 3
vm.a // -> 3
edit
There is another caveat here:
Due to limitations in JavaScript, Vue cannot detect when you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue. So, use Vue.set(newBoard.team1.pointsMade, 0, newBoard.team1.pointsMade[0] + 1);.
I updated your fiddle.
Code:
initialize: function() {
this.todos = [
{id: 100, text: 'Rich'},
{id: 200, text: 'Dave'}
];
},
activeTodos: function() {
this.todos = this.todos.length(function() {
return this.todos;
});
this.emitChange();
}
<p>Todo's Remaining: {this.activeTodos} </p>
activeItems: function(){
this.context.executeAction(activeToDosAction);
},
Explanation:
I am trying to print out the size of the array to the browser window (this can be seen in the <p> tags within the code). So far nothing is displaying and I cant figure out why. activeTodos should be calling the length of todos.
i can post more code if people require it. i am using reactjs hence the { } brackets within HTML
There are a couple of weird things there. If you only need to display the length of this.todos you can do something like this:
<p>Todo's Remaining: {this.todos.length} </p>
I think the problem here is that you pass a function to length. That is not required.
Your code should work if you change it to:
activeTodos: function() {
return this.todos.length;
}
I'm not sure what your function emitChange should be doing, so I omitted it for now in the code example. If you need it to be called, put it before the return as that is the last thing that will run in your function.
First, to access the length of an array you just have to do
myArray = [1, 2, 3];
myArray.length; //3
Moreover you have 2 fonctions : initialize and activeTodos where you use this.todos = ...
Without further explanations I assume these variables created with the keyword 'this' inside 2 different functions are out of score.
Verify that your this.todos refers to the same thing.
You do not need active.Todos() as .length is a built in function by itself.
Just do
Todo's Remaining: {this.todos.length}
Cheers.
What is the best way to view all jQuery data key-value pairs across every element (in jQuery 2.x)?
A selection-oriented approach ( e.g. $('*').data() ) obviously does not work, because the return value is tied to a single element.
I know that I can iterate over every element, checking each for data:
var allData = [];
$('html *').each(function() {
if($.hasData(this)) {
allData.push({ el: this, data: $(this).data() })
}
})
JSFiddle
This does produce the expected output, but iterating over each possible data key feels like a backwards approach to this problem.
Is there some way to find all element data directly?
N.B. I'm interested for debugging, not production code.
You could select every element within the body with $("body *") and apply jQuery's .filter() to it. Working example:
var $elementsContainingData $("body *").filter(function() {
if($.hasData(this)) return this;
});
console.log($elementsContainingData);
Edit
As #spokey mentioned before, there's an internal variable named "cache" within the jQuery object: $.cache.
This variable consists of a bunch of objects which contain keys like "data" or "events":
5: Object
data: Object
events: Object
handle: function (a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)}
__proto__: Object
You can iterate through that object and filter for the data:
var filteredCache = $.each($.cache,function() {
if(typeof this["data"] === "object") return this;
});
Here's an working example plus a function to merge that stuff into a single and more handy object consisting only of dataKey => dataValue pairings: Fiddle
Edit
As mentioned in comments this solution does not work in jQuery version 2.x since $.cache is deprecated.
My last suggestion is creating a hook for jQuerys data function in order to extend an own object$.dataCache = {}; each time data() is called.
Extending, replacing or adding jQuerys functions is done by accessing $.fn.functionName:
$.fn.data = function(fn,hook) {
return function() {
hook.apply(this,arguments);
return fn.apply(this,arguments);
}
}($.fn.data,function(key,value) {
var objReturn = {};
objReturn[key] = value;
$.extend($.dataCache,objReturn);
});
This also works great in jQuery version 2: Fiddle