15

It's pretty straightforward to dynamically add a CSS class to a component in Java code using an AttributeAppender:

component.add(new AttributeAppender("class", true, new Model<String>("foo"), " "));

Or, if you've extracted the above into an appropriate utility method or class, simply something like:

component.add(WicketUtils.cssClassAppender("foo"));

But how can I remove a CSS class?

You can easily remove all CSS classes by clearing the class attribute altogether:

component.add(new SimpleAttributeModifier("class", ""));

...but that is not acceptable if the component has other CSS classes that you do not wish to remove.

Wicket 1.4 (but feel free to post advice specific to later versions too).

Jonik
  • 80,077
  • 70
  • 264
  • 372
  • 3
    Not useful, eh - care to elaborate, downvoter? IMHO it's useful (for myself and others) to document non-immediately-obvious aspects of Wicket here on SO. – Jonik Apr 13 '12 at 07:22

3 Answers3

17

Here's one way I came up with:

public class CssClassRemover extends AttributeModifier {
    public CssClassRemover(String cssClass) {
        super("class", false, new Model<String>(cssClass));
    }

    @Override
    protected String newValue(String currentValue, String valueToRemove) {
        // NB: naive approach; breaks with e.g. "foo foo-bar" & "foo"
        return currentValue.replaceAll(valueToRemove, "");
    }
}

The code that uses the above helper would then be:

component.add(new CssClassRemover("foo"))

(Of course you can also just create anonymous AttributeModifier subclasses as needed, but putting the logic in a separate utility class or method cleans it up a lot.)

Edit: An improved version of newValue() that handles corner cases better (see comment by biziclop). NB: uses Guava. (You're welcome to post simpler (regex?) versions.)

@Override
protected String newValue(String currentValue, String valueToRemove) {
    if (currentValue == null) return "";

    Set<String> classes = Sets.newHashSet(Splitter.on(" ").split(currentValue));
    classes.remove(valueToRemove);
    return Joiner.on(" ").join(classes); 
}
Jonik
  • 80,077
  • 70
  • 264
  • 372
  • **Feel free to post better / simpler ways to do this.** Specifically if I overlooked something like this that comes with Wicket built-in... – Jonik Apr 13 '12 at 05:49
  • Depending on the use case it could also be suitable to use an `AttributeModifier` and decide which classes to use in an `IModel`. – Xavi López Apr 13 '12 at 08:41
  • You should add whitespace (and string start/end) matching to the beginning and the end of `valueToRemove`, just consider the case where `class="foo foo-bar"` Even better if you use `currentValue.split( "\\s+" )` to obtain all the individual classes and then re-append them without the one you want to remove. For a bonus point, you can also do the same to `valueToRemove` to be able to remove several classes with a single `CssClassRemover`. – biziclop Apr 13 '12 at 10:24
  • 1
    @biziclop: Good call! I'm not a big fan of regex minutiae, so I quickly coded a Guava version that *should* be pretty robust. If anyone doesn't like it, competing implementations are welcome. :-) – Jonik Apr 13 '12 at 10:54
  • 1
    Well, if you're using Guava anyway, it's easy. :) – biziclop Apr 13 '12 at 12:00
3

Building on Jonik's answer, the following adds negative lookahead to ignore occurrences in the middle of a different style class (and is case insensitive).

public class StyleClassRemover extends AttributeModifier {

    public StyleClassRemover(final String cssClass) {
        super("class", false, Model.of(cssClass));
    }

    @Override
    protected String newValue(final String currentValue, final String valueToRemove) {
        if (currentValue == null) {
            return "";
        }

        final String patternString = "(^|\\s+)" + Pattern.quote(valueToRemove) + "(?!\\S)";
        return Pattern.compile(patternString, Pattern.CASE_INSENSITIVE).matcher(currentValue).replaceAll("");
    }
}

Tested input: http://fiddle.re/ah0ca6

Community
  • 1
  • 1
nschum
  • 15,322
  • 5
  • 58
  • 56
0

Wicket 1.5+ has org.apache.wicket.AttributeModifier#remove()

martin-g
  • 17,243
  • 2
  • 23
  • 35
  • 4
    Hmm, looking at the javadoc ("Creates a attribute modifier that removes an attribute with the specified name"), it doesn't seem to do what I want... Sounds like it would remove the "class" attribute entirely, just like the SimpleAttributeModifier example in the question. – Jonik Apr 13 '12 at 16:07