The FileView class extends ItemView and is designed for views that display or edit files. It handles file loading, unloading, and state management.
Properties
Whether this view can exist without a file.
The file currently displayed in this view, or null if no file is loaded.
File views can be navigated by default.Default: true
Inherited Properties
FileView inherits properties from ItemView and View:
contentEl: HTMLElement
app: App
icon: IconName
leaf: WorkspaceLeaf
containerEl: HTMLElement
scope: Scope | null
Constructor
constructor(leaf: WorkspaceLeaf)
The workspace leaf this view will be attached to
Methods
getDisplayText()
Gets the display text for this view. By default, returns the file basename.
The display text (file name or custom text)
onload()
Called when the view is loaded.
getState()
Gets the current state of this view, including the file path.
getState(): Record<string, unknown>
The current view state including file path
Since: 0.9.7
setState()
Sets the state of this view, loading the specified file.
setState(state: any, result: ViewStateResult): Promise<void>
The state to set, typically includes a file property with the file path
Result object to indicate if state change should be recorded in history
Promise that resolves when state is set
Since: 0.9.7
onLoadFile()
Called when a file is loaded into this view. Override this to handle file loading.
onLoadFile(file: TFile): Promise<void>
Promise that resolves when file is loaded
onUnloadFile()
Called when a file is unloaded from this view. Override this to handle cleanup.
onUnloadFile(file: TFile): Promise<void>
Promise that resolves when file is unloaded
onRename()
Called when the file is renamed. Override this to handle file renames.
onRename(file: TFile): Promise<void>
Promise that resolves when rename is handled
canAcceptExtension()
Determines whether this view can display files with the given extension.
canAcceptExtension(extension: string): boolean
The file extension (without the dot)
Whether this view can display files with the given extension
Since: 0.9.7
Creating a Custom FileView
Here’s an example of creating a custom file view for a specific file type:
import { FileView, TFile, WorkspaceLeaf } from 'obsidian';
const VIEW_TYPE_JSON = 'json-view';
class JsonView extends FileView {
data: any;
constructor(leaf: WorkspaceLeaf) {
super(leaf);
}
getViewType() {
return VIEW_TYPE_JSON;
}
getDisplayText() {
return this.file?.basename ?? 'JSON View';
}
async onLoadFile(file: TFile) {
// Read and parse the JSON file
const content = await this.app.vault.read(file);
try {
this.data = JSON.parse(content);
this.renderData();
} catch (e) {
this.contentEl.setText('Invalid JSON: ' + e.message);
}
}
async onUnloadFile(file: TFile) {
// Clean up
this.data = null;
this.contentEl.empty();
}
renderData() {
const container = this.contentEl;
container.empty();
container.createEl('h4', { text: 'JSON Data' });
container.createEl('pre', {
text: JSON.stringify(this.data, null, 2)
});
}
canAcceptExtension(extension: string) {
return extension === 'json';
}
async onClose() {
this.data = null;
}
}
Registering a FileView
import { Plugin } from 'obsidian';
export default class JsonViewPlugin extends Plugin {
async onload() {
// Register the view
this.registerView(
VIEW_TYPE_JSON,
(leaf) => new JsonView(leaf)
);
// Register the view for .json files
this.registerExtensions(['json'], VIEW_TYPE_JSON);
// Add command to open JSON files in this view
this.addCommand({
id: 'open-as-json',
name: 'Open as JSON view',
callback: () => {
const file = this.app.workspace.getActiveFile();
if (file && file.extension === 'json') {
const leaf = this.app.workspace.getLeaf(false);
leaf.setViewState({
type: VIEW_TYPE_JSON,
state: { file: file.path }
});
}
}
});
}
async onunload() {
this.app.workspace.detachLeavesOfType(VIEW_TYPE_JSON);
}
}
Handling File Changes
You can listen to file changes and update your view:
class MyFileView extends FileView {
async onLoadFile(file: TFile) {
// Initial load
await this.loadAndRender(file);
// Listen for changes
this.registerEvent(
this.app.vault.on('modify', (modifiedFile) => {
if (modifiedFile === this.file) {
this.loadAndRender(modifiedFile);
}
})
);
}
async loadAndRender(file: TFile) {
const content = await this.app.vault.read(file);
// Render content
this.contentEl.empty();
this.contentEl.setText(content);
}
}
State Management
class MyFileView extends FileView {
scrollPosition: number = 0;
getState() {
const state = super.getState();
state.scrollPosition = this.scrollPosition;
return state;
}
async setState(state: any, result: ViewStateResult) {
await super.setState(state, result);
this.scrollPosition = state.scrollPosition || 0;
// Restore scroll position after render
this.contentEl.scrollTop = this.scrollPosition;
}
onResize() {
// Save scroll position
this.scrollPosition = this.contentEl.scrollTop;
}
}
Best Practices
1. Always check if file exists
async onLoadFile(file: TFile) {
if (!file) return;
const exists = await this.app.vault.adapter.exists(file.path);
if (!exists) {
this.contentEl.setText('File not found');
return;
}
// Load file...
}
2. Handle errors gracefully
async onLoadFile(file: TFile) {
try {
const content = await this.app.vault.read(file);
this.renderContent(content);
} catch (error) {
this.contentEl.setText(`Error loading file: ${error.message}`);
}
}
3. Clean up resources
async onUnloadFile(file: TFile) {
// Cancel any pending operations
this.cancelPendingOperations();
// Clear data
this.data = null;
// Clear UI
this.contentEl.empty();
}
4. Update display text on rename
async onRename(file: TFile) {
// The leaf will automatically update the tab title
// You may want to update internal references
this.file = file;
}
See Also