I am working on the stencil platform on big commerce. this platform uses the handlebars syntax. I need to be able to set a value based on one of the parameters in my URL, more that like the 'window.location.pathname', and i need to be able to access this new variable across the site. I am able to make something work two different ways using regular JavaScript, but i do not want to recreate my script in every place throughout the site. So basically, I could use some help getting one of my 2 vanilla scripts into a handlebars for formatting. What i have that works is shown below:
<p id="BrandLogo"></p>
<script>
var directory = window.location.pathname;
var branding;
var str = directory.includes("blackvue");
if (str === false) {
branding = value one;
} else {
branding = value 2
}
document.getElementById("BrandLogo").innerHTML = branding;
</script>
or
<p id="BrandLogo"></p>
<script>
var directory = window.location.pathname;
var branding;
if (str == '/URL-I-Want-To-Focus-On/') {
branding = value one;
} else {
branding = value 2
}
document.getElementById("BrandLogo").innerHTML = branding;
</script>
Thanks in advance
If you are trying to set and use a local variable within handlebars try something like this:
First, create a helper function named 'setVar':
var handlebars = require('handlebars');
handlebars.registerHelper("setVar", function(varName, varValue, options) {
options.data.root[varName] = varValue;
});
Next, set your variable(s) using the function you created in the first step.
{{setVar "greeting" "Hello World!"}}
{{#if some-condition}}
{{setVar "branding" "Value one"}}
{{else}}
{{setVar "branding" "Value 2"}}
{{/if}}
Finally, use the variable(s) on your page:
<div>
<h1 id="BrandLogo">{{greeting}}</h1>
</div>
document.getElementById("BrandLogo").innerHTML = {{branding}};
If you are trying to add a serial number inside a nested 'each' or based on a condition inside nested each following helper works good:
hbs.registerHelper('serialNo', function (options) {
var currentSerialNo = options.data.root['serialNo'];
console.log("############Current serial No is:"+currentSerialNo);
if (currentSerialNo === undefined) {
currentSerialNo = 1;
} else {
currentSerialNo++;
}
options.data.root['serialNo'] = currentSerialNo;
return currentSerialNo;
});
Now inside your template you can simply use it like:
{{serialNo}}
Everytime {{serialNo}} is encountered it prints a serial number one greater than before.
Related
const data = [{color : "red"},{color : "blue"}, {color : "green"} ];
function libraryRoot() {
load();
return (`<div id="appDiv">
${data.map(function(value){
return `<div><p>Color ${value.color} from libraryRoot</p>`
}).join("")}
</div>
`);
}
window.onload = libraryRoot;
function load() {
let a = document.getElementById("appDiv");
console.log(a);
}
let defaultLayout = libraryRoot();
document.getElementById("root").innerHTML = defaultLayout;
<div>
<div id="root"></div>
</div>
Hi Guys i modified the script as you guys suggested, but still the return value at the first instance prints null, and then it prints the div.can you guys help me where im going wrong.
All i wanted to do is i want to call the "appDiv" id and wirte a button funcion to it. like on click {//do something}.
updated Codepen Project
You won't be able to access the element until it's written to the DOM.
Notice the word document in document.getElementById(). It's a method of the document API.
DOM stands for Document Object Model.
If you want to modify it before then then split your string literal into different pieces. Assign specific variables to important parts of the element.
Then modify them. Concatenate them back into one string and add them to the DOM.
Your code could use some comments. It's a little unclear what you're trying to do.
Here I've modified your pen to show some different ways to handle each color in your data array. You can do some conditional logic and return different template strings based on that. You could easily pass data object properties to another javascript function using the onclick attribute.
Hopefully this helps you get closer to your goal
const data = [{color : "red"},{color : "blue"}, {color : "green"} ];
function alertFunction(color) {
alert(color);
}
function libraryRoot(){
return('<div id="appDiv">' +
data.map(function(value) {
var result = `<div><p>Color ${value.color} from libraryRoot</p>`;
if (result.indexOf("red") > -1) { // checking if the div includes red
return result;
} else if (`${value.color}` == "blue") { // checking if the objects color prop includes blue
return "<p>blue</p>";
} else {
return `<button onclick="alertFunction('${value.color}')">Button with function</button>`;
}
}).join("")
+ "</div>"
);
}
window.onload = libraryRoot;
document.getElementById("root").innerHTML = libraryRoot();
<div>
<div id="root"></div>
</div>
The first issue is that you are calling load() first, which is trying to access the appDiv node that doesn't exist yet. So rethink the order on that.
The second issue is that you never actually added the appDiv content to the DOM. You can use things like document.appendChild(libraryRoot()); or something like that, though I have never injected a string as a DOM node - I use createElement for that. The link has some examples.
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.
Update: scroll to see my solution, can it be improved?
So I have this issue, I am building a word translator thats translates english to 'doggo', I have built this in vanilla JS but would like to do it React.
My object comes from firebase like this
dictionary = [
0: {
name: "paws",
paws: ["stumps", "toes beans"]
}
1: {
name: "fur",
fur: ["floof"]
}
2: {
name: "what"
what: ["wut"]
}
]
I then convert it to this format for easier access:
dictionary = {
what : ["wut"],
paws : ["stumps", "toe beans"],
fur : ["floof"]
}
Then, I have two text-area inputs one of which takes input and I would like the other one to output the corresponding translation. Currently I am just logging it to the console.
This works fine to output the array of the corresponding word, next I have another variable which I call 'levelOfDerp' which is basically a number between 0 - 2 (set to 0 by default) which I can throw on the end of the console.log() as follows to correspond to the word within the array that gets output.
dictionary.map(item => {
console.log(item[evt.target.value][levelOfDerp]);
});
When I do this I get a "TypeError: Cannot read property '0' of undefined". I am trying to figure out how to get past this error and perform the translation in real-time as the user types.
Here is the code from the vanilla js which performs the translation on a click event and everything at once. Not what I am trying to achieve here but I added it for clarity.
function convertText(event) {
event.preventDefault();
let text = inputForm.value.toLowerCase().trim();
let array = text.split(/,?\s+/);
array.forEach(word => {
if (dictionary[word] === undefined) {
outputForm.innerHTML += `${word} `;
noTranslationArr.push(word);
} else {
let output = dictionary[word][levelOfDerp];
if (output === undefined) {
output = dictionary[word][1];
if (output === undefined) {
output = dictionary[word][0];
}
}
outputForm.innerHTML += `${output} `;
hashtagArr.push(output);
}
});
addData(noTranslationArr);
}
Also here is a link to the translator in vanilla js to get a better idea of the project https://darrencarlin.github.io/DoggoSpk/
Solution, but could be better..
I found a solution but I just feel this code is going against the reason to use react in the first place.. My main concern is that I am declaring variables to store strings inside of an array within the function (on every keystroke) which I haven't really done in React, I feel this is going against best practice?
translate = evt => {
// Converting the firebase object
const dict = this.state.dictionary;
let dictCopy = Object.assign(
{},
...dict.map(item => ({ [item["name"]]: item }))
);
let text = evt.target.value.toLowerCase().trim();
let textArr = text.split(/,?\s+/);
let translation = "";
textArr.forEach(word => {
if (dictCopy[word] === undefined) {
translation += `${word} `;
} else {
translation += dictCopy[word][word][this.state.derpLvl];
}
});
this.setState({ translation });
};
levelOfDerp is not defined, try to use 'levelOfDerp' as string with quotes.
let output = dictionary[word]['levelOfDerp' ];
The problem happens because setState() is asynchronous, so by the time it's executed your evt.target.value reference might not be there anymore. The solution is, as you stated, to store that reference into a variable.
Maybe consider writing another function that handles the object conversion and store it in a variable, because as is, you're doing the conversion everytime the user inputs something.
My aim is to replace the teacher-id(f_teacher) of one outputted array with the teacher name of another array. I wrote a custom filter, that should do the job:
angular.module('core')
.filter('replaceId', function () { //filter, which replaces Id's of one array, with corresponding content of another array
return function (t_D, s_D, t_prop, s_prop) { //data of target, data of source, target property, source property
var replacment = {};
var output = [];
angular.forEach(s_D, function (item) {
replacment[item.id] = item[s_prop]; //replacment - object is filled with 'id' as key and corresponding value
});
angular.forEach(t_D, function (item) {
item[t_prop] = replacment[item[t_prop]]; //ids of target data are replaced with matching value
output.push(item);
});
return output;
}
});
I use a 'ng-repeat' like this:
<tr ng-repeat="class in $ctrl.classes | filter:$ctrl.search | replaceId:$ctrl.teachers:'f_teacher':'prename' | orderBy:sortType:sortReverse">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{class.f_teacher}}</td>
</tr>
But it only outputs an empty column. Now the strange thing: If I follow the steps with the debugger, it works for the first time the filter is performed. But when it is performed a second time it outputs an empty column.
I noticed that the returned object of the filter overwrites the $ctrl.classes - array, but normally this shouldn't be the case?
Here is a plnkr:
https://plnkr.co/edit/EiW59gbcLI5XmHCS6dIs?p=preview
Why is this happening?
Thank you for your time :)
The first time through your filter the code takes the f_teacher id and replaces it with the teacher name. The second time through it tries to do the same thing except now instead of getting a teachers ID in f_teacher it finds the teacher's name so it doesn't work. You could fix it by making a copy of the classes instead of modifying them directly. e.g.
angular.forEach(t_D, function (item) {
var itemCopy = angular.copy(item);
itemCopy[t_prop] = replacment[itemCopy[t_prop]];
output.push(itemCopy);
});
https://plnkr.co/edit/RDvBGITSAis3da6sWnyi?p=preview
EDIT
Original solution will trigger an infinite digest because the filter returns new instances of objects every time it runs which will cause angular to think something has changed and retrigger a digest. Could you just have a getter function that gets a teachers name instead of using a filter?
$scope.getTeacherName = function(id) {
var matchingTeachers = $scope.teachers.filter(function(teacher) {
return teacher.id == id;
})
//Should always be exactly 1 match.
return matchingTeachers[0].prename;
};
And then in the HTML you could use it like
<tr ng-repeat="class in classes">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{getTeacherName(class.f_teacher)}}</td>
</tr>
https://plnkr.co/edit/gtu03gQHlRIMsh9vxr1c?p=preview
I want to make a dynamic filter method, but don't know how to include conditions dynamically.
Example
var do_test_1 = true,
do_test_2 = false,
do_test_3 = true;
Now there are three tests:
if (foo == bar) // test 1
if (john == doe) // test 2
if (jane == doe) // test 3
Now I want to build a dynamic if clause, based on the do_test vars.
Real use case
I have a list of tasks and want to filter them:
If the "assigned to me" filter is active, it should return only the the tasks that are assigned to me.
If the "high priority" filter and the "assigned to me" filter is active, it should return only the tasks that are assigned to me and have a high priority.
... and so on ...
I played around with the filter method, but I only get it working with an OR logic (show tasks that are assigned to me or have a high priority):
var $show = $tasks.filter(function(index, task) {
var $task = $(task);
return ((task.data('assigned') && filters['mytasks']) || (task.data('priority') == 3 && filters['priority']))
});
use eval() function
for(var i=0;i<3;i++){
var f=i+1;
eval("if(f==i+1){alert(i);}");
}
Demo: http://jsfiddle.net/ke42b/2/
This is just a tip on how to create dynamic if conditions. Now you know how to create dynamic if conditions.
Code accordingly to your needs !