Journal d'un Terrien

Web log de Serge Boisse

On line depuis 1992 !

Publicité
Si cette page vous a plu, Copiez son adresse et partagez-la !
http://sboisse.free.fr/trucs et astuces/Obsidian/Plugins/DataviewJS.php
Savez-vous quels sont les articles les plus vendus sur Amazon.fr ?
DataviewJS

DataviewJS

DataviewJS n'est pas à proprement parler un plugin mais une syntaxe et une fonctionnalité spécifique du plugin Dataview d'Obsidian, permettant d'inclure du javascript dans le code.

API DataviewJS

in block

il faut mettre les instructions dans un bloc de code markdown de nom dataviewjs

Note

La variable dv donne accès à l'API Dataview :
cf https://blacksmithgu.github.io/obsidian-dataview/api/code-reference/
Par exemple dv.app donne accès à l'API obsidian !
La variable window est également accessible, ainsi que this et notamment this.file, par exemple this.file.ctime
idem pour document, par exemple document.createElement("div")

exemple :

const div = document.createElement("div");
div.innerHTML = "**div**";// pas affiché immédiatement mais...
dv.el("span",div) // en fait la div est un fils de celui-ci
div.innerHTML = "**div2**";
**div2**

inline

commencer par $= dans un bloc de code.
ci dessous la suite était "Tu es sur la page: " + dv.fileLink(dv.current().file.path, false) + ".";
Texte avant Tu es sur la page DataviewJS. Texte après.

DataArray

La structure de données DataArray est comme les Array dans js, mais donne en plus accès à d'autres opérations comme sort, groupBy, where...

dv.page(_tag ou page_) donne accès à une DataArray.

Vous pouvez également convertir explicitement un tableau JavaScript normal en un tableau Dataview en utilisant dv.array(<array>). Si vous voulez reconvertir un tableau de données "montableau" en tableau normal, utilisez montableau.array().

Les tableaux de données supportent l'indexation classique comme les tableaux normaux (comme array[0]), mais surtout, ils supportent le "swizzling" (arrosage) du langage de requête : si vous indexez un tableau de données avec un nom de champ (comme array.field), chaque élément du tableau est automatiquement associé à field, en aplatissant field s'il s'agit aussi d'un tableau.

Par exemple, dv.pages().file.name renverra un tableau de données de tous les noms de fichiers de votre coffre-fort ; dv.pages("#books").genres renvoie tous les genres de livres listées dans des pages avec le tag "#book"

CodeBlock

Dataview Query

dv.pages(source)
dv.current()
dv.pagePaths(source)
dv.page(path) relatif à la racine du coffre

On pourra les afficher avec dv.list() ou dv.table() comme dans le Dataview normal (sans js)

afficher la structure de la note !

avec les liens in et out, les tags....
(mis en commentaire ci-dessous parce que très long)

//dv.paragraph(dv.current());

Liste des fichiers par répertoire ou tag

// on peut mettre plusieurs répertoires entre "" en les séparant par OR dans les '...' . 
// Il faut mettre les full path
// on peut aussi utiliser des tags mais sans les "" e.g. let reps = '"fun" OR #fun'; 
let reps = '"_Créer/Blog, vidéos, site" OR "_voyager"'; 
dv.list(dv.pages(reps).file.link)//idem dataview

Pour une arborescence complète et récursive d'un dossier, voir liste des notes par répertoire(lien privé)

Lister des fichiers (par répertoire ou tag) avec leur titre "h1"

C'est à dire le premier heading de la note

let reps = '"_Créer/Blog, vidéos, site" OR "_voyager"'; 

function titre(path) {
	let t = dv.app.metadataCache.getCache(path).headings[0];
	return t?t.heading:path;
}
// liste avec le titre h1 et les liens
let links = dv.pages(reps).file.link;// full path needeed
dv.list(links.map(li => {
		return `<a class ="internal-link" href="${li.path}">${titre(li.path)}</a>`
}));

C'est comme cela que j'ai créé les pages "index" de ce site.

modifier le texte visible d'un lien

dv.fileLink('atomic-habits', false, 'Atomic Habits') équivalent à [[atomic-habits|Atomic Habits]]
On peut aussi utiliser link.withDisplay(display)

Réutilisation du code d'un bloc dans un autre

Création d'élements HTML dans le doc :

