After upgrade from version 4 to 5, knockout dynamic icon toggle no longer work.
Based on the view model the css class of changed but icon does not.
Hi!
Thanks for being part of the Font Awesome Community.
I can confirm this issue and it happens because <i>
tags are dinamically replaced by <svg>
tags.
At the moment, you could consider using the css version: http://jsfiddle.net/tagliala/hmLpf64f/29/
I think this is an answer to your question: https://fontawesome.com/how-to-use/js-component-packages#questions
@robmadole any thoughts?
Hi @tagliala !
The css version could be a workaround, but we are so happy with the svg version right now and we don't want to lose all the great new additional styling and use power transforms features. :(
The strange one is that normaly knockout handles dynamic added HTML elements well.
The strange one is that normaly knockout handles dynamic added HTML elements well.
Yes, I can see the data-bind
attribute attached to the svg element too, but I'm not into knockout.js, so I can't help
OK, I understand that and I think I found the problem.
Dynamically add elements are not automatically added to the knockout context.
After adding you have to do something like ko.applyBindingsToDescendants(bindingContext, element);
to the new elements.
So what I basically need is a way to react after font awesome has replaced the elements. Is there an event or something?
One possible solution I found is to use nest instead of replacing. Because with this the binding stays active.
Like here: http://jsfiddle.net/898m68hv/
But then FA loses the icon completely.
I think the reason is data-prefix="null"
is getting lost, because FA remove fas
from surrounding <i class="..."
.
It looks like a bug when you combine "Nest
I don't know why, but the fas
prefix is been swallowed in the svg tag.
Just adding fas
to css data bind doesn't work
I came up with a workaround I cannot explain without checking FA5's JavaScript (or knockout) internals: http://jsfiddle.net/tagliala/42p2pd5f/12/
I think you posted the wrong link. Should be http://jsfiddle.net/tagliala/42p2pd5f/13/ :)
Also, I can to the same solution with another workaround (<i class="fas" data-prefix="fas"...
).
But I think this is still a bug and needs a permanent solution.
I found the code in FA5 where it happens:
nest: function nest(mutation) {
[...]
var splitClasses = abstract[0].attributes.class.split(' ').reduce(function (acc, cls) {
if (cls === config.replacementClass || cls.match(forSvg)) {
acc.toSvg.push(cls);
} else {
acc.toNode.push(cls); <------ here the class 'fas' never added becaus its not element of `abstract[0].attributes.class`
}
return acc;
}, { toNode: [], toSvg: [] });
[...]
node.setAttribute('class', splitClasses.toNode.join(' '));
[...]
}
};`
I would rather use the svg instead of webfonts. This will be awesome if we could get svgs working for ko.js!
We'll have to take a deeper look and see how we can integrate with Knockout. I'd prefer to have a specific library that works directly with Knockout.js. (Similar to how we have a Vue or React component).
@robmadole I don't think its a special knockoutjs problem. It's more a general binding problem. As I see in the code of FA you cut out the related CSS classes to detect already handled elements. So nest option would work if there could be a better "marker" and the parent element stays at it is. Maybe a custom data-* tag would be a better option. After this, it will work with binding frameworks like knockoutjs in general.
My use case was similar to the jsfiddle example above but, for me, icon was not showing on page load, only after the binding had been updated. I found I needed to add data-prefix="fas"
.
Another gotcha is the FontAwesome configuration must be set before the library is included. E.g.:
<script>window.FontAwesomeConfig = {autoReplaceSvg: 'nest'};</script>
<script defer src="https://use.fontawesome.com/releases/v5.0.13/js/all.js" integrity="sha384-xymdQtn1n3lH2wcu0qhcdaOpQwyoarkgLVxC/wZ5q7h9gHtxICrpcaSUfygqZGOe" crossorigin="anonymous"></script>
@robmadole This binding problem also affects React if you are using the web font:
On a toggle button that's checked I have <i className='far fa-check-square'/>
On uncheck, the page re-renders with <i className='far fa-square'/>
but it does not change. In some cases it throws an error.
Here is a codepen showing the problem and a solution using unique keys: https://codepen.io/stahlmandesign/pen/pKjdEV
EDIT: I noticed that if you wrap the <i>
element in a <div>
and assign a unique key, the binding is resolved. For that matter, if you wrap the first <i>
in a <span>
it resolves because it is a different element and re-renders without needing a unique key.
@stahlmanDesign I'm not so much into react. but we have a react component: https://github.com/FortAwesome/react-fontawesome
Ref: https://fontawesome.com/how-to-use/js-component-packages#react
with knockout and FA5 I have this,
<i class="far fa-lg" data-bind="css: {
'fa-square': i_response() != 'NO',
'fa-check-square': i_response() == 'NO'
}"></i>
which becomes this;
<svg class="svg-inline--fa fa-square fa-w-14 fa-lg" data-bind="css: {
'fa-square': i_response() != 'NO',
'fa-check-square': i_response() == 'NO'
}" aria-hidden="true" data-prefix="far" data-icon="square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-6 400H54c-3.3 0-6-2.7-6-6V86c0-3.3 2.7-6 6-6h340c3.3 0 6 2.7 6 6v340c0 3.3-2.7 6-6 6z"></path></svg>
I know the binding works because if it didn't; I'd have the dotted circle with the slow burn question mark for all my response blocks, fa-square
doesn't get assigned until the binding is evaluated. It's when the value updates, that the binding falls flat.
With knockout 3.5 and FA5 the following worked for me. I wrapped the <i>
tag in a <span>
and added an html binding to a function and injected the entire contents if the <i>
tag.
<span data-bind="html: toggleIcon"></span>
The function is:
self.toggleIcon = ko.pureComputed(function() {
return self.isCollapsed() ? '<i class="far fa-plus-square fa-fw"></i>' : '<i class="far fa-minus-square fa-fw"></i>';
});
});
Fiddle here: https://jsfiddle.net/atombyte/nkc3f4gh/
My use case was similar to the jsfiddle example above but, for me, icon was not showing on page load, only after the binding had been updated. I found I needed to add
data-prefix="fas"
.Another gotcha is the FontAwesome configuration must be set before the library is included. E.g.:
<script>window.FontAwesomeConfig = {autoReplaceSvg: 'nest'};</script> <script defer src="https://use.fontawesome.com/releases/v5.0.13/js/all.js" integrity="sha384-xymdQtn1n3lH2wcu0qhcdaOpQwyoarkgLVxC/wZ5q7h9gHtxICrpcaSUfygqZGOe" crossorigin="anonymous"></script>
In my case: Blazor project with dynamic links in the header, adding configuration to index.html fixed the problem. Thanks!!!
Most helpful comment
My use case was similar to the jsfiddle example above but, for me, icon was not showing on page load, only after the binding had been updated. I found I needed to add
data-prefix="fas"
.Another gotcha is the FontAwesome configuration must be set before the library is included. E.g.: