[Solved] How to use virtual-dom's "h" helper in <head> for header-dropdown hamburger widget override? + How to override @computed method?

Hi,

I’m attempting to override the main header-dropdown html tags via my custom theme.

My proof on concept was this:

<script type="text/discourse-plugin" version="0.8.23">
api.reopenWidget("header-dropdown", {
  html(attrs) {
    if (attrs.icon == 'bars') {
        return 'TEXT TO RETURN INSTEAD OF HAMBURGER';
    } else {
        return this._super(attrs);
    }
  }
});
</script>

In this example, ‘TEXT TO RETURN INSTEAD OF HAMBURGER’ displays correctly.

Now, I want to wrap this text with a tag.

The solution seems to use the h helper like so:

 return h('b', "TEXT TO RETURN INSTEAD OF HAMBURGER");

However, this returns ReferenceError: h is not defined.

Attempting to add import { h } from "virtual-dom"; doesn’t work, which makes sense because I’m in the custom theme head override. (/admin/customize/themes/7/common/head_tag/edit)

What would be the correct way to do this without creating a plugin?

You can access the helper from a theme by putting this at the top of the javascript:

const h = require("virtual-dom").h;
8 Likes

I love you! Thanks :smiling_face_with_three_hearts:

1 Like

@david

Another things that I’m trying to do, if you have an answer that will avoid the need to create a separate topic.


I want to throw a validation message when the user only chooses one level of category.

I was attempting to override categoryValidation() of composer.js.es6 … except that is a @computed method.

Looking at https://github.com/discourse/discourse-assign/blob/master/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6#L69 this should be doable.

My issue is that from the <head>, I don’t have acces to @computed. Is there a way to require it?

I did manage to override the controller:composer save action. However, from there I don’t have access to InputValidation to throw a custom error (I wouldn’t know how to target the correct input from there either).

<script type="text/discourse-plugin" version="0.8.23">
    api.modifyClass('controller:composer', {
        @computed("model.categoryId", "lastValidatedAt") //ReferenceError: computed is not defined
        categoryValidation() {
            console.log('categoryValidation');
            this._super();
        }
    });
</script> 

I’m not sure if this is the only way to do it, but I’ve modified computed properties in customizations like this (the example is using the availableSorts computed property):

 api.modifyClass('component:edit-category-settings', {
    availableSorts: Ember.computed(function() {
    let sorts = this._super();
    let voteSort = {};
    voteSort["name"] = "Votes";
    voteSort["value"] = "votes";
    sorts.push(voteSort);
    return sorts;
    })
  });
5 Likes

If you want to use the decorator syntax, you can do this

const computed = require("ember-addons/ember-computed-decorators").default;

Then you can do

api.modifyClass('component:edit-category-settings', {
    @computed("model.categoryId", "lastValidatedAt")
    availableSorts(categoryId, lastValidatedAt){
        let sorts = this._super();
        let voteSort = {};
        voteSort["name"] = "Votes";
        voteSort["value"] = "votes";
        sorts.push(voteSort);
        return sorts;
    }
  });

Notice that these lines are just a re-arranging of the import lines that you see in Discourse’s codebase. So anything you can import, you should be able to require in a theme.

7 Likes

Awesome, this worked really well!


Here is the snippet for the composer window’s categoryValidation override, if anyone stumble here using the search.

<script type="text/discourse-plugin" version="0.8.23">
const computed = require("ember-addons/ember-computed-decorators").default;
api.modifyClass('controller:composer', {
    @computed("model.categoryId", "lastValidatedAt")
    categoryValidation(categoryId, lastValidatedAt) {
        var InputValidation = require("discourse/models/input-validation").default;
        var categoryDepth = /*Find categoryId's category level*/;
        if(this.get('model').draftKey == "new_topic" && (categoryDepth !== 0 && categoryDepth < 2)) {
            return InputValidation.create({
                failed: true,
                reason: 'You need to select a subcategory',
                lastShownAt: lastValidatedAt
              }); //THIS WORKS AND DISPLAY AN CUSTOM ERROR MESSAGE
        } else {
            return this._super(categoryId, lastValidatedAt);
        }
    }
});
</script>
4 Likes