dv.el(type_element_html, contenu) Le type est un type d'élément html (i,b,p,span...) voir plus loin des exemples
Notons que si le contenu est en markdown il sera rendu !
Si le contenu est un Array il sera traduit en une liste à points (pas terrible)
exemple dv.el("span",["pi$=\\pi$",["bb"],"cc"])

  • pi
    • bb
  • cc

dv.paragraph (text markdown) MathJax fonctionne ! Attention aux \ qu'il faut doubler

var elt = dv.paragraph("maths : $\\pi= 3.14\\ldots$")
elt.style = "color:green"; 

résultat :

maths :

dv.header(level,text) exemple :

let elt = dv.header(4,"header 4")
elt.style = "color:blue"; 

résultat :

header 4

dv.span(textmarkdown) :

un span italiqueun autre

exemples

p

bold ital mark cite quote rt stroke var span toto span titi
div toto
style="color:blue;"hello"
texte formatté
ligne 2

éléments interactifs, pas toujours documentés :

dv.el("button","coucou"); // WOW ! non documenté
// input marche... pas documenté non plus
dv.el("input","coucou");// coucou pas affiché. non documenté
let elt=dv.el("input","");
	elt.type="range"; //ca marche !

Ci dessus les éléments sont affichés, mais pas interactifs. C'est normal. pour que ça marche il faut déclarer des variables. cf interaction :

On remarque que pour dv.el, si le type est "b" ou "i" ou "abbr", ou "cite" ou "em", toutes les écritures suivantes seront dans le même élément html. Ce n'est pas le cas pour "p" ou "paragraph" ou "div" qui forcent un retour à la ligne.
On remarque aussi que pour "paragraph" on peut y mettre du latex mais il faut escaper les "\" comme en markdown.
exemple

const texte = `coucou $\\sqrt 2$
*serge*`;
[[Dataview]]
dv.paragraph(texte);

coucou
serge
Dataview

styles

il est possible de récupérer les éléments html et de les modifier / supprimer, changer le style...

var elt=dv.el("p","ceci sera remplacé par un texte orange");
elt.style.color = 'orange';
elt.innerHTML = "orange ou <sup>tang</sup> ?";

orange ou tang ?

Si on met dv.paragraph ça ne marche pas...


Récursion

on peut même créer des blocs de code qui seront réinterprétés récursivement par le postprocesseur markdown !

dv.el("p",
"```dataviewjs\ndv.el('i','ce texte est créé par DataviewJS dans DataviewJS')\n```") 

ce texte est créé par DataviewJS dans DataviewJS


interaction

const bouton=dv.el("button","coucou");
const inp = dv.el("input");
inp.placeholder="entre quelque chose";
const sortie = dv.el("p","???");

bouton.addEventListener('click', (e) => 
						{ inp.value="coucou, édite moi!"});
inp.addEventListener('input', (e) => 
					 {sortie.innerHTML = e.target.value});

Clique le bouton et/ou tape du texte dans le champ ci dessous :

???

ça marche !

Essayons avec un slider et une progressBar ::

@ Slider et progressBar en DataviewJS
@ Slider et progressBar en DataviewJS
50%-

YES !
cf @ Slider et progressBar en DataviewJS(lien privé)

Si on veut du markdown dynamique...

Attention

ça ne marche pas avec mon plugin à cause du //sortie = dv.paragraph dans le gestionnaire d'évènement.
#TBC


Listes et tables

dv.list(elements)

	dv.list([1, 2, 3]) => list of 1, 2, 3
	dv.list(dv.pages().file.name) => list of all file names
	dv.list(dv.pages().file.link) => list of all file links
	dv.list(dv.pages("#book").where(p => p.rating > 7)) => list of all books with rating greater than 7

Il y a ça aussi, :
dv.taskList(tasks, groupByFile)

par exemple :

// List all tasks from pages marked '#project'
dv.taskList(dv.pages("#project").file.tasks)

// List all *uncompleted* tasks from pages marked #project
dv.taskList(dv.pages("#project").file.tasks
    .where(t => !t.completed))

// List all tasks tagged with '#tag' from pages marked #project
dv.taskList(dv.pages("#project").file.tasks
    .where(t => t.text.includes("#tag")))

dv.table(headers, elements)

