Options in XenForo JS

Robust

Well-known member
How'd you get options in XF's JS files? I can't see any usage of XenForo's options in the core JS. I need to get a value set by the administrator in the options.
 
You'd set a js variable with the value of the option
Yeah, but how do I get the value of the option? I don't see any methods to interact with XenForo's options.

I'm overriding an existing XenForo js function, btw.
 
Oh thanks, but are you sure '{$xenOptions}' is valid in this case? I tried it and it returns NaN. Not sure if it's valid to reference like that
He means to place that in a XenForo template (like page_container_js_head) where $xenOptions can be parsed, and then access 'myVar' from your JavaScript file.

$xenOptions is a globally-available template variable.
 
template modification on PAGE_CONTAINER:

Code:
$.extend(XenForo, {
randomExample: {$xenOptions.optionId}
});

Then in your javascript call
Code:
XenForo.randomExample
Something like this could work?
var myVar = '{$xenOptions.option_id}';

then reference it using 'myVar'

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
Which of the above methods is better? Both make a "variable" accessible for use, is there a 'better' method? In terms of efficiency or good practice?
 
I just used one of them for now, unless the other is better. Thanks everyone! I'd say I love you, but some people might get the wrong idea, so I'll just say I love your assistance ;)
 
I would go with either Cédric's solution as suggested by 0ptima or Daniel Hood's solution.

If you look at Daniel's solution, it's essentially what XF does in the page_container_js_body template - it's how we pass things like the user ID, certain options, style properties into the JS.

Cédric's solution is also used widely within XF which uses the HTML5 data attribute to pass arbitrary values from the HTML to be read using the data() function in jQuery. e.g. simplified example:
HTML:
<button class="Button" data-any-name-you-like="any value you like">Button</button>
<script>
    var $button = $('button.Button');
    alert($button.data('any-name-you-like')); // creates an alert with the text "any value you like"
</script>

I guess the deciding factor depends on the rest of your code. If you are working in JS with a single element or a simple set of elements, then getting the value from the data- attribute works best, because you can just read the data attribute from that element like you can read any attribute.

If your option needs to be accessed across a number of functions, by a number of elements and/or be available on multiple XF pages, that's where Daniel's solution maybe works best; it makes the variable available within the XenForo namespace in the JS, therefore making the scope of its use wider and potentially more convenient than applying that value to specific elements.

Jake's solution could also work, but I feel like the other options are more elegant.
 
Thanks, @Chris D! I'm actually using Daniel's solution right now. I extend the profile post limitations and change those, editing a bunch of areas to add data attributes wouldn't be appropriate, however I didn't realise that was his solution. I only looked through the library and js folders, probably why it didn't all add up. That's a good solution.

Since I'm extending an existing function, I could add a lot of template modifications or just make it global, I ended up just making it global in case the end user added profile post areas anywhere else in XF, and xenforo.js is global and the code for it is contained in there, of course. A concern was mostly that it's going to increase page load time, especially for sites with high fetch time (a lot of time wasted in waiting and receiving the data to begin the request).

I did encounter another problem. What I often do is for small edits edit core XF files to make sure it works, then put it into an extending XFCP class (and undo the ones to the core). I noticed afterwards that extending a DataWriter preSave method is hard to extend.

So I'm adjusting the var $maxLength, it's really basic. But then I should also be calling the parent preSave function in case other add-ons wish to extend it, as well. I mean, right now it does nothing but the message preSave but another add-on might add more stuff in and want to run a preSave there, or reference another function. So for the sake of good code implementation, the parent should be called I guess. Thing is, if I call the parent the limit with 140 will be called, making an error.

I thought about removing the error from $_errors, but it's actually thrown as an exception so that'd be futile. I thought about changing the throw exception thing to 0 throughout the method, but it's a constant so it can't be adjusted.

Is there any way to still make it decently call the parent function without the error being thrown? Any way to suppress DW errors for a short time?
 
Is there any way to still make it decently call the parent function without the error being thrown? Any way to suppress DW errors for a short time?

You could try wrapping it in a try and use catch then check the error message, but that probably isn't the best idea. Only thing I could really think of though

Jake
 
Untested but something like:

PHP:
class AddOn_DataWriter_Thing extends XFCP_AddOn_DataWriter_Thing
{
public function __construct($errorHandler = self::ERROR_EXCEPTION, array $inject = null)
{
parent::__construct(self::ERROR_SILENT, $inject);
}
}

That'd make it so that when the parent datawriter is initiated, there's no error handler. Should be careful with that though. A try/catch method like Jake said might be a better route.[/php]
 
You could try wrapping it in a try and use catch then check the error message, but that probably isn't the best idea. Only thing I could really think of though

Jake
Untested but something like:

PHP:
class AddOn_DataWriter_Thing extends XFCP_AddOn_DataWriter_Thing
{
public function __construct($errorHandler = self::ERROR_EXCEPTION, array $inject = null)
{
parent::__construct(self::ERROR_SILENT, $inject);
}
}

That'd make it so that when the parent datawriter is initiated, there's no error handler. Should be careful with that though. A try/catch method like Jake said might be a better route.[/php]
I tried this:

PHP:
try {
    parent::_messagePreSave();
} catch (XenForo_Exception$e) {
    unset($this->_errors['message']);
}

Doesn't seem to really work, throws the exception anyway (I know I'm not checking if the exception is X, I'm just doing dummy test code here)
 
However, this works:

PHP:
try {
    parent::_messagePreSave();
    unset($this->_errors['message']);
} catch (XenForo_Exception $e) {
    unset($this->_errors[0]);
}
 
However, this works:

PHP:
try {
    parent::_messagePreSave();
    unset($this->_errors['message']);
} catch (XenForo_Exception $e) {
    unset($this->_errors[0]);
}

You may want to verify exactly what error you're unsetting instead of just blindly removing an error
 
You may want to verify exactly what error you're unsetting instead of just blindly removing an error
Yeah, of course, I'm just seeing what works right now.

Edit: Actually, that's the only type of message related error in this DW, it's set to remove any others for the same field. Completely override.
 
Top Bottom