dB CRUD app: create TextInputs with dB fields names


#1

I’m trying to build a simple CRUD (create, read, update, delete) database app. For definition, a CRUD application does not know the name of the dB fields that it will go to read from the dB. It will get them from the dB every time it is ran. In PHP it’s simple:

<?php
    
    error_reporting(E_ERROR | E_WARNING | E_PARSE);
    
    $db = new mysqli('localhost', 'root', 'root', 'my_db');
    
    $query = 'SHOW COLUMNS FROM my_table';
    $result = $db->query($query);

    while($row = $result->fetch_assoc()){
        $fields[] = $row['Field'];
    }

    echo json_encode($fields);
    
?>

The above code will list all the dB fields in the table my_table like this:

["first_name", "second_name", "address"]

The CRUD application will then use a FOR loop to create the form with the listed INPUT fields. In HTML will be:

<form id="generate-form" type="POST">
    <?php foreach($fields as $field): ?>
        <label>
            <?php echo "$field: "; ?>
            <input type="text" name="<?php echo $field; ?>" />
        </label><br/>
    <?php endforeach; ?>
    <input type="submit" name="submit" />
</form>

The matter is translate this logic into Fuse. I can use the PHP code above to extract the dB fields and pass their name JSON encoded to Fuse:

fetch("http://localhost/api/dbfields.php").then(function(response) {
    if (response.ok) {
        return response.json(); 
    } else {    
        throw Error(response.status);
    }
})
.then(function(json) {
    datas.replaceAll(json);

    // Debug purposes
    datas.forEach(function(i) {
        console.log(i);
    });

}).catch(function(error) {
    if (error == "TypeError: Network request failed") {
        isConnection.value = true;
    }
});

But the problem that I’m getting mad on is that I need to transform each dB field name into a Observable. The following Fuse code outputs only the first field name (“first_name”) while TextInput it is not ‘recognized’ as a Input field, nor it is PlaceholderText:

<JavaScript>
    var Observable = require('FuseJS/Observable');

    var datas = Observable();

    module.exports = {
        data:           datas,
    };
</JavaScript>


<Each Items="{data}">                
    <TextInput Value="{data}" PlaceholderText="{data}" />
</Each>

How can I dynamically render a number of <TextInput> based on the given output

["first_name", "second_name", "address"]

and set their names as Observables so that I can use them properly in Fuse?


#2

Try this:

<Each Items="{data}">                
    <TextInput Value="{}" PlaceholderText="{}" />
</Each>

#3

You owe me a nice meal from your country :smile: :

<App>
	<JavaScript>
		var Observable = require('FuseJS/Observable');
		
		function uppercaseFirst(string) 
		{
			return string.charAt(0).toUpperCase() + string.slice(1);
		}
		
		//Assuming you get this payload from your API server
		var fieldData = [
			{ "fieldName": "first_name"}, 
			{ "fieldName": "second_name"}, 
			{ "fieldName": "address"}
		];
		
		var fields = Observable();
		
		for (var i = 0; i < fieldData.length; i++) {
			
			//Add modified field to object
			fields.add(Object.assign({
				"label": uppercaseFirst(fieldData[i].fieldName.replace('_',' '))
			}, fieldData[i]));
		}
		
		module.exports = {
			fields: fields,
		};
	</JavaScript>

	<StackPanel Alignment="Center">
		<Each Items="{fields}">
			<Text Value="DB Field Name: {fieldName}" />
			<Panel Padding="10" Margin="10,10,10,30" Color="#eaeaea">
				<TextInput Value="" TextColor="Black" PlaceholderText="{label}" PlaceholderColor="#666" />
			</Panel>
		</Each>
	</StackPanel>
</App>

#4

Thank you Aeq. I would never work it out by my self. This level of JavaScript is too complex for me. I’m just starting to understand it. “Bucatini all’Amatriciana” are on shipping!


#5

Sorry Aeq, but it’s two days that I’m trying to READ the <TextInput> values. I added to fieldData the key content which mimics a query to a dB:

var fieldData = [
    { "fieldName": "first_name", "content": "John"}, 
    { "fieldName": "second_name": "content": "Smith"}, 
];

I added content to the <TextInput> to display it:

<TextInput Value="{content}" TextColor="Black" PlaceholderText="{label}" PlaceholderColor="#666" />

I then added to the UX a Save button:

<Button Text="Save" Clicked="{saveItem}" />

and the corresponding JavaScript function:

function saveItem() {
    fields.forEach(function(i) {
         console.log(JSON.stringify(i.content));
    });
}

But by reading content like this I simpy get the same informations written in fieldData. I do not see any changes. I suppose because content is not an Observable. But fields is a Observable so isn’t content a Observable too as it is part of fields? So, if content must be declared ad Observable how do I do it? And how I can then read changes?

This for example returns the same content:

fields.forEach(function(i) {
    var test = Observable(i.content);
    console.log(test.value);
});

Many thanks.


#6

This is the simplest solution I came up with and it has an added feature, thats 2 good meals and good drink you owe me now :smile: :

<App>
	<JavaScript>
		var Observable = require('FuseJS/Observable');
		
		function uppercaseFirst(string) 
		{
			return string.charAt(0).toUpperCase() + string.slice(1);
		}
		
		//Assuming you get this payload from your API server
		var fieldData = [
			{ "content":"Andrew", "fieldName": "first_name"}, 
			{ "content":"Enrico", "fieldName": "second_name"}, 
			{ "content":"South Africa", "fieldName": "address"}
		];
		
		var fields = Observable();
		
		for (var i = 0; i < fieldData.length; i++) {
			
			//Add modified field to object
			fields.add(Object.assign({
				"label": uppercaseFirst(fieldData[i].fieldName.replace('_',' ')),
				"contentOb": Observable(fieldData[i].content)
			}, fieldData[i]));
		}

		function save() {
			
			var fieldsToSave = {};

			fields.forEach(function(field, index) {
				
				console.log(index, field.fieldName, field.content);
				console.log(index, field.fieldName, field.contentOb.value);

				if (field.content != field.contentOb.value) {
					console.log(field.fieldName + " was changed");
					fieldsToSave[field.fieldName] = field.contentOb.value;
				}

			});
			
			if (Object.keys(fieldsToSave).length > 0) {
				console.log('Saving...')
				console.dir(fieldsToSave);
			} else {
				console.log('No changes to save.')
			}
			
		}
		
		module.exports = {
			fields: fields,
			save: save,
		};
	</JavaScript>

	<StackPanel Alignment="Center">
		<Each Items="{fields}">
			<Text Value="DB Field Name: {fieldName}" />
			<Panel Padding="10" Margin="10,10,10,30" Color="#eaeaea">
				<TextInput Value="{contentOb}" TextColor="Black" PlaceholderText="{label}" PlaceholderColor="#666" />
			</Panel>
		</Each>
		<Button Text="Save" Clicked="{save}" />
	</StackPanel>
</App>

#7

Thank you very much for the prompt help :pray:t2:. This is still too advanced for me :sweat:.

We could keep in mind this code for the TextInput module if we decide to use JavaScript.

P.S. Mozzarella di bufala it’s on the way…! :grin: