Right now i have the following lines of code:
private async Task SetImageUsingStreamingAsync()
{
var imageStream = await GetImageStreamAsync();
var dotnetImageStream = new DotNetStreamReference(imageStream);
await JSRuntime.InvokeVoidAsync("setImageUsingStreaming",
"image1", dotnetImageStream);
}
Above snippet gets a Stream, and repackages that stream into DotNetStreamReferance, and then passes that DotNetStreamReferance to a JS function:
async function setImageUsingStreaming(imageElementId, imageStream) {
const arrayBuffer = await imageStream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
document.getElementById(imageElementId).src = url;
}
which populates an html element by ID. This is all fine, and works, but if i want to have a loadingscreen while i get the streams from the DB things get messy:
if(isloading)
{
show loadingscreen
}
else
{
foreach(string in listofstrings)
{
<img id="somename"></img>
}
}
the img element does not "show up", and causes an error in the js snippet if the bool isloading is not changed to false BEFORE populating the images on the site. Which kind of defeats the purpose with a loadingscreen.
Is there a smarter way of doing this? Preferably without using javascript, since i dont really know that language and cannot modify it to my needs.
The above code is a direct reference from .net documentation on blazor(not the loading part, but the stream to image part): https://learn.microsoft.com/en-us/aspnet/core/blazor/images?view=aspnetcore-6.0
if (isloading)
{
// show loadingscreen
}
else
{
// Populate a list with image elements...
List imageElements = new List();
foreach(string in listofstrings)
{
Element image = new Element("img");
image.Id = "somename";
imageElements.Add(image);
}
// ...and append the list to the DOM using RenderElementsAsync
// to ensure that it is rendered only when the list is fully
// populated.
await JSRuntime.InvokeAsync("setImageUsingStreaming", imageElements, dotnetImageStream);
}
The idea is to populate an in-memory list of elements, and then append the list to the DOM in one go, by rendering it asynchronously using RenderElementsAsync.
Note: In your original code, you should invoke the JavaScript function setImageUsingStreaming() asynchronously. That is, you should use InvokeAsync instead of InvokeVoidAsync.
Let me know if this helps...
I figured it based on Yogi's answer in the comment section of my post:
there is absolutely no need to use JS here, i dont know why the blazor docs prefer it that way.
a simple sample on how i did:
private async Task PopulateImageFromStream(Stream stream)
{
MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
byte[] byteArray = ms.ToArray();
var b64String = Convert.ToBase64String(byteArray);
string imageURL = "data:image/png;base64," + b64String;
}
}
add the imageURL to an array, or simply declare it globaly to get it in HTML:
<img src="#imageURL">
Related
So I have built an REST API with working endpoints GET, POST, PUT and DELETE on an Express server and currently working on the clientside. The Express server is reading and writing to a JSON-file. So on the clientside I've done a HTML and a scriptfile, I've managed to make a GET-request and render what's in the JSON-file so far on the HTML. Also I've managed to make a inputform on the HTML and make a POST-request that makes a new object with all keys/values I would like the object to have. So my thought was that to make a PUT-request and update a certain object. Is it possible to use the HTML attribute contenteditable? At the part where I'm rendering everything from the script-file I'm in a for of loop and also making a addEventListener where I'd like to send the NEW information from the contenteditable element to the async/await-function that is making the PUT-request. But at all tries I'm only able to see the old information.
async function printCharacters(){
const get = await fetch ('http://localhost:3000/api')
const characters = await get.json()
for(const character of characters){
const characterContainers = document.createElement("div");
main.appendChild(characterContainers);
characterContainers.className = "characterContainer";
const characterName = document.createElement("p");
characterName.setAttribute("contenteditable", "true");
characterName.innerHTML = character.characterName;
characterContainers.appendChild(characterName);
const updateButton = document.createElement("button");
updateButton.className = "updateButton";
updateButton.innerText = "Update";
characterContainers.appendChild(updateButton);
updateButton.addEventListener("click", () => {
updateCharacter(characterName.innerHTML);
});
}
}
async function updateCharacter(data){
const response = await fetch (`http://localhost:3000/api/update/${data.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"characterName": data.characterName
})
})
return response.json();
};
I've tried making outside a function and then it's possible to console.log and catch the new information.
updateButton.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
const changedData = {
id: character.id,
characterName: characterName.innerHTML,
class: characterClass.innerHTML,
weapon: characterWeapon.innerHTML,
description: characterDescription.innerHTML
};
updateCharacter(changedData);
This was the working solution. I've tried before to get innerHTML both inside and outside the eventListener, but to get the updated information inside the element it has to be inside the eventListener and also the addition of preventDefault and stopPropagation.
I've been able to sort out the middle bit (the API seems to be called to just fine) along with the submenu displaying. Originally I thought that just the end part wasn't working but I'm now thinking that the selection part isn't either.
What am I doing wrong with the getSelection() and what do I need to do to insert a link into said selection? (to clarify, not to replace the text with a link, but to insert a link into the text)
//Open trigger to get menu
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Scry', 'serumVisions')
.addToUi();
}
//Installation trigger
function onInstall(e) {
onOpen(e);
}
//I'm not sure if I need to do this but in case; declare var elements first
var elements
// Get selected text (not working)
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
Logger.log(elements);
} else {
var elements = "Lack of selection"
Logger.log("Lack of selection");
}
}
//Test run
// insert here
// Search Function
function searchFunction(nameTag) {
// API call + inserted Value
let URL = "https://api.scryfall.com/cards/named?exact=" + nameTag;
// Grabbing response
let response = UrlFetchApp.fetch(URL, {muteHttpExceptions: true});
let json = response.getContentText();
// Translation
let data = JSON.parse(json);
// Jackpot
let link = data.scryfall_uri;
// Output
Logger.log(link);
}
// Test run
searchFunction("Lightning Bolt");
//Let's hope this works how I think it works
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
// Unsure what class I'm supposed to use, this doesn't
const insertLink = DocumentApp.getActiveDocument().getSelection().newRichTextValue()
.setLinkUrl(linkage);
Logger.log(linkage);
}
For the first part, I tried the getSelection() and getCursor() examples from the Google documentation but they don't seem to work, they all just keep returning null.
For the inserting link bit, I read all those classes from the Spreadsheet section of the documentation, at the time I was unaware but now knowing, I haven't been able to find a version of the same task for Google Docs. Maybe it works but I'm writing it wrong as well, idk.
Modification points:
In your script, the functions of getSelectedText() and searchFunction(nameTag) return no values. I think that this might be the reason for your current issue of they all just keep returning null..
elements of var elements = selection.getRangeElements(); is not text data.
DocumentApp.getActiveDocument().getSelection() has no method of newRichTextValue().
In the case of searchFunction("Lightning Bolt");, when the script is run, this function is always run. Please be careful about this.
When these points are reflected in your script, how about the following modification?
Modified script:
Please remove searchFunction("Lightning Bolt");. And, in this case, var elements is not used. Please be careful about this.
From your script, I guessed that in your situation, you might have wanted to run serumVisions(). And also, I thought that you might have wanted to run the individual function. So, I modified your script as follows.
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
var text = "";
if (selection) {
text = selection.getRangeElements()[0].getElement().asText().getText().trim();
Logger.log(text);
} else {
text = "Lack of selection"
Logger.log("Lack of selection");
}
return text;
}
function searchFunction(nameTag) {
let URL = "https://api.scryfall.com/cards/named?exact=" + encodeURIComponent(nameTag);
let response = UrlFetchApp.fetch(URL, { muteHttpExceptions: true });
let json = response.getContentText();
let data = JSON.parse(json);
let link = data.scryfall_uri;
Logger.log(link);
return link;
}
// Please run this function.
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
if (linkage) {
Logger.log(linkage);
DocumentApp.getActiveDocument().getSelection().getRangeElements()[0].getElement().asText().editAsText().setLinkUrl(linkage);
}
}
When you select the text of "Lightning Bolt" in the Google Document and run the function serumVisions(), the text of Lightning Bolt is retrieved, and the URL like https://scryfall.com/card/2x2/117/lightning-bolt?utm_source=api is retrieved. And, this link is set to the selected text of "Lightning Bolt".
Reference:
getSelection()
I'm generating PDF by using https://pdfgeneratorapi.com/.
Now I can show data one by one using this code.Can any one give me suggestion how can show all data with loop or any other way?
This below photos showing my template from pdfgenerator .
This is the code I'm using to generate PDF
let communicationWay1=[
{0:"dim"},
{1:"kal"}
];
let cstomerExpence1=[
{0:"dim"},
{1:"kal"}
];
let title="test";
let names="test";
let phone="test";
let email="test";
let maritalStatus="test";
let city="test";
let other="test";
const result = await wixData.query(collection)
.eq('main_user_email', $w('#mainE').text)
.find()
.then( (results) => {
if (results.totalCount>0) {
count=1;
// title=results.items[1].title;
names=results.items[0].names;
email=results.items[0].emial;
phone=results.items[0].phone;
maritalStatus=results.items[0].maritalStatus;
city=results.items[0].city;
other=results.items[0].cousterExpenses_other;
title=results.items[0].title;
communicationWay=results.items[0].communicationWay;
cstomerExpence=results.items[0].cstomerExpence;
}
if (results.totalCount>1) {
names1=results.items[1].names;
email1=results.items[1].emial;
phone1=results.items[1].phone;
maritalStatus1=results.items[1].maritalStatus;
city1=results.items[1].city;
other1=results.items[1].cousterExpenses_other;
title1=results.items[1].title;
communicationWay1=results.items[1].communicationWay;
cstomerExpence1=results.items[1].cstomerExpence;
}
} )
.catch( (err) => {
console.log(err);
} );
// Add your code for this event here:
const pdfUrl = await getPdfUrl
({title,names,email,phone,city,maritalStatus,other,communicationWay,cstomerExpence,title1,
names1,email1,phone1,city1,maritalStatus1,other1,communicationWay1,cstomerExpence1
});
if (count===0) { $w("#text21").show();}
else{ $w("#downloadButton").link=wixLocation.to(pdfUrl);}
BELOW CODE IS BACKEND CODE/JSW CODE.
Also I want to open pdf in new tab. I know "_blank" method can be used to open a new tab.But I'm not sure how to add it with the url
import PDFGeneratorAPI from 'pdf-generator-api'
const apiKey = 'MYKEY';
const apiSecret = 'MYAPISECRET';
const baseUrl = 'https://us1.pdfgeneratorapi.com/api/v3/';
const workspace = "HELLO#gmail.com";
const templateID = "MYTEMPLATEID";
let Client = new PDFGeneratorAPI(apiKey, apiSecret)
Client.setBaseUrl(baseUrl)
Client.setWorkspace(workspace)
export async function getPdfUrl(data) {
const {response} = await Client.output(templateID, data, undefined, undefined, {output: 'url'})
return response
}
Just put it in a while loop with a boolean condition.
You can create a variable, for example allShowed, and set its value to False. After that, create another variable, for example numberOfDataToShow, and set it as the number of elements you want to display. Then create a counter, countShowed, initialized with 0 as its value.
Now create a while loop: while allShowed value is False, you loop (and add data).
Everytime a piece of your data is showed, you increment the value of countShowed (and set it to go on adding/showing data). When countShowed will have the exact same value of numberOfDataToShow, set allShowed to True. The loop will interrupt and all your data will be showed.
You would need to use the Container or Table component in PDF Generator API to iterate over a list of items. As #JustCallMeA said you need to send an array of items. PDF Generator API now has an official Wix Velo (previously Corvid) tutorial with a demo page: https://support.pdfgeneratorapi.com/en/article/how-to-integrate-with-wix-velo-13s8135
I have a img src which just looks like this:
<img [src]="currentImg" />
Which traverses a JSON and grabs the image url.
I have a button underneath the image directly that refreshes the JSON feed.
<a (click)="refreshFeed(url)">Refresh feed</a>
refreshFeed(url) {
this.subscriptions.add(
this.jsonService.getJson().subscribe(() => {
this.currentImg = url.properties.img;
return this.currentImg;
})
)
}
However, while I can see it's fetching the new json, it doesn't seem to update the image url and change it's source at all, also, the image url will always be the same name in the json (img: "image.jpeg")
Any help is appreciated.
First, subscribe is a void function, there is no meaning for it to return a value.
Second, It seems like you're using a different Change Detection Strategy somewhere along the component tree.
Try doing the following, if it works then you definitely use the onPush strategy.
constructor(
...,
private cd: ChangeDetectorRef,
...
) {
...
}
refreshFeed(url: string) {
this.jsonService.getJson()
.pipe(take(1))
.subscribe(data => {
this.currentImg = data.properties.img;
this.cd.detectChanges();
})
}
I used the Rxjs "take" pipe instead of destroying subscriptions.
This is a better approach, try using it where you can.
I would not use any ngIfs. The image will jump every time the user generates a new image.
Hope this helps!
Try like this:
.html
<img [src]="currentImg" *ngIf="showImage"/>
.ts
showImage:boolean
refreshFeed(url) {
this.showImage = false;
this.subscriptions.add(
this.jsonService.getJson().subscribe(() => {
this.currentImg = url.properties.img;
this.showImage = true;
return this.currentImg;
})
)
}
I'm making a js-based epub reader as a weekend project and I'm trying to change the src attribute of the images on each of the book's pages from image urls to data URI's loaded from the epub zip. Here's my function:
//page contents is just an html string of the book's page
pageContents = GlobalZipLoader.load('epub.zip://' + pageLocation);
pageContents = replaceImages(pageContents)
...
function replaceImages(pageContents){
$(pageContents).find('img').each(function(){
var domImage = $(this);
//this is something like ".../Images/img.jpg"
var imageLocation = domImage.attr('src');
//this returns the proper data-uri representation of the image
var dataUri = GlobalZipLoader.loadImage('epub.zip://' + imageLocation);
//this doesn't seem to "stick"
domImage.attr('src', dataUri);
});
return pageContents;
}
The returned pageContents from the replaceImages functions still has the old src attributes. I can provide more detail if needed but any help would be very much appreciated.
Correct answer thanks to The System Restart and Ilia G:
function replaceImages(pageContents) {
newContent = $(pageContent);
... manip ...
return newContent;
}
You don't need to clone it. Simply set pageContents = $(pageContents);, then perform your image replacement on pageContents, then return pageContents.html();
you should try to change the image src after image load complete;
and I think this is happening with in loadImage function.
According to your update question:
You don't need any clone(), I think. Just store pageContents in a tempContents variable and use that variable
Since pageContents is just a string, you'll need to return the modified version of it. Try this:
function replaceImages(pageContents){
// save jQuery object
var $pageContents = $(pageContents);
$pageContents.find('img').each(function(){
var domImage = $(this);
//this is something like ".../Images/img.jpg"
var imageLocation = domImage.attr('src');
//this returns the proper data-uri representation of the image
var dataUri = GlobalZipLoader.loadImage('epub.zip://' + imageLocation);
//this doesn't seem to "stick"
domImage.attr('src', dataUri);
});
// return contents of the modified jQuery object
return $pageContents.html();
}