There are a couple of questions about this that works, but don't in my scenario.
I want to invoke a function defined in my superclass view, instead of invoking a function in the itemTpl itself.
I´ve something like this in the itemTpl:
'<span class="x-button-label" style="" id="ext-element-115">{[this.getTranslation("textcodestr", "Text by default")]}</span>',
And then, I've this function in the itemTpl config.
getTranslation: function (textCode, defaultText) {
var store = Ext.getStore('TranslationsStore');
var index = store.findExact('TextCode', textCode)
if (index > -1) {
return store.getAt(index).data.TextTranslated;
}
return defaultText;
}
It works, but I want to move this function to the superclass of the view, and be able to invoke the function within the template, instead of copy and paste in all the application templates.
Of course that use a hyper-long line that do what is in the function is not ideal.
Is there a way to do this ?
Thanks!
Milton
I managed to solve it by using a singleton helper, which I can invoke from the itemTpl.
Ext.define('MyApp.util.Shared', {
singleton : true,
getTranslation: function (textCode, defaultText) {
var store = Ext.getStore('TranslationsStore');
var index = store.findExact('TextCode', textCode)
if (index > -1) {
return store.getAt(index).data.TextTranslated;
}
return defaultText;
}});
Then, in the itemTpl
'<span class="x-button-label" style="" id="ext-element-115">{[MyApp.util.Shared.getTranslation("textcodestr", "Text by default")]}</span>',
HTH!
Milton.
Related
I have the following code in a Nuxtjs app in SSR mode.
<Component
:is="author.linkUrl ? 'a' : 'div'"
v-bind="!author.linkUrl && { href: author.linkUrl, target: '_blank' }"
#click="author.linkUrl ? handleAnalytics() : null"
>
The click event in case it's an a tag, will only fire if it's written as handleAnalytics(), but handleAnalytics will not work.
Don't get me wrong the code is working, but I don't understand why.
With classical event binding (#click="handleAnalytics), Vue will auto bind it for you because it sees it's a function.
But when provided a ternary condition, it's not auto binded but wrapped into a anonymous function instead. So you have to call it with parenthesis otherwise you're just returning the function without executing it.
To be clearer, you can write it this way: #click="() => author.linkUrl ? handleAnalytics() : null"
Note: when having a dynamic tag component, I'd suggest to use the render function instead.
This is an advanced technique, but this way you won't bind things to an element that doesn't need it (without having the kind of hack to return null).
Example:
export default {
props: {
author: { type: Object, required: true },
},
render (h: CreateElement) {
const renderLink = () => {
return h('a', {
attrs: {
href: author.linkUrl,
target: '_blank',
},
on: {
click: this.handleAnalytics
},
)
}
const renderDiv = () => {
return h('div')
}
return this.author.linkUrl ? renderLink() : renderDiv()
}
}
Documention: Vue2, Vue3
In javascript functions are a reference to an object. Just like in any other language you need to store this reference in memory.
Here are a few examples that might help you understand on why its not working:
function handleAnalytics() { return 'bar' };
const resultFromFunction = handleAnalytics();
const referenceFn = handleAnalytics;
resultFromFunction will have bar as it's value, while referenceFn will have the reference to the function handleAnalytics allowing you to do things like:
if (someCondition) {
referenceFn();
}
A more practical example:
function callEuropeanUnionServers() { ... }
function callAmericanServers() { ... }
// Where would the user like for his data to be stored
const callAPI = user.preferesDataIn === 'europe'
? callEuropeanUnionServers
: callEuropeanUnionServers;
// do some logic
// ...
// In this state you won't care which servers the data is stored.
// You will only care that you need to make a request to store the user data.
callAPI();
In your example what happens is that you are doing:
#click="author.linkUrl ? handleAnalytics() : null"
What happens in pseudo code is:
Check the author has a linkUrl
If yes, then EXECUTE handleAnalytics first and then the result of it pass to handler #click
If not, simply pass null
Why it works when you use handleAnalytics and not handleAnalytics()?
Check the author has a linkUrl
If yes, then pass the REFERENCE handleAnalytics to handler #click
If not, simply pass null
Summary
When using handleAnalytics you are passing a reference to #click. When using handleAnalytics() you are passing the result returned from handleAnalytics to #click handler.
I am trying to make a progress bar the progress bar works fine but its not changing text within html and keeps static 0%. N.B I am pasting here only relevant codes to avoid a large page of code.
<div class="progressTopBar"><div class="inner-progressBar" :style="{width: this.ProgressBar }">
#{{ getProgressBar() }}
</div></div>
//property
data: function () {
return {
ProgressBar:"0%",
}
}
//function on change to upload and make progress
fileSelected(e) {
let fd = new FormData();
fd.append('fileInput', $("#file")[0].files[0], $("#file")[0].files[0].name);
axios.post("/admin/chatFileUpload", fd, {
onUploadProgress: function (uploadEvent) {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
$(".inner-progressBar").css("width", this.ProgressBar);
}
});
},
//getting progress bar value in text which only returns preset value
getProgressBar() {
return this.ProgressBar;
},
You need to make getProgressBar() a computed property instead of a method.
computed: {
getProgressBar() {
return this.progressBar;
}
}
Also, you should use camel case or snake case for your variables.
The problem is the scoping of this in the code below:
onUploadProgress: function (uploadEvent) {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
Because this is a new function it has its own this value, it does not use the this value from the surrounding code.
The simplest way to fix this is to use an arrow function:
onUploadProgress: (uploadEvent) => {
this.ProgressBar = Math.round((uploadEvent.loaded / uploadEvent.total)*100) + '%';
An arrow function retains the this value from the surrounding scope.
I also suggest getting rid of the jQuery line starting $(".inner-progressBar"), that shouldn't be necessary once you fix the this problem as it will be handled by the template instead.
Further, it's unclear why you have a getProgressBar method at all. If it is just going to return ProgressBar then you can use that directly within your template without the need for a method.
I am rendering two texts based on a condition and be able to pass methods to the click event based on the condition. The default text is ADD TO COLLECTION because initially hasPaid property is false. Once payment has been made, I want to set that property to true
The function addToCollection first opens a modal, on the modal, the handlePayment function is implemented. I have been able to conditionally render the div to show either ADD TO COLLECTION or DOWNLOAD using v-on="". I also return hasPaid property from the handlePayment function.
<div class="float-right peexo-faded-text card-inner-text" :face="face" v-on="!hasPaid ? {click: addToCollection} : {click: handleDownload(face)}">
{{!hasPaid ? 'ADD TO COLLECTION': 'DOWNLOAD' }}
</div>
data: function () {
return {
hasPaid: false,
}
},
addToCollection(){
this.showcollectionModal = true;
},
handlePayment(){
this.showcollectionModal = false;
let accept = true;
this.showpaymentsuccessmodal = true;
//this.hasPaid = true;
return {
hasPaid: accept
}
},
I want to be able to set hasPaid property on the handlePayment function for the render function to pick it, so that the handleDownload function can then work.
The last section of this bit is going to be problematic:
v-on="!hasPaid ? {click: addToCollection} : {click: handleDownload(face)}"
When hasPaid is true it will invoke the method handleDownload immediately. That is, it will be called during render, not when the <div> is clicked.
You could fix it by 'wrapping' it in a function:
{click: () => handleDownload(face)}
I've used an arrow function in my example but you could use a normal function if you prefer.
Personally I wouldn't try to do this using the object form of v-on.
My first instinct is that you should consider just having two <div> elements and use v-if to decide which one is showing.
If you did want to use a single <div> I would put the click logic in a method. So:
<div class="..." :face="face" #click="onDivClick(face)">
Note that despite the apparent syntactic similarity to the way you defined your click listener this won't invoke the method immediately.
Then in the methods for the component:
methods: {
onDivClick (face) {
if (this.hasPaid) {
this.handleDownload(face)
} else {
this.addToCollection()
}
}
}
I know this has been asked before but I wanted to ask it in my own way with more clarification. I am trying to conditionally set the background of a td that is created using a webGrid in ASP.NET MVC. I don't see a good way to do this.
So far what I have come up with is this:
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<div style=\"background-color: red\">Item Backordered</div>"
:
""
)),
This is an okay solution but I would like a more clean look because the webgrid default has a small padding in the table cell so the div won't expand completely to the size of the cell either.
Is there a way to edit the td in any way? I know I can change the background and other style attributes using jquery or javascript but I don't like the idea of having doing duplicate work to first build the table on the server, then on the client side iterate over it again conditionally changing the colors when this should have been completed with the first iteration.
Hope the following answer will help you
grid.GetHtml(columns: grid.Columns(grid.Column(columnName: "DATT", header: "Date",format: #<text> #{
if (#item.isCurrentlyBackordered)
{
<span>Item Backordered</span>
<script>
$("tr:contains('Item Backordered')").css("background-color", "yellow");
</script>
}
}</text>)))
Also you can write this in a common JQuery too
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<span>Item Backordered</span>"
:
""
)),
JQuery
<script type="text/javascript">
$(function () {
$("tr:contains('Item Backordered')").css("background-color", "yellow");
})
</script>
With the help of Golda's response and here
I was able to create an elegant solution. This solution uses JavaScript/JQuery, as it doesn't seem possible to do it without it but using (to me) a cleaner solution than what I had came across. What I did in the model class (type for List<>()) was add a property that refers to itself and returns an instance cast to its interface like so:
public iTrans This
{
get
{
return this;
}
}
I did this because the webGrid seems to only allow access to the properties and not methods; regardless of access level.
Then in that same model I have a method which will conditionally attach markup for a hidden input field to the data string and return it as an MvcHtmlString object:
public MvcHtmlString htmlColorWrapper(string cellStr, string hexColor = "#ccc")
{
if (isOnBackorder)
{
cellStr = cellStr + "<input type='hidden' class='color' value='" + hexColor + "'/>";
}
return new MvcHtmlString(cellStr);
}
And in the markup (partial view) I make my grid.Column call:
grid.Column("Date", header: "Date", format: (item) => item.This.htmlColorWrapper(item.Date.ToString("MM/dd/yyy"))),
Then I create the JavaScript function(s):
window.onload = function () {
SetFeaturedRow();
};
function SetFeaturedRow() {
$('.color').each(function (index, element) {
$(element).parent().parent().css('background-color', $(element).val());
});
}
The window.onload is needed to point to the SetFeaturedRow() function to set the row colors at page load, the function name, "SetFeaturedRow" is stored in the ajaxUpdateCallback property through the webgrid constructor arguments: new WebGrid(Model ..... ajaxUpdateCallback: "SetFeaturedRow"); Or it can be set through the WebGrid reference, ref.ajaxUpdateCallback = "SetFeatureRow"
This will be used during any ajax call the WebGrid class will make. So for example if there are multiple pages to the webgrid each selection is an ajax call and the row colors will need to be re-updated.
Hopefully this helps someone.
I have written a custom component to create a html button.
custom component is defined as follows
dojo.provide("ovn.form.OvnButton") ;
require([ "dojo/_base/declare",
"dojo/dom-construct",
"dojo/parser",
"dojo/ready",
"dijit/_WidgetBase"],
function (declare, domConstruct, parser, ready, _WidgetBase){
return declare ("ovn.form.OvnButton",[_WidgetBase],{
label: "unknown",
constructor : function(args){
this.id = args.id;
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
});
alert("from constructor " + this.label);
},
postMixInProperties : function(){
},
buildRendering : function(){
alert("from renderer label is " + this.label);
this.domNode = domConstruct.create("button", { innerHTML: this.label }); //domConstruct.toDom('<button>' + this.label + '</button>');
},
_getLabelAttr: function(){
return this.label;
},
_setLabelAttr : function(label){
alert("from set input is " + label)
this.label = label;
},
postCreate : function(){
alert("Post create label is " + this.label);
},
startUP : function(){
}
});
});
This is how I am instantiating the component
var button = new ovn.form.OvnButton({
id:'run',
props:[{"name":"label","value":"Run"},{"name":"class","value":"btn"}]
});
In the constructor of the custom component, I am iterating through the array passed and assigning to the instance variable called 'label'. To my surprise when we print the instance variable in buildRendering function, it is still printing the default instead of the assigned value.
can somebody give some light on why this is so.
FYI:
I am getting the following sequence of messages on the console
1.found label Run
2. from constructor unknown
3. from renderer label is unknown
4. from set input is unknown
5. Post create label is unknown
This happens because inside the little forEach function, this actually points to something completely different than your OvnButton object.
Javascript's this keyword is quite strange in this regard (it doesn't have anything to do with Dojo, actually). You can read more about how it works here: http://howtonode.org/what-is-this . It's a quite fundamental concept of Javascript, different from other languages, so it's well worth your time to get familiar with.
But there are various different ways you can quickly solve it, so here are a few!
Use a regular for loop instead of forEach and callback
The easiest is probably to use a regular for loop instead of forEach with a callback.
....
// args.props.forEach(function(prop) {
for(var i = 0, l = args.props.length; i < l; i++) {
var prop = args.props[i];
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}//); <-- no longer need the closing parenthesis
The takeaway here is that Javascript's this magic only happens for function calls, so in this case, when we just use a for loop, this continues to point to the right thing.
... or use forEach's second thisArg argument
But perhaps you really want to use forEach. It actually has a second argument, often called thisArg. It tells forEach to make sure this points to something of your choice inside the callback function. So you would do something like this:
....
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}, this); // <--- notice we give forEach two arguments now,
// the callback function _and_ a "thisArg" value
I'm not completely sure the above works in all browsers though, so here's another way to solve your issue:
... or use a temporary "self" variable
We will make a temporary variable equal to this. People often call such a variable self, but you can name it anything you want. This is important: it's only the this keyword that Javascript will treat differently inside the callback function:
....
var self = this; //<--- we basically give `this` an alternative
// name to use inside the callback.
args.props.forEach(function(prop) {
if(prop.name == 'label'){
self.label = prop.value; //<--- replaced `this` with `self`
alert("found label " + self.label); //<--- here as well
}
});
... or use hitch() from dojo/_base/lang
Some people don't like the self solution, perhaps because they like to consistently use this to refer to the owning object. Many frameworks therefore have a "bind" function, that makes sure a function is always called in a particular scope. In dojo's case, the function is called hitch. Here's how you can use it:
require([....., "dojo/_base/lang"], function(....., DojoLang) {
....
args.props.forEach(DojoLang.hitch(this, function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}));
... or use Javascript's own bind()
Dojo and pretty much every other framework out there has a hitch() function. Because it's such a commonly used concept in Javascript, the new Javascript standard actually introduces it's own variant, Function.prototype.bind(). You can use it like this:
....
args.props.forEach(function(prop) {
if(prop.name == 'label'){
this.label = prop.value;
alert("found label " + this.label);
}
}.bind(this));
That was a very long answer for a pretty small thing, I hope it makes some sense!