Joomla 3.x load form with serialized data - serialization

I'm serializing some data to save them in the database as serialized.
Reason is because i dont want to create 30 columns in the database.
I've overriden the save method and they are being saved successfuly as serialized string. Problem is how to fill the form fields upon editing the fields.
<field
name="tickets][price]"
type="text"
default=""
class="span6" />
How should i edit the loadFormData or how to solve this ?
protected function loadFormData()
{
$data = JFactory::getApplication()->getUserState(
'com_buildings.edit.building.data',
array()
);
if (empty($data))
{
$data = $this->getItem();
$data->tickets = unserialize($data->tickets);
}
return $data;
}

Are you aware that serialized data is much harder when it comes to searching? Just wanted to make sure in case you wanted to search for your data at one point (and not only store it).
Having said that, you should replace the following line:
$data->tickets = unserialize($data->tickets);
With this:
if (unserialize($data->tickets) !== FALSE)
$data->tickets = unserialize($data->tickets);

Related

bind dynamically to this vuejs

Hey guys I have the following function its working ok but I think it could be better.
methods: {
onFileChange(e, filedName) {
console.log(e.target.files);
console.log(filedName);
const file = e.target.files[0];
const fileToCheck=document.getElementById(filedName);
console.log(fileToCheck);
if(filedName=='thumbnail1'){
if(fileToCheck.value!=''){
this.thumbnail1 = fileToCheck;
this.thumbnail1Url= URL.createObjectURL(file);
} else {
this.thumbnail1=null;
this.thumbnail1Url=null;
}
}
if(filedName=='thumbnail2'){
if(fileToCheck.value!=''){
console.log(fileToCheck);
this.thumbnail2=fileToCheck;
this.thumbnail2Url = URL.createObjectURL(file);
} else {this.thumbnail2=fileToCheck; this.thumbnail2Url=null;}
}
},
Instead of checking the value for
if(fieldName == "something"){
this.something = URL.createObjectURL(file)
}
I would simply pass in a string of the fieldName and bind to it dynamically by just typing this.fieldName (filedName could equal thumbnail1 or thumbnail2 or chicken for all I care I just want to be able to pass in the name of the data atrribute and bind to it that way) but when ever I do this it doesn't work. Any help here would be great.
It's not completely clear to me what you want to accomplish, but I think you're asking about creating a dynamic data property for a view component. If that's the case, there are a couple of things to consider.
First, the example you cite, this.fieldName is not correct JavaScript syntax if fieldName is a string that contains a property name. The correct version is this[fieldName].
Note, though, that you can't simply define a new data property for a Vue component by setting it to a value. That's a limitation of JavaScript that's described in the Vue documentation. If data[fieldName] is an existing property that's defined in the component's data object, then you'll be okay. Even if you don't know the value of the property, you can initialize it, for example, with a value of null and then update the value in your method. Otherwise, you'll need to add the property to an existing non-root-level property as the documentation explains.

How to use a single form to collect data for two or more models in Yii2?

My problem is to dynamically create forms through jquery.
The user should be able to dynamically generate inputs such that he can save multiple rows into the corresponding model/table in one go.
Now I don't know how do I generate the name attribute for the forms for multiple models. I suppose it should be something like ModelName[Property][] (but I would preferably want to do it in 'Yii way' instead of hardcoding the names)
To understand this better, Here I found a similar post in Yii Wiki.
using a single form to collect data for two or more models
How can this be modified for Yii2? So that the user should be able to fill in data for all the (dynamically generated) rows and submit them in one go.
Tutorial of tabular form for multiple models.
Try this, Reference
public function actionCreate()
{
$user = new User;
$profile = new Profile;
if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post()) && Model::validateMultiple([$user, $profile])) {
$user->save(false); // skip validation as model is already validated
$profile->user_id = $user->id;
$profile->save(false);
return $this->redirect(['view', 'id' => $user->id]);
} else {
return $this->render('create', [
'user' => $user,
'profile' => $profile,
]);
}
}

Odoo 8: Set client field value from javascript

