I have model user
window.User = Backbone.Model.extend({
defaults: {
"id": null,
"login": "",
"password": "",
"role": ""
}
});
window.UserCollection = Backbone.Collection.extend({
model: User
});
json response:
{
"id":1,
"login":"log1",
"password":"psw1",
"role":
{
"id":1,
"name":"user"
}
},
I have json response with all roles:
[{"id":1,"name":"user"},{"id":2,"name":"admin"}]
How create dropdown form with all roles and after set it to model user when i create a new user
To create a drop down at render time and select the correct option, you could define a template and then using when the view is rendered.
Template:
<script type="text/html" id="rolesSelectTemplate">
<select id="selectRole" name="userRole">
<% _(allRoles).each(function(aRole) { %>
<% if (aRole.get('IsSelected') == "selected") { %>
<option value="<%= aRole.get('id') %>" id="<%- aRole.get('id') %>" selected="selected"><%- aRole.get('name') %></option>
<% } %>
<% else { %>
<option value="<%- aRole.get('id') %>" id="<%- aRole.get('id') %>"><%- aRole.get('name') %></option>
<% } %>
<% }); %>
</select>
</script>
Function:
render: function(){
// do something first
// Assuming you are able to init the users and roles collection with JSON
var selectedRole = this.model.get('role');
allRoles.each(function (aRole) {
aRole.set('IsSelected', "");
if (selectedRole && selectedRole.id == aRole.get('id') /* if the nested role attribute is a simple object or else use selectedRole.get('id')*/) {
aRole.set('IsSelected', "selected");
}
});
var allRolesDropDown = _.template($("#rolesSelectTemplate").html())({
allRoles: allRoles.models,
});
$("#allRolesDropDownElement").append(allRolesDropDown);
// do something else
return this;
}
Related
I'm learning Turbo Frames and Streams + Stimulus so it's possible I'm not 100% on track. I have a form for creating a new object, but within the form I'd like to have a select component that will display certain fields depending on the selection. It's important to note that due to this, I do not want to submit the form until the user has made this selection.
This is what I have:
_form.html.erb
<div class="form-group mb-3">
<%= form.label :parking, class: 'form-label' %>
<%= form.number_field :parking, class: 'form-control' %>
</div>
<%= turbo_frame_tag "turbo_transactions" do %>
<%= render 'property_transactions' %>
<% end %>
_property_transactions.html.erb
<div class="form-group mb-3" data-controller="property-transaction">
<%= label_tag :property_transactions, 'Property in:', class: 'form-label' %>
<%= select_tag :property_transactions, options_for_select(#property_transactions.collect {|p| [p.name, p.id]}), { data:
{ action: "property-transaction#redraw", property_transaction_target: 'transaction', turbo_frame: 'turbo_transactions' },
class: 'form-control', prompt: '', autocomplete: 'off' } %>
</div>
<% if #property_transaction %>
<%= turbo_frame_tag #property_transaction.en_translation_key do %>
<div class="form-group mb-3">
<%= render #property_transaction.en_translation_key %>
</div>
<% end %>
<% end %>
property_transaction_controller.js
import { Controller } from "#hotwired/stimulus";
import Rails from "#rails/ujs";
export default class extends Controller {
static targets = [ "transaction" ];
redraw() {
const params = { property_transaction: this.transaction };
Rails.ajax({
type: 'post',
dataType: 'json',
url: "/set_property_transaction",
data: new URLSearchParams(params).toString(),
success: (response) => { console.log('response', response) }
});
}
get transaction() {
return this.transactionTarget.value;
}
}
property_controller.rb
def set_property_transaction
respond_to do |format|
format.json
format.turbo_stream do
if #property_transactions
#property_transaction = #property_transactions.select { |p| p.id == property_transaction_params }
else
#property_transaction = PropertyTransaction.find(property_transaction_params)
end
end
end
end
set_property_transaction.turbo_stream.erb
<%= turbo_stream.replace #property_transaction.en_translation_key %>
_rent.html.erb
<%= turbo_frame_tag "rent" do %>
<!-- some input fields -->
<% end %>
_rent_with_option_to_buy.html.erb
<%= turbo_frame_tag "rent-with-option-to-buy" do %>
<!-- other input fields -->
<% end %>
_sale.html.erb
<%= turbo_frame_tag "sale" do %>
<!-- more input fields -->
<% end %>
When selecting the option, this error happens:
Started POST "/set_property_transaction" for ::1 at 2022-09-07 19:49:03 -0600
Processing by PropertiesController#set_property_transaction as JSON
Parameters: {"property_transaction"=>"2"}
Completed 406 Not Acceptable in 223ms (ActiveRecord: 0.0ms | Allocations: 1879)
ActionController::UnknownFormat (PropertiesController#set_property_transaction is missing a template for this request format and variant.
request.formats: ["application/json", "text/javascript", "*/*"]
request.variant: []):
My understanding to this is that I'm missing the set_property_translation template, but I do have it. Not sure what else could I do to make it recognizable.
Les Nightingill's comment definitely sent me in the right direction. I'll put the changes needed here.
_property_transactions.html.erb
<div class="form-group mb-3" data-controller="property-transaction">
<%= label_tag :property_transactions, 'Propiedad en:', class: 'form-label' %>
<%= select_tag :property_transactions, options_for_select(#property_transactions.collect {|p| [p.name, p.id]}), { data:
{ action: "property-transaction#redraw", property_transaction_target: 'transaction', turbo_frame: "turbo_transactions" },
class: 'form-control', prompt: '', autocomplete: 'off' } %>
</div>
<%= turbo_frame_tag "dynamic_fields" %>
property_transaction_controller.js
import { Controller } from "#hotwired/stimulus";
import { post } from "#rails/request.js";
export default class extends Controller {
static targets = [ "transaction" ];
async redraw() {
const params = { property_transaction: this.transaction };
const response = await post("/set_property_transaction", {
body: params,
contentType: 'application/json',
responseKind: 'turbo-stream'
});
if (response.ok) {
console.log('all good', response); // not necessary
}
}
get transaction() {
return this.transactionTarget.value;
}
}
set_property_transaction.turbo_stream.erb
<%= turbo_stream.update "dynamic_fields" do %>
<%= render partial: #property_transaction.en_translation_key %>
<% end %>
the expense.id prints as undefined in the log
My Underscore template
<% _.each(expenses, function(expense) { %>
<tr>
<td><%= expense.get('name') %></td>
<td><%= expense.get('amount') %></td>
<td><%= expense.get('category') %></td>
<td><% console.log(expense.id) %></td>
</tr>
<% }); %>
My Backbone View
var ExpenseList = Backbone.View.extend({
el: '.page',
render : function(){
var expenses = new Expenses();
var that = this;
expenses.fetch({
success : function(expenses){
var template = _.template($('#expense-list-template').html(),
{expenses: expenses.models});
that.$el.html(template);
}
});
}
});
Server Response
[
{
"_id": "53c25827555953000091ab71",
"name": "Shell",
"amount": "$1000",
"category": "Internet"
},
{
"_id": "53c2bfee4bf93700006fb714",
"name": "FC",
"amount": "$432",
"category": "Game"
}
]
Your server is sending back _id attributes, not id attributes.
The easiest option would be to set idAttribute on your model:
var Expense = Backbone.Model.extend({
idAttribute: '_id',
//...
});
var Expenses = Backbone.Collection.extend({
model: Expense,
//...
});
That will give you expense.get('_id') and expense.id as the same thing and the models will send _id back to the server.
You could also add a parse method to your model to rename the attribute but that would cause problems when you tried to send data back to the server.
I have a simple model as follows:
module.exports = {
attributes: {
firstName: 'STRING',
lastName: 'STRING',
contact: 'STRING',
email: 'STRING'
}
};
I already have an index action that displays all the humans. This is the corresponding view:
<h1>List of all humans</h1>
<ul>
<% _.each(humans, function(model) { %>
<li><%= model.firstName %> /// <%= model.lastName %> /// <%= model.contact %> /// <%= model.email %> <button id="<%=model.firstName %>"type="button">Edit</button> </li>
<% }) %>
</ul>
What I want to accomplish is that every time someone clicks on the EDIT button, to display a view containing all the information of that specific model (localhost:1337/human/edit/:id). How can I write my controller? How can I tell my controller that I want THAT specific model to be displayed and route it properly?
Thank you!
You should point browser to localhost:1337/human/edit/:id url, where :id is your particular model's id. For example
<ul>
<% _.each(humans, function(model) { %>
<li><%= model.firstName %> <button id="<%=model.firstName %>" type="button">Edit</button>
</li>
<% }) %>
</ul>
This will automatically execute Human.edit controller with id param set to particular model's id. You don't have to write any custom routes, this is default behaviour.
Example of Human/edit controller action:
edit: function(req, res) {
Human.findById( req.param('id') )
.done(function(err, human) {
if (err) {
return res.send(err, 500);
} else {
if (human.length) {
res.json(human[0]);
} else {
res.send('Human not found', 500);
}
}
});
}
Here I return model encoded as json in response, for simplicity, but you can use your view.
In addition, firstName property is not the best value to use as buttons id attribute, because it should be unique.
template code :
<script type="text/template" id="baseTemplate">
<% collection.each(function() { %>
<div>
<%= collection %>
</div>
<% }); %>
</script>
<div id="baseContainer"></div>
and other code is :
//model
var PageModel = Backbone.Model.extend({
defaults: {
"id":null,
"title":"",
"content":""
},
});
//collection
var PageCollection = Backbone.Collection.extend({
defaults: {
model: PageModel
},
model: PageModel,
url: 'api/get_page/?id=6'
});
//view
var PageView = Backbone.View.extend({
el: $('#baseContainer'),
initialize: function () {
this.collection = new PageCollection();
this.collection.bind("reset", this.render, this);
this.collection.bind("change", this.render, this);
this.collection.fetch();
},
render: function () {
var html = _.template($("#baseTemplate").html(), { collection: this.collection });
this.$el.html(html);
console.log(html);
return this;
}
});
var page = new PageView();
problem is that its return and object how can i get values from object? api link is http://furqankhanzada.com/backbonejs/api/get_page/?id=6 and here you can see object in browser console http://furqankhanzada.com/backbonejs/
i need to get title, content , attachments -> images -> Gallery Large -> url (attachments using each() ).
Not sure whether this is proper solution or not, but you can give it a try.
An alternative can be like,
var html = _.template($("#baseTemplate").html(), { models: this.collection.models });
Pass models instead of directly passing collection. And in the template you can do something like this,
<script type="text/template" id="baseTemplate">
<% _.each(models, function(mdl) { %>
<div>
<%= mdl.get('title') %>
<%= mdl.get('content') %>
<% _.each(mdl.get('page').attachments, function(attachment) { %>
<%= attachment.images["Gallery Large"].url %>
<% }) %>
</div>
<% }); %>
</script>
<div id="baseContainer"></div>
Please modify the markup as per your needs. But this solution is too specific to the problem :( :(
I’m working with backbone, this is the static data I have:
var emailfields = new EmailFields([
new EmailField({ id: "data-email", name: "Email" }),
new EmailField({ id: "data-first-name", name: "First Name" }),
new EmailField({ id: "data-last-name", name: "Last Name" }),
]);
I want to create n (n > 1) drop-down lists populated with the same data (emailfields). If any of the values is selected I want to notify the user that he cannot select the same field again.
This is my view:
EmailFieldSelectView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, "addSingleEmailField", "add");
this.add();
},
addSingleEmailField: function(emailfield) {
$("select").each(function() {
$(this).append(new EmailFieldView({ model: emailfield}).render().el);
});
},
add: function() {
this.collection.each(this.addSingleEmailField);
},
});
This is my initialization:
window.emailview = new EmailFieldSelectView({
collection: emailfields
});
In order to populate each select with the same data I’m using $("select").
Is there a better way to do that (I feel this looks like a hack)?
Thanks.
What you've got seems like a reasonable way to approach things. You could use a template to reduce the javascript and need for a view to create select list options.
<script type="text/template" id="exampleTemplate">
<select id="list1">
<% _(emailFields).each(function(emailField) { %>
<option value="<%= emailField.id %>"><%= emailField.name %></option>
<% }); %>
</select>
<select id="list2">
<% _(emailFields).each(function(emailField) { %>
<option value="<%= emailField.id %>"><%= emailField.name %></option>
<% }); %>
</select>
etc.
</script>
Then the view becomes -
EmailFieldSelectView = Backbone.View.extend({
template: _template($('#exampleTemplate').html());
initialize: function() {
_.bindAll(this, "addSingleEmailField", "add");
this.render();
},
render: function() {
this.$el.html(this.template({ emailFields: this.collection.toJSON() }));
return this;
}
});