Issue
I have this component which I attach to an input
to stylize it the way I want.
@Component({
selector: 'input[ui-input]',
template: '',
styleUrls: ['./input.component.scss'],
})
export class InputComponent {}
I then use it in the html like below. I also have a button which I click that opens a folder selection also seen below.
<ui-form-field *ngIf="home$ | async">
<label ui-label>Default Project Location</label>
<input ui-input [(ngModel)]="home" />
<div ui-form-field-actions>
<button ui-button-fab (click)="selectFolder()">
<fa-icon [icon]="browseFolderIcon"></fa-icon>
</button>
</div>
</ui-form-field>
The component that I have attached to this looks like this:
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent {
home = '';
home$ = of(this.settings.get<string>('default-save-location')).pipe(
tap(i => (this.home = i))
)
selectFolder() {
this.electron
.openFolder()
.pipe(
tap(i => console.log(i)),
tap(i => (this.home = i))
)
.subscribe();
}
}
When the component loads the home gets set and renders the value into the input. However, when selectFolder()
runs, the new value gets logged to the console, but the template doesn't update unless I click inside of the input
then click outside of the input
.
I don't have changeDetection
set on any of my components either so they are all using the default change detection strategy.
Not sure if it is relevant, but I have this in the electron service:
@Injectable({ providedIn: 'root' })
export class ElectronService {
private home = new BehaviorSubject('/');
home$ = this.home.pipe(switchMap(() => this.#getItem<string>('path', 'home')));
openFolder() {
return this.#getItem<{ filePaths: string[] }>('open-folder').pipe(
map(i => i.filePaths?.[0] ?? ''),
take(1)
);
}
#getItem<T, U = unknown>(key: string, ...args: U[]) {
return new Observable<T>(sub => {
window.ipcRenderer.once(key, (e, v) => {
sub.next(v);
sub.complete();
});
window.ipcRenderer.send(key, ...args);
});
}
}
Solution
I was able to solve this issue by creating a preloader
in the electron main service with contextIsolation
. I also added a dialog handler to handle opening of dialogs:
let win: BrowserWindow;
app.on('ready', () => {
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
ipcMain.handle('dialog', (evt, method: any, ...params: any[]) => dialog[method](...params));
});
Next in the preloader, I added a context bridge to expose some functions to the renderer:
contextBridge.exposeInMainWorld('electron', {
dialogOpen: (method: string, options: any) => ipcRenderer.invoke('dialog', method, options),
});
Next, within my renderer process I just need to call thedialogOpen
function:
openFolder() {
return from(
window.electron.dialogOpen('showOpenDialog', { properties: ['openDirectory'] })
).pipe(
map(i => i.filePaths?.[0] ?? ''),
take(1)
);
}
Not only does this fix the issue, but I no longer have to call window.ipcRenderer.send()
and then create a listener to listen for a response. The function just returns a promise with the result!
Answered By - Get Off My Lawn
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.