React Native - Dynamic Image Source - javascript

I'm trying to loop through the SOURCE array with the map method, but I keep getting this error:
Unknown named module: '../images/one.jpeg'
Anyone know why this is happening? The file path in the require is definitely correct.
var SECTIONS = [
{
title: 'One',
fileName: 'one.jpeg',
},
{
title: 'Two',
fileName: 'two.jpeg',
},
{
title: 'Three',
fileName: 'three.jpeg',
},
{
title: 'Four',
fileName: 'four.jpeg',
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={require(`../images/${section.fileName}`)}
title={section.title}
/>
))}

I don't think this is possible because react native needs to know what to bundle ahead of time (AFAIK). However, you can require all the files in your array:
var SECTIONS = [
{
title: 'One',
file: require('../images/one.jpeg'),
},
{
title: 'Two',
file: require('../images/two.jpeg'),
},
{
title: 'Three',
file: require('../images/three.jpeg'),
},
{
title: 'Four',
file: require('../images/four.jpeg'),
},
];
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}

You can't use dynamic links. The best hack that i found to solve this is this:
var SECTIONS = {
One: {
title: 'One',
file: require('../images/one.jpeg'),
},
Two: {
title: 'Two',
file: require('../images/two.jpeg'),
},
Three: {
title: 'Three',
file: require('../images/three.jpeg'),
},
Four: {
title: 'Four',
file: require('../images/four.jpeg'),
},
};
{SECTIONS.map((section, i) => (
<CategoryCard
key={i}
source={section.file}
title={section.title}
/>
))}
That way, you can just use the files and if you have some kind of dynamic image selection, you can just use something like this
<Image source={SECTIONS[image.type]} />

try opening the file in separate browser using direct URL something like
http://<><>/imgages/one.jpg
You can also do something like this as well:
One working example for displaying dynamic images using react :
Example Click Here

Got a working solution, though not recommended for large images, works perfectly for (a lot of)small images.
Steps:
Convert the icon(s) to base64 string(s).
Create a JSON file with filename as the keys and the base64 strings as values.
(You can also store them to a local database)
e.g.
ImageData.json
{
"icon1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......==",
"icon2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQ.......=="
}
3.Import the json file to the place where you require the images dynamically.
e.g.
const imageData = require("./images/ImageData.json")
4: Get/generate the key/filename at runtime. and get the image source.
e.g.
const imageSrc = imageData[keyname]
5: Generate a image dynamically at runtime.
e.g.
<Image style={{ width: 70, height: 70, resizeMode: Image.resizeMode.contain }} source={ uri: imageSrc } />
Done..
Extra..
Written a helper python script to automate the json file creation.
import base64
import os
directory = os.fsencode('.')
with open('ImagesData.json', 'wb') as jsonFile:
jsonFile.write(bytes('{', 'utf-8'))
written = False
for file in os.listdir(directory):
filename = os.fsdecode(file)
if filename.endswith('.png'):
with open(filename, "rb") as image_file:
if written:
jsonFile.write(bytes(',\n','utf-8'))
encoded_string = base64.b64encode(image_file.read())
jsonFile.write(bytes(('"' +filename+ '":'), 'utf-8'))
jsonFile.write(bytes('"data:image/png;base64,', 'utf-8') + encoded_string + bytes('"', 'utf-8'))
written = True
jsonFile.write(bytes('}', 'utf-8'))
Copy the script to the image folder and run the script (requires python 3.6).
A json file will the created with image name as key and base64 string as values.
Copy the file to project and use (You can delete the images after that).
Use the json file as mentioned above.

I had the same problem but my situation was a little different. I had an array of different objects that needed dynamic images. I was already mapping the array, but I needed to match the images to that array based off of name. It was a little hacky, but this is how I went about it.
First, in my parent component I created a function to render a component for my array of objects. I passed the objects data into a prop called "object".
In my case I knew what my data was and I needed to match the corresponding image to the object that was being pulled off of an external api that I was grabbing my data from.
renderObjects() {
return this.state.objects.map(object => (
<ObjectListDetail
key={object.id}
next
props={this.props}
object={object}
/>
));
}
In my ObjectListDetail component, I created a variable called icons, which was another array of objects. This time, I created a name property that would match the object being passed to the component from the parent and then had a second key called source in which I provided the path to the image. It went something like this.
var icons = [
{ name: "BTC", source: Images.btc },
{ name: "ETH", source: Images.eth },
{ name: "ETC", source: Images.etc },
{ name: "ZRX", source: Images.zrx },
{ name: "USDC", source: Images.usdc },
{ name: "LTC", source: Images.ltc },
{ name: "BCH", source: Images.bch },
{ name: "USD", source: Images.usd }
];
NOTE *** I had already required all of my images into a separate file for my entire app and imported them at the top.
I then created a variable called imgSrc and filtered the result to match the name of the object i was passing to the child component.
var imgSrc = icons.filter(
icon => icon.name === props.wallet.name
)
I then created an Image component and in the source requirement I called the result of the filter and pointed it to the source.
<Image source={imgSrc[0].source} />
That is how I achieved dynamic image rendering within my application.
Its probably not the best way to do things, and I am still kinda new at this, but I would love any criticism