//Render a simple table of book info sorted by rating.
dv.table(["File", "Genre", "Time Read", "Rating"],dv.pages("#book")
		    .sort(b => b.rating)
		    .map(b => [b.file.link, b.genre, b["time-read"],
		    b.rating]))

Utilitaires

dv.array(value) --> DataArray
dv.isArray(arg)
dv.fileLink(path, [embed?], [display-name]
dv.date(text) : exemples :
dv.date("2021-08-08") => DateTime for August 8th, 2021
dv.date(dv.fileLink("2021-08-07")) => dateTime for August 7th, 2021

dv.compare(a,b) -> Returns a negative value if a < b, 0 if a = b, and a positive value if a > b.
dv.equal(a, b)

dv.executeJS

dv.executeJS("code"); interprète la chaine et l'exécute.
exemple :

dv.executeJs("dv.span('toto'+1)");
toto1
dv.io

fournit des fonctions pour charger des fichiers textes, csv... Attention functions asynchrones : Utiliser await J'avoue que la doc n'est pas claire à ce sujet....
dv.io.csv(path, [origin-file])
dv.io.load(path, [origin-file])

refresh de la page

dv.app.workspace.activeLeaf.rebuildView();
voir aussi https://forum.obsidian.md/t/refresh-and-toggle-dataview-source/54786/2

This time will keep updating while the note is open:

11:00:39

Accès aux autres plugins

essayons d'accéder à un autre plugin...

//console.dir(require("obsidian"));// ca marche mais qu'en faire ?
// require("musicPlugin"); marche pas. même si le plugin export ce nom
//ABCJS.renderAbc("") marche pas

Pas encore trouvé comment.... SI !
console.log(app.plugins.plugins); donne la liste de tous les plugins avec accès à leur API, par exemple app.plugins.plugins.dataview.api ! et même code ! (prototype)


Evaluation

dv.tryEvaluate(expression, [context]`
dv.evaluate(expression, [context])` retourne un objet Result

dv.tryEvaluate("2 + 2")  => 4
dv.tryEvaluate("x + 2", {x: 3}) => 5
dv.tryEvaluate("length(this.file.tasks)") => number of tasks in the current file
dv.evaluate('link(target)', { target: "Okay" }) = [[Okay]]

Exemple (ça devrait afficher 5 ci dessous)

5

dv.view

dv.view(path,input) charge un fichier javascript (path/view.js) et lui passe dv et input !!
eg. dv.view("views/custom", { arg1: ..., arg2: ... });
Si tu souhaites également inclure du CSS personnalisé dans la vue, tu peux passer un chemin vers un dossier contenant view.js et view.css ; le CSS sera ajouté à la vue automatiquement :
views/custom
-> view.js
-> view.css
Dans le script on peut utiliser les variables input et dv. Le script peut ainsi appeler dv.el(), etc. pour inclure du html dans la note.
cf Appel d'un script js depuis Obsidian et test dv.view(lien privé)
j'ai créé le script "contenu répertoire" qui s'utilise ainsi (dans un bloc dataviewjs)

await dv.view("contenu répertoire","planete");

Si on omet le second argument, le script liste le contenu du répertoire parent de la note.


TEST appel des scripts

Appel d'un script js depuis Obsidian

On utilise ici la fonction dv.view(viewName,input) qui cherche un fichier, l'exécute et passe le résultat en HTML (dans un nouvel élément span)

Essayons avec JS/DataviewJS/essai1/view.js (fichier local)(lien privé)
et dans le fichier essai1/view.js :

function toto() {
	dv.el("b","coucou:"+input);
}
toto();
coucou:Serge

Ca marche !!! 👌 ça ne se rafraichit pas quand on change le script mais Ok si on modifie la note

En fait, dv.view(viewName,input) cherche d'abord dans le répertoire se situe le fichier .md, un fichier qui s'appelerait ${viewName}.js d'abord, ensuite il cherche ${viewName}/view.js. S'il ne trouve pas, il prend viewName pour un path (relatif à la racine du coffre) et cherche un fichier "view.js" dans ce path. Ce fichier est évalué...
Le résultat est alors traité comme du HTML qui sera inclu dans un nouvel élément span.
Cela peut poser problème lorsque input est interactif, car à chaque modif un nouveau span est créé. Il est cependant possible de supprimer le précédent... cf créneaux et briques > crénaux et fractions

second essai

avec des modules et des classes et import
code mis en commentaire

Là, ça marche pas... 😒 SyntaxError: Cannot use import statement outside a module (import dans le fichier views n'est pas reconnu)
C'est un bug assez frequent. Lorsqu'il s'agit d'un script appellé depuis une page html, on peut utiliser

 <script src="main.js" type="module"></script>

troisième essai

avec require (donc node.js). On va essayer d'appeler une fonction dans un autre fichier depuis view.js
Dans essai3/view.js :

var totofile = require(app.vault.adapter.basePath + "_NE PAS UPLOADER/JS/dataviewJS/essai3/toto.js");
function truc() {
	dv.el("b","coucou: "+totofile(dv));
}
truc();

Dans toto.js:

function toto() {
	return "toto_ok";
}
module.exports = toto;

et dataview:

coucou: toto_ok

Ouf, ça marche ! 👌 Donc dataviews utilise Node.

Quatrième essai

avec des classes mais avec require
et dataview:
dans essai4/views.js:

var path = app.vault.adapter.basePath + "/_NE PAS UPLOADER/JS/dataviewJS/essai4/Toto.js";
var Toto = require(path);
// les variables dv et input sont visibles ici
function truc() {
	dv.el("b","init: ");
	const t = new Toto();
	dv.el("b","(coucou de truc"+input+")");
	t.recuperedv(dv);
	dv.paragraph(t.meth(123));
}
truc();

dans Toto.js:

class Toto {
	constructor() {
	}
	recuperedv(dv) {
		this.dv = dv;// récupère dataview
	}
	 meth(val) {
		this.dv.el("b","(coucou de Toto)");
		return "(toto_ok_"+val+")";

	}
}
module.exports = Toto

et dataview :

init:

(coucou de truc4)

(coucou de Toto)

(toto_ok_123)

Finalement après des centaines (si si) d'essais, OK ! 👌 mais il est nécessaire de fermer Obsidian et de le relancer lorsqu'on change le code de l'un des scripts à cause du cache de Node.

On remarque que dv.el() écrit toujours dans le même élément !

API Obsidian

Elle est accessible via dv.app !!! ou this.app
cf API Obsidian. (page web)
Par exemple app.plugins.plugins.dataview.api donne accès aux mêmes fonctions que dv, à la différence près qu'il n'y a pas de fichier implicite pour les requêtes dataview.

Affichage du titre de la note ! (premier en-tête)

const {path} =
	//l'une ou l'autre
	//dv.app.workspace.getActiveFile();
	"art.md";
var elt=dv.el("p","hello");
elt.innerHTML = 
//console.log(
this.app.metadataCache.getCache(path).headings[0].heading
//);

Vive l'Art avec un grand A !

this.app.metadataCache.getCache(path) a la structure suivante :

Pasted image 20241115190709.png

luxon (pour les dates)

J'ai pas trouvé comment ça marche.

const { DateTime, Duration } = luxon
const otherDate = DateTime.fromObject({ years: 3000, hours: 13, minutes: 37 })
const thatDuration = Duration.fromISOTime('13:37')
const thatIntervalButActuallyAnotherDuration = this.date.diffNow() // documentation says this returns an Interval but it's actually a Duration. Don't know why.
console.log(thatIntervalButActuallyAnotherDuration.plus(thatDuration).toObject())

Templater dans Dataviewjs !

Il est possible de récupérer l'objet "tp" de Templater par :
const tp = app.plugins.plugins["templater-obsidian"].templater.current_functions_object

on peut alors appeler tp.system.clipboard, tp.system.prompt, tp.system.suggester, etc

ajouter une fonction à DQL (Dataview query langage)

Publicité
Commentaires

Commentaires (0) :

Page :



Ajouter un commentaire (pas besoin de s'enregistrer)

Pseudo :
Message :


image de protection
En cliquant sur le bouton "Envoyer" vous acceptez les conditions suivantes : Ne pas poster de message injurieux, obscène ou contraire à la loi, ni de liens vers de tels sites. Respecter la "netiquette", ne pas usurper le pseudo d'une autre personne, respecter les posts faits par les autres. L'auteur du site se réserve le droit de supprimer un ou plusieurs posts à tout moment. Merci !
Ah oui : le bbcode et le html genre <br>, <a href=...>, <b>b etc. ne fonctionnent pas dans les commentaires. C'est voulu.
< Retour en haut de la page