Importing dynamic filenames into React-Native - react-native

I have a menu.json file with hundreds of items in it:
menu_items = [
{
name: "Category 1",
file: "cat1.json"
},
{
name: "Category 2",
file: "cat2.json"
},
{
name: "Category 3",
file: "cat3.json"
},
{
name: "Category 4",
file: "cat4.json"
},
{
name: "Category 5",
file: "cat15.json"
}
]
I am able to read the menu.json file. I then create a Scrollable List showing each of the entries. When a user clicks on the entry, I want to load another screen that imports the specified json file.
Currently, when someone clicks an item in the list, the following module is loaded:
<DisplayItem file_name={this.state.selected_file} />
Then in my DisplayItem I have the following:
const item_data = require('./' + this.props.file_name);
However, I get the error:
Invalid call at line 52: require(file)
Apparently, I am unable to do imports or requires when the filename is dynamic. The only solutions I have seen is to manually require all of them first of all.
I find this solution a bit challenging because then I end up with redundant data. Any time I update the menu.json file then I have to remember to make the exact same update to the module requiring all the json files.
Is there a more efficient way to do this that eliminates the redundancy issue?

As I know You can't require a file in react native from a dynamic value, because it will make the compiler doesn't know which file to be included when building the app.
As written in React-Native's Image documentation
In order for this to work, the image name in require has to be known
statically.
For example
// GOOD
<Image source={require('./my-icon.png')} />;
// BAD
var icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;
// GOOD
var icon = this.props.active ? require('./my-icon-active.png') : require('./my-icon-inactive.png');
<Image source={icon} />;

Related

Using a placeholder image in react native without state manipulation

So the part below is a code snippet. These images are rendered in a flatlist.
<Image
resizeMode='cover'
style={styles.itemImage}
source={(product && !_.isEmpty(imageSource) && imageSource.url) ? { uri: imageSource.url } : noProductImage}
/>
Now some URLs provided will be present but it may be a broken link, so it'll pass the condition. Now I solved this by adding states for each image source but if the list has 100 items then we need to maintain 100 different states that can potentially be the same. So if there some way to solve this without states. Like maybe check the URL in the conditional statement.
You can use one array in state for all images.
this.state = {
images : [
{id: 0, url: '...'},
{id: 1, url: '...'},
...
]
}
Maybe it's not the best solution, but in such case you will work only with one state.

React Admin - FileInput that uploads to a single flat key instead of an object

In react admin, if you have a FileInput, it requires a child field, and stores the file URL inside the child field. So for example
{
"photo": {
"url": "https://url-to-image.com"
}
}
and the corresponding react admin code is
<FileInput source='photo' label='Photo' placeholder={<p>Drop your image file here</p>}>
<ImageField source='url' title='title' />
</FileInput>
I want to upload to a single key called photoUrl instead of a nested object photo.url, which would look like this:
{
"photoUrl": "https://url-to-image.com"
}
How can I do this? When I omit the ImageField, I get an error that a single child is expected. I tried leaving source="" empty for the imagefield but it still produces an error.
Any ideas?
Try passing parse and format functions to FileInput props
<FileInput
source='photoUrl'
label='Photo'
placeholder={<p>Drop your image file here</p>}
parse={p => p.url}
format={url => ({url})}
>
<ImageField source='url' title='title' />
</FileInput>
https://marmelab.com/react-admin/Inputs.html#transforming-input-value-tofrom-record

why react native can't require image currently?

<Image source={require(rowData.avatar)} />
error : Unknown name module ‘xxxxxx’
Why can print out the path but can't read pictures?
Try
<Image source={(rowData.avatar)} />
Images cannot use dynamically generated sources. Assuming what you're trying to do is to load an image as part of your package, your code must read:
const avatar = require('./path/to/avatar.jpg');
Only then you can use avatar as your source a follows:
rowData = {
avatar: avatar
}
<Image source={rowData.avatar} />
If you know before hands what images are going to be needed in your app, I suggest that you create an asset file in which you add all your hardcoded require, such as:
// assets.js
return {
avatar1: require('./path/to/file.jpg'),
avatar2: require('./path/to/file.jpg'),
avatar3: require('./path/to/file.jpg'),
}
And then you would construct your rowData as follows:
import avatars from './assets';
rowData = {
avatar: avatars['avatar1']
}
Where you would likely replace avatar1 with a variable containing the key pointing to the avatar you're interested in.
Here is an asset file I used for one of my projects.

Titanium appcelerator model and collections persistance