Related

How to Export HTML to Word Document with React js

Hello I have HTML data in my React App and want to export this in word document and download at button click event. can any one help to make this one please.
let element = (
<p style={{fontSize: "10px", color:"#000"}}>
Your investment in the maintenance package is just , billed directly to your credit card.
</p>
)
const generateDocx = async () => {
const doc = new Document({
sections: [
{
properties: {},
children: [
new Paragraph({
children: [
new TextRun(element),
],
}),
],
},
],
});
const blob = await Packer.toBlob(doc);
return blob;
};
how to save as .docx file instead of return blob
You can try using the Open XML SDK if you need to build Word documents on the fly. You may search for any libraries in JavaScript that allows creating Word documents like you could do using that SDK, see https://openxmldeveloper.org/. For example, you may find the Creating OpenXML documents using JavaScript post helpful.

Import Multiple images and export array

Sorry if it seems a bit confusing, I'll try to be as simple and put as much demonstration as possible.
The problem
I have a file that needs to import a bunch of images (for now 65 images). In order not to be a manual process, even when adding a new image in the folder and already being 'automatically detected' I created a function to import everything. However, I need to 'export' this function into an object like this:
const brandsAdds = [
{
label: "brand1",
value: "brand1",
logo: Object,
},
{
label: "brand2",
value: "brand2",
logo: Object,
},
...
];
How am I importing all the images:
// Import all images in folder
function importAll(r) {
let images = {};
r.keys().map((item, index) => {
images[item.replace("./", "").slice(0, -4)] = r(item).default;
});
return images;
}
// Variable for easy access to data
const image = importAll(
require.context("../", false, /\.(png|jpe?g|svg|gif)$/)
);
Demo
Automated process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index.js
Result:
Print result: what must be converted and how
Manual process:
https://codesandbox.io/s/frosty-sun-n75j5?file=/pages/index_manual.js
Result:
Print result manual process
I'm using next.js and this file that will export an array object will be used in another component. The file is currently over 300 lines long as I'm calling it image by image.
Would there be any way to import all the images by adding them to the array with "label:" and "value:" with the filename and "logo:" with the image path (next.js)?

iterating over data including image in Gatsby

In Gatsby I would like to iterate over an array, which contains objects. One of the properties of each object would be an image. I would like to be able to use Gatsby Image.
Here is one example of when I'd like to do so: a page on a website with a gallery of images, each image opens a particular associated video when clicked. Perhaps I'd like 20, 50, or even 100+ objects in the array:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: // HOW TO ACHIEVE?
},
// Many more objects
]
videos.map((item) => {
return (
<Img
key={item.id}
fluid= // HOW TO ACHIEVE?
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})
I understand how to query for single images with GraphQL; or how to query multiple images and use aliases; or how to query all images from a folder. But I have't worked out how to achieve my goal. There's probably a better way. Thanks in advance.
To use internal images in gatsby-image you need to allow Gatsby and their transformers and sharps to know where the images are located using the gatsby-source-filesystem. This will create queryable nodes from your images and will allow you to use gatsby-image with them.
Applied to your case, you need to put all images in the same folder, let's say /src/images and:
const path = require(`path`)
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src`, `images`),
},
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
],
}
There, your JSON object will look like:
const videos = [
{
id: 1,
name: 'Festival 2018',
url: 'https://www.youtube.com',
img: '/relative/path/to/your/image.extension',
extension: 'png'
},
// Many more objects
]
Note: thanks John for the clarification about the extension field. Source: https://stackoverflow.com/a/56012718/13714522
In your case, since you are using a JSON-based source, you will need to add the gatsby-transformer-plugin. The configuration will look like:
module.exports = {
plugins: [
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/data/`,
},
},
],
}
Note: assuming that the JSON is placed in /src/data
Also assuming that your JSON file is named data.json, if everything is properly set, Gatsby will create a GraphQL node called allDataJson. Then you only last to create a query (page query or static query) with the following content:
{
allDataJson {
edges {
node {
name
url
id
img {
childImageSharp {
fluid(maxWidth: 1000, quality: 100) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
Note: check your exact query in the localhost:8000/___graphql playground
If your paths are correct, Gatsby will create the childImageSharp node which will allow you to use your own internal images within gatsby-image feature. Since your queried data is stored inside props.data, your final loop should look like:
props.data.allDataJson.edges.node.map((item) => {
return (
<Img
key={item.id}
fluid={item.img.childImageSharp.fluid}
alt={item.name}
onClick={() => openPlayer(item.url)}
/>
)
})

How do you create edgeless graphql element in Gatsby?

The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help
Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.

Vue Js v-model not working with array created in Laravel model

I'm currently in the process of building a CMS using Laravel and Vue JS which build forms dynamically based on an array of data created in the Laravel model. Here is an example:
class TheBuilding extends Model
{
protected $fillable = [...];
public function formFields(){
$fields = [
[
'label' => 'Title',
'name' => 'title',
'component' => 'input_text'
],
[
'label' => 'Content',
'name' => 'content',
'component' => 'input_textarea'
],
[
'label' => 'Main Image',
'name' => 'main_image',
'component' => 'input_single_upload'
],
[
'label' => 'Gallery',
'name' => 'gallery',
'component' => 'input_multiple_upload',
'meta' => [
[
'type' => 'text',
'name' => 'caption',
'label' => 'Caption'
]
]
],
];
return $fields;
}
}
Basically this array gets passed into Vue JS and parsed to dynamically display Vue JS form components accordingly. This has been working great but I've come across an interesting issue with the Gallery multiple upload component which needs the ability to assign captions to images.
To fast forward a bit, I'm at the point where I have an array of uploaded files which get iterated through and displayed on the page, and then I have the input textfield for the caption underneath.
Here's my component (edited to show the relevant bits):
<template>
<div class="row">
<div v-for="(file, i) in files">
<img :src="file.file" >
<div v-for="meta in file.meta">
<input v-if="meta.type == 'text'" type="text" v-model="meta.value">
</div>
</div>
</div>
</template>
<script>
export default{
computed:{
files(){
let uploads = [];
/*this.uploaded is just an array of filenames*/
this.uploaded.forEach((file, i) => {
let createdMeta = [
{
name:"caption",
type:"text",
value:''
}
];
uploads.push({file,meta:createdMeta});
});
return uploads;
}
},
props:{ ... },
mounted(){
//CODE THAT HANDLES DROPZONE UPLOAD
},
name: 'InputMultipleUpload',
data(){
return {
showUploadProgress:true,
}
}
}
</script>
The bit I'm focusing on is:
let createdMeta = [{
name:"caption",
type:"text",
value:''
}];
You'll notice here that I've created that array statically. If I do that, when I type in a caption textbox everything works fine and the caption value gets updated dynamically by v-model as expected. Essentially, I get the desired result and everything is good.
However, if I try and set this this dynamically from the created model ie:
let createdMeta = formFields;
where formFields is the reference to the model array, when I then type in the textbox it updates all other textboxes and values in the files array created. V-Model no longer seems to relate to the specific textbox.
So I guess the question I'm asking is:
a) Why is it behaving that way when I passed in the referenced array
b) Why does it work fine if I just manually create that array?
c) How can I get A to behave like B?
Thanks everyone, happy to clarify anything. I assume i'm missing a piece in the reactivity puzzle.
Cheers,
Lew
You should be using data not computed.
Try something like this:
<script>
export default {
props: {...},
mounted () {
//CODE THAT HANDLES DROPZONE UPLOAD
// 2. handlers should call `onUpload`
},
name: 'InputMultipleUpload',
data () {
return {
files: [], // 1. declaring `files` here makes it reactive
showUploadProgress: true,
}
},
methods: {
onUpload (file) {
const meta = createdMeta = [{
name: "caption",
type: "text",
value: ''
}]
this.files.push({file, meta}); // 3. push the new data onto the stack and the component will update
}
}
}
</script>
With this:
let createdMeta = formFields;
uploads.push({file,meta:createdMeta});
in a loop you actually just pass the reference to the same formFields array object and then binds all your inputs to it.
This is likely to work as it should, if you pass a new array copy for each input. You can do it easy this way:
let createdMeta = formFields.slice();
let createdMeta = JSON.parse(JSON.stringify(formFields))
Thanks to Rick in Slack, and Dave Steward too, I have refactoring to do but for the purpose of this thread here is the solution

Categories