How can I set the value of a field using javascript (on the client)?
In my view's XML-file, I have:
<field name="zip" />
<field name="city" class="city" />
When the zip changes, I want to do a lookup and set the city (I could do this on the server side with an #api.onchange method, but for performance reasons, I prefer client side).
The lookup works, and I can set the value with:
$('span.city input').val(city);
This puts the city in the input field, but the client doesn't get aware of the change (for instance, I have a server side onchange method to handle other fields, and this method does not get the new city value).
From what I can find, I should call set_value(city) on the field, but how do I find the right object to call the method on?
Please take a look at hr_timesheet_sheet module in static/src/js/timesheet.js. It will give you a clear view of how you can create field and give it a value.
I found a solution. In my zip widget, I find the parent and save the fields list:
openerp.zip_widget = function(instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
var fields; // <-- Variable to keep the field list
instance.web.form.widgets.add('zip', 'instance.zip_widget.zip_lookup');
instance.zip_widget.zip_lookup = instance.web.form.FieldChar.extend({
template: "zip_widget",
start: function() {
this._super();
fields = this.getParent().fields; // <-- Get the field list
},
Now I can set a field value with fields.city.set_value(...);

Dynamically changing jQuery unobtrusive validation attributes

I have a page built in ASP.NET MVC 4 that uses the jquery.validate.unobtrusive library for client side validation. There is an input that needs to be within a range of numbers. However, this range can change dynamically based on user interactions with other parts of the form.
The defaults validate just fine, however after updating the data-rule-range attribute, the validation and message are still triggered on the original values.
Here is the input on initial page load:
<input id="amount" data-rule-range="[1,350]" data-msg-range="Please enter an amount between ${0} and ${1}">
This validates correctly with the message Please enter an amount between $1 and $350 if a number greater than 350 is entered.
After an event fires elsewhere, the data-rule-range is updated and the element looks as such:
<input id="amount" data-rule-range="[1,600]" data-msg-range="Please enter an amount between ${0} and ${1}">
At this point if 500 is entered into the input it will fail validation with the same previous message stating it must be between $1 and $350. I have also tried removing the validator and unobtrusiveValidation from the form and parsing it again with no luck.
$('form').removeData('validator');
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
Is there a clean way to change the validation behavior based on the input attributes dynamically?
As Sparky pointed out changing default attributes dynamically will not be picked up after the validation plugin has been initialized. To best work around this without rewiring how we register validated fields and rules, I found it easiest to register a custom adapter in the unobtrusive library:
jQuery.validator.unobtrusive.adapters.add("amount", {}, function (options) {
options.rules["amount"] = true;
options.messages["amount"] = function () { return $(options.element).attr('data-val-amount'); };
});
jQuery.validator.addMethod("amount", function (val, el, params) {
try {
var max = $(el).attr('data-amount-max');
var min = $(el).attr('data-amount-min');
return val <= max && val >= min;
} catch (e) {
console.log("Attribute data-amount-max or data-amount-min missing from input");
return false;
}
});
Because the message is a function, it will be evaluated every time and always pick up the latest attribute value for data-val-amount. The downside to this solution is that everytime there is a change we need to change all three attributes on the input: data-amount-min, data-amount-max, and data-val-amount.
Finally here is the input markup on initial load. The only attribute that needs to be present on load is data-val-amount.
<input id="amount" data-val-amount="Please enter an amount between ${0} and ${1}" data-val="true">
You cannot change rules dynamically by changing the data attributes. That's because the jQuery Validate plugin is initialized with the existing attributes on page load... there is no way for the plugin to auto re-initialize after dynamic changes are made to the attributes.
You must use the .rules('add') and .rules('remove') methods provided by the developer.
See: http://jqueryvalidation.org/rules/
you can try this one:
// reset the form's validator
$("form").removeData("validator");
// change the range
$("#amount").attr("data-rule-range", "[1,600]");
// reapply the form's validator
$.validator.unobtrusive.parse(document);
charle's solution works! you cannot have model attributes to use it though, I build my inputs like:
#Html.TextBoxFor(m => Model.EnterValue, new
{
#class = "form-control",
id="xxxx"
data_val = "true",
data_val_range = String.Format(Messages.ValueTooBig, Model.ParamName),
data_val_range_max = 6,
data_val_range_min = 2,
data_val_regex_pattern = "^\\d+(,\\d{1,2})?$"
})
and then in javascript:
$("form").removeData("validator");
$("#xxxx").attr('data-val-range-max', 3)
$("#xxxx").attr('data-val-range-min', 0)
$.validator.unobtrusive.parse(document);

Typeahead with database as source

This snippet works and it correctly sets data from Table Product and column name as input for bootstrap typeahead extension for YII.
but, I have ended up writing a SELECT ALL from Table Product which is having large number of data.
Can we modify this so that a WHERE condition can be added to the DataProvider on user input event. Based on each alphabet entered, a new query could then be fired and only a subset of data retrieved?
<?php
$dataProvider = new CActiveDataProvider('Product');
$dataArray = $dataProvider->getData();
$myarray = array();
foreach ($dataArray as $data){
array_push($myarray, CHtml::encode($data->name));
}
$this->widget('bootstrap.widgets.TbTypeahead', array(
'name' => 'typeahead',
'options'=>array(
'name'=>'typeahead',
'source'=>$myarray,
'items'=>4,
'matcher'=>"js:function(item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
}",
),
'htmlOptions'=>array('class'=>'search-query span3', 'placeholder'=>"Search" ),
)); ?>
Once you start to supply a function to source, then you have the power to manipulate what happens, including how often you send requests.
minLength: 3, // <- custom option
source: function(query, process) {
var longEnough = query.length >= this.options.minLength;
// you can create custom variables (this.search) that a remembered across
// searches
if (longEnough && (! this.search || whateverRuleYouWantToLimitBy)) {
// remember the query so that you can compare it to the next one
this.search = query;
$.ajax({
url: '/ajaxsearch.php?value=' + query,
type: "GET",
success: process
});
}
}
I have some code that does something similar, and I cache the results returned by the Ajax code, and then I see if the new query string has the potential to change the results (e.g., if you limit by 4 results, but I only have 3 results, then a query that simply adds to the last query (search) has no need to hit the server).
Alternatively, you can kick off a timer that effectively waits for the user to stop typing to avoid the behavior of hitting the server for every key press. Technically, that results in slower feedback, but it's better for the server and mobile users. This is appropriate on sites that have a lot of traffic.