I am about to retrieve datas from remote and create model and collections, here are each part of the app (the controller, the view and the model).
If i really understand using model in titanium is like storing into database so the data persists even if there is no internet connection after i get all datas.
Below code works well, it seems no data is displayed after connection is lost, so i ask myself what is the advantage of using models in titanium instead of using classic way : retrieve from xhr and display data ?
2- My second question (if i am wrong) after retrieving datas and storing into model, i can retrieve it without xhr again inside another page?
3- And the last one : is it a good practice to retrieve data from alloy.js and save to model because i need datas in all my app pages ?
THE CONTROLLER
// This is an istance of my xhr library
var XHR = require('xhr');
var xhr = new XHR();
$.win.addEventListener('open', function(){
url = 'mydomain.com/api/get_posts';
xhr.get(url, onSuccess, onError);
});
function onSuccess(response){
if(typeof response !== null ){
datas = JSON.stringify(response.data);
postsModel = [];
_.each(datas, function(data){
/* Create model */
postsModel.push(Alloy.createModel('mypostsmodel',{
title : data.title,
id : data.id
}));
});
$.posts.reset(postsModel);
}
}
** THE VIEW **
<Alloy>
<Collection src="myposts" instance="true" id="myposts" />
<Window id="win" title="Inscription" class="container" >
<View id="posts_view" class="myposts" dataCollection="$.myposts">
<View postId="{id}" class="post_item">
<Label class="post_label" text="{title}" />
<Label class="exp" id="exp_{id}" text="" />
</View>
</View>
</View>
</Alloy>
THE MODEL
exports.definition = {
config: {
"columns": {
"title": "Text",
"id": "Integer"
},
"defaults": {
"title": "-",
"id": "-"
},
adapter: {
type: "sql",
collection_name: "myposts"
}
},
extendModel: function(Model) {},
...
Thank you all.
The advantage in my opinion a clearer definition of the View. In my opinion the biggest thing Alloy brings to the table is the ability to more cleanly separate your view from the logic that drives your application. Your logic is also simplified (in most cases!) because all you need to do is add the data to the collection, and Alloy handles the display.
The alternative to how you are doing it:
_.each(datas, function(data){
var container = Ti.UI.createView({class: "post_item"}),
title = Ti.UI.createLabel({
text: data.title,
class: "post_label"
}),
exp = Ti.UI.createLabel({class: "exp"});
container.add(title);
container.add(exp);
$.posts_view.add(container);
});
I've done it both ways, and even with alloy, sometimes it is necessary to do this, because of the limitations of Backbone as implemented in Titanium - but I think clearly if you can include your repeating UI components in the markup, it's easier to read and maintain.

Unable to assign QString to QQuickItem* with Qt.resolvedUrl

I have the following code:
Tabs {
Tab {
id: financialDetailsTab
title: i18n.tr("Financial Details")
page: Qt.resolvedUrl("FinancialDetails.qml")
}
Tab {
id: monthlyBudgetTab
title: i18n.tr("Monthly Budget")
page: Qt.resolvedUrl("MonthlyBudget.qml")
}
Tab {
id: annualBudgetTab
title: i18n.tr("Annual Budget")
page: Qt.resolvedUrl("AnnualBudget.qml")
}
Tab {
id: savingsGoalsTab
title: i18n.tr("Savings Goals")
page: Qt.resolvedUrl("SavingsGoals.qml")
}
}
which is generating the following errors:
Unable to assign QString to QQuickItem*
Unable to assign QString to QQuickItem*
Unable to assign QString to QQuickItem*
Unable to assign QString to QQuickItem*
on the lines where Qt::resolvedUrl is being used. The Tabs component is a part of the Ubuntu SDK, and not Qt Quick, and the only example of it's use doesn't provide much insight into the problem.
I've added the exact same lines as properties of the MainView, outside of the Tabs component, and the problem has not been evident there, leading me to believe the issue lies with the Ubuntu component. While the problem may be with the Ubuntu component, some help in understanding what the error message actually means would be helpful.
The error you are getting means that the 'page' property from Tab { } item can't accept a string (or url) content, it wants an Item or derived component.
So I'm pretty sure your AnnualBudget or SavingsGoals files can be instanciated as components, since they are in the same folder (so no import needed) and the file name first letter is uppercase so QML engine automatically allows you to do this :
Tabs {
Tab {
id: financialDetailsTab
title: i18n.tr("Financial Details")
page: financePage;
}
}
FinancialDetails {
id: financePage;
}
So 'financePage' is an ID, so for the QML engine it's a QQuickItem pointer and it will accept it.
Just do the same for each tab/page pair.