I am trying Localize Fluent UI component by i18n solution using Typescript and React. But I stuggle with changing name in property..
i18n is implemented according to documentation using hooks (in common component it works well)
but I am unable to change CommandBar items names when I change languege
from functional component
...
return (
...
<CommandBar
items={CommandBarItems.menu}
)
/>
...
)
..
items is in separate file
import i18n from '../i18n';
...
const FileMenuOpen:ICommandBarItemProps = {
key: Routes.FileMenuOpenRoute,
name: i18n.t('tpmain:menu.fileOpenLbl'),
href: `#${Routes.FileMenuOpenRoute}`,
iconProps: { iconName: 'DocumentSet', style: { fontSize: 16 } }
};
....
CommandBarItems = { menu: [FileMenu, UserMenu, EditMenu, AdviceMenu], farItems: [], }
export default CommandBarItems;
My Issue is that CommandBarItem is translated only once on start.. doesnt react on language change.
I also try to useMemo inside component, but without success:
const { t, i18n } = useTranslation();
const items = useMemo(() => {
console.log("check for memo working")
return CommandBarItems.menu
}, [i18n.language])
return (
...
<CommandBar
items={items}
)
/>
Please can anyone help me to solve it?
Module-level variables such as FileMenuOpen are only calculated once, when the module loads. The useMemo appears well formed, but is currently always fetching the original static object.
Instead, you want dynamically generate this object on demand. That is easily done by putting your object creation inside a function and return it, and call that from useMemo or whenever needed.
This technique applies not only here, but anytime you need to regenerate Fluent UI component configurations.
Related
How can i send data from client to server using html5 webcomponent
setting up data from server to client, is very easy, works like a charm
how ever cannot find solution to send data to server
Please assist, but Iam not going to use Lit or Polymer
#JavaScript
class SimpleComponent extends HtmlElement {
connectedCallback() {
this.innerHTML = '<input type="text" id="test"/>";
this._input = this.querySelector('#test');
this._input.onchange = function() {
***** i want to send the value to server ****
})
}
setInputValue(value) {
this._input.value = value;
}
}
customElements.define("simple-com",SimpleComponent);
Now Java at Server
#Tag("simple-com")
class SimpleComponent extends Component {
public SimpleComponent() {
}
public void setValue(String value) {
getElement().callJsFunction("setValue",value);
}
}
The main challenge compared to Polymer or LitElement is that an event handler defined using the pattern innerElement.onchange = function() {} will not be run with this referencing the custom element instance. This in turn means that trying to use this.$server won't work because this isn't pointing to the expected value even though $server is indeed present in the place where it's supposed to be.
The easiest way of fixing this is to change the code to use an arrow function (() => {}) instead of an explicit function. This works because arrow functions inherit this from the scope where the function is defined whereas explicit functions have this defined in different ways depending on how it is run. Another approach would be to store a reference to this in a separate variable (e.g. let root = this) and then reference that variable instead of this in the function (e.g root.$server.doSomething()).
Putting everything together, this is what the code looks like with my modifications to make everything work.
class SimpleComponent extends HTMLElement {
connectedCallback() {
this.innerHTML = '<input type="text" id="test"/>';
this._input = this.querySelector('#test');
this._input.onchange = () => {
this.$server.handleChange(this._input.value);
};
}
setValue(value) {
this._input.value = value;
}
}
customElements.define("simple-com", SimpleComponent);
I'm having 2 JSONs. The first one is having the format of the JSON value and the second one is having the actual value which is I want to display in the UI.
But I'm seeing the "application.appID" instead of 101. Does any help please?
Not working if label:"applicaiton.appID". I'm having label: "string"
working if label: applicaiton.appID
component.ts
this.json1={
label:"applicaiton.appID"
};
this.application ={
appID:101
};
ui.html
<mat-label> {{json1.label}} </mat-label>
<mat-label [innterHtml]="json1.lable"> </mat-label>
If I understand right, what you're trying to do is to interpolate based on a string expression coming from a json. This is not something that you can do by just using the {{ }} construct. Here's why:
(For simplicity I will use div instead of mat-label)
In theory, this line would solve your problem
<div>{{this[json1.label]}}</div>
Just that it doesn't work since the inner json1.label part is not expanded/evaluated as expected.
Even if we manually write it as an explicit string, it still doesn't give us 101.
<div>{{this['application.appID']}}</div>
The only way to make such a syntax work would be to chain the field indexers, but that doesn't help us with using json1.label as the 'path' of the object and inner field.
<div>{{this['application']['appID']}}</div> // this returns 101, but it's not helpful for us...
So as you can see, pure html interpolation can't really help us achieve our goal. Instead, we should create a helper method inside the .component.ts file:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
json1 = {
label: 'application.appID'
};
application = {
appID: 101
};
accessPropertyByString(path: string): any {
let result = this;
const parts = path.split('.');
for (const part of parts) {
if (part in result) {
result = result[part];
} else {
return null;
}
}
return result;
}
}
The new accessPropertyByString() method can be now used in the html template like this:
<div>{{accessPropertyByString(json1.label)}}</div>
Which finally returns 101 as expected.
I want module sync-fetch to be accessible globally without need to import in each component and be named as simple fetch.
Also I want to extend it with custom method then.
Now in rollup.config.js there are:
export default {
...
output: {
...
intro: `const fetch = require('sync-fetch');
fetch.Response.prototype.then = function(foo) {
return foo(this);
}`
},
};
And it works, but looks dangerous) Is intro is the only way to do it?
If you want to make it seem less dangerous, you could put that code in a file and then return the contents of it in a function. The output.intro option also takes a function that returns the code as a string.
{
output: {
intro: () => require('fs/promises').readFile('path/to/the/file.js', 'utf-8')
}
}
This question is about a Kotlin JS project which uses the Kotlin Frontend Plugin.
I want to use some UI components from the Vaadin Components library.
I have two questions about this:
(1) What would be the best way to include web components in Kotlin JS
=> for my complete code, see the link to the source below. In summary the relevant details are:
build.gradle.kts
kotlinFrontend {
npm {
dependency("#vaadin/vaadin-grid")
}
}
vaadin.grid.Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
external class GridElement {
companion object
}
Why the companion object? I need it for the workaround (see below).
foo.kt
fun main() {
document.getElementById("container")!!.append {
vaadin_grid {
attributes["id"] = "grid"
}
}
initUI()
}
fun initUI() {
// Force the side-effects of the vaadin modules. Is there a better way?
console.log(GridElement)
val grid = document.querySelector("#grid") /* ?? as GridElement ?? */
}
The console.log is the ugly workaround trick I want to avoid. If I don't do anything with GridElement then it's just not included in my bundle.
The vaadin_grid DSL is defined as a custom kotlinx.html tag which is unrelated code.
(2) I want to keep my code as typed as possible to avoid asDynamic but when I cast the HTMLElement to a Vaadin Element I get ClassCastExceptions (because GridElement is undefined).
For example I want to write something like this:
val grid : GridElement = document.querySelector("#grid") as GridElement
grid.items = ... // vs grid.asDynamic().items which does work
Here is how I define the external GridElement
vaadin/button/Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement : HTMLElement {
var items: Array<*> = definedExternally
}
build/node_modules/#vaadin/vaadin-grid/src/vaadin-grid.js
...
customElements.define(GridElement.is, GridElement);
export { GridElement };
Source example
To run:
From the root of the git repo:
./gradlew 05-kt-frontend-vaadin:build && open 05-kt-frontend-vaadin/frontend.html
I found the answer(s)
For the first question
(1) What would be the best way to include web components in Kotlin JS
Instead of the console.log to trigger the side effects I use require(...)
external fun require(module: String): dynamic
fun main() {
require("#vaadin/vaadin-button")
require("#vaadin/vaadin-text-field")
require("#vaadin/vaadin-grid")
...
}
(credits to someone's answer on the kotlin-frontend-plugin list)
(2) I want to keep my code as typed as possible to avoid asDynamic
Instead of importing #vaadin/vaadin-grid I have to import the file which actually exposes the element. Then it seems to work and I can even add generics to my GridElement:
#file:JsModule("#vaadin/vaadin-grid/src/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement<T> : HTMLElement {
var items: Array<out T> = definedExternally
}
This way I was able to get rid of all the asDynamics
val firstNameField = document.querySelector("#firstName") as TextFieldElement?
val lastNameField = document.querySelector("#lastName") as TextFieldElement?
val addButton = document.querySelector("#addButton") as ButtonElement?
val grid = document.querySelector("#grid") as GridElement<Person>?
val initialPeople: Array<out Person> = emptyArray()
grid?.items = initialPeople
addButton?.addEventListener("click", {
// Read the new person's data
val person = Person(firstNameField?.value, lastNameField?.value)
// Add it to the items
if(grid != null){
val people = grid.items
grid.items = people.plus(person)
}
// Reset the form fields
firstNameField?.value = ""
lastNameField?.value = ""
})
I am trying to require multiple images from a folder within my react native project. I am trying to dynamically load the images I need to render an animation with a react-native-image-sequence component in another react component. I know you can only require images by only passing a string in react-native like this: require("../somepath"), so I am trying to find a work around to load multiple images by passing in a dynamic path. I need to be able to pass the image sequence component an array like this:
[ require("./1.png"),
require("./2.png"),
require("./3.png")
]
for it to render the animation sequence.
I have tried creating a file that contains a method called prepareDirImages that creates and returns a string that contains an array full of require statements to the path passed in as well as the extension of the file. My plan was to then use this method in the component that I am creating and calling eval(prepareDirImages("./dirPath", "png")) to transform the string into its actual array representation and then passing this into the image sequence component.
prepareReactImages.js:
module.exports = {
prepareDirImages(path, ext){
fs.readDir(path).then(files => {
const ex =
"[\n" +
files.map((x, index) =>{
if(index < result.length - 1){
return `require("${path}/${x}"),`
}
`require("${path}/${x}")`
}).join("\n") +
"]";
console.log(ex)
fs.writeFile("../images/index.js", ex);
return ex;
})
}
}
myComponent.js:
import React, { Component } from 'react';
import ImageSequence from 'react-native-image-sequence';
import { prepareDirImages } from '../services/prepareReactImages';
import { View, Text } from 'react-native';
const fs = require('react-native-fs');
export default class myComponent extends Component {
render(){
console.log(eval(prepareDirImages("../images/imagesDir", "png")))
let images = getImages();
return (
<View>
<ImageSequence
images={images}
style={this.props.style}
framesPerSecond={24}
/>
</View>
);
}
}
async function getImages(){
return eval(await prepareDirImages("../images/ed_anim_wave_v01",
"png"))
}
What I get is a promise and I get an error saying that imagesequence cant using property filter of object. I read that you have to use fs.MainBundlePath or fs.DocumentDirectoryPath but I dont understand how to get these files. If anyone can help me out, I'd appreciate it tremendously!!