It possible to show attributes from model to other attributes from model? - backbone.js

Hi i have a view with model attributes :
name: "P",
surname: "a",
sorters: ["name","surname"] // dynamical array ( parameter to show )
in template :
<% for(i=0 ;i<sorters.length(); i++ ){ %>
<h2><%= sorters[0] %></h2> // its <%= 'name' %> with quotes
<% } %>
as result i got
name,
surname
i need get
P,
a
As result i get values from Sorters[array] not model values:
Some examples
1.
name: "P",
surname: "a",
sorters: ["name"]
P
2.
name: "P",
surname: "a",
sorters: ["surname","name"]
a, P
With this code in template i dont have a values from models but string text from my array and my view instead of attributes from model show labels

Based on the fact that sorters returns a value which appears to be equal to another field in the model, you wish to dynamically return that field.
There's a few ways to do this, probably the best is to provide the variable option when creating the template (docs). See example below:
var model = new Backbone.Model({
name: "P",
surname: "a",
sorters: ["name","surname"]
});
var tmp = _.template($('#template').html(), {variable: 'data'});
$('#result').html(tmp(model.attributes));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script id="template" type="text/template">
<% for(i=0; i < data.sorters.length; i++ ){ %>
<h2><%= data[data.sorters[i]] %></h2>
<% } %>
</script>
<div id="result"/>
Also, better to use each rather than a for loop:
<% _.each(data.sorters, function(sorter) { %>
<h2><%= data[sorter] %></h2>
<% }) %>

Related

Elixir Phoenix update nested many-to-many association

I'm using Elixir & Phoenix. I have the schemas Venues.Team and Accounts.Job with a many-to-many relationship between them.
defmodule Runbook.Venues.Team do
use Ecto.Schema
import Ecto.Changeset
schema "teams" do
field :name, :string
belongs_to :venue, Runbook.Venues.Venue
many_to_many :employees, Runbook.Accounts.Job, join_through: "jobs_teams"
timestamps()
end
#doc false
def changeset(team, attrs) do
team
|> cast(attrs, [:name])
|> validate_required([:name])
end
end
and
defmodule Runbook.Accounts.Job do
use Ecto.Schema
import Ecto.Changeset
schema "jobs" do
field :role, :string
belongs_to :user, Runbook.Accounts.User
belongs_to :venue, Runbook.Venues.Venue
many_to_many :teams, Runbook.Venues.Team, join_through: "jobs_teams"
timestamps()
end
#doc false
def changeset(job, attrs) do
job
|> cast(attrs, [:role])
|> validate_required([:role])
end
end
I have the default Venues.update_team/2 method:
def update_team(%Team{} = team, attrs) do
team
|> Team.changeset(attrs)
|> Repo.update()
end
I want to be able to include a jobs argument inside the attrs parameter when updating a Team. This should insert an association into the :employees field.
I can do this in the interactive elixir shell (from ElixirSchool docs)
team = Venues.get_team!(1)
team_changeset = Ecto.Changeset.change(team)
team_add_employees_changeset = team_changeset |> put_assoc(:employees, [job])
Repo.update!(team_add_employees_changeset)
But I'm not sure how to abstract this to the update_team method, which builds the changeset up without a database call.
When I try to do it:
%Team{}
|> Team.changeset(%{id: 1, name: "Floor"})
|> put_assoc(:employees, [job])
|> Repo.update()
I get an error complaining that the :employees association is not loaded:
** (Ecto.NoPrimaryKeyValueError) struct `%Runbook.Venues.Team{__meta__: #Ecto.Schema.Metadata<:built, "teams">, employees: #Ecto.Association.NotLoaded<association :employees is not
loaded>, id: nil, inserted_at: nil, name: nil, updated_at: nil, venue: #Ecto.Association.NotLoaded<association :venue is not loaded>, venue_id: nil}` is missing primary key value
(ecto) lib/ecto/repo/schema.ex:898: anonymous fn/3 in Ecto.Repo.Schema.add_pk_filter!/2
(elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/repo/schema.ex:312: Ecto.Repo.Schema.do_update/4
I can sorta do it like this:
changeset = Team.changeset(%Team{}, %{id: 1, name: "Floor"})
team = Venues.get_team!(1) |> Repo.preload(:employees)
preloaded_changeset = %Ecto.Changeset{changeset | data: team}
preloaded_changeset |> put_assoc(:employees, [job]) |> Repo.update()
(untested, a cleaner version of the below)
%Ecto.Changeset{tc | data: Venues.get_team!(1) |> Repo.preload(:employees)} |> put_assoc(:employees, [job]) |> Repo.update()
What is the best/cleanest/most conventional way to do this?
You can update by using Ecto.Changeset.cast_assoc/3
def update_team(%Team{} = team, attrs) do
team
|> Repo.preload(:employees) # has to be preloaded to perform update
|> Team.changeset(attrs)
|> Ecto.Changeset.cast_assoc(:employees, with: &Job.changeset/2)
|> Repo.update
end
update form
<%= form_for #changeset, #action, fn f -> %>
<%= if #changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>
<%= label f, :name %>
<%= text_input f, :name %>
<%= error_tag f, :name %>
<div class="form-group">
<%= inputs_for f, :job, fn cf -> %>
<%= label cf, :job_param_1 %>
<%= text_input cf, :job_param_1 %>
<%= error_tag cf, :job_param_1 %>
<% end %>
</div>
<div class="form-group">
<%= inputs_for f, :job, fn cf -> %>
<%= label cf, :job_param_2 %>
<%= text_input cf, :job_param_2 %>
<%= error_tag cf, :job_param_2 %>
<% end %>
</div>
<div>
<%= submit "Save" %>
</div>
<% end %>

How to bind variables to INPUT dynamically

I have an object in the controller which value I am getting from http GET request:
$scope.myObj = {
id1: "1",
id2: "5",
id3: "",
id4: ""
};
It can have any number of fields (id...) with any values. But if id-k is not empty all id-n where n < k is not empty too.
I need to bind to INPUT last not empty field of the object. What is best way to do it?
Here is a plnkr
Update: This object is a position in the classificator.
In my example ID of position is 1.5. I need to allow edit only last position in the classification. The user can change 5 to 6, 7 or anything else, but it can not change 1 segment
If the object will be
$scope.myObj = {
id1: "2",
id2: "5",
id3: "4",
id4: "8"
};
The classification is 2.5.4.8 and user must be able edit only last segment - 8.
Controller
...
$scope.myObj = {
id1: "1",
id2: "5",
id3: "6",
id4: ""
};
$scope.segments = Object.keys($scope.myObj)
.filter(function(key) {
return $scope.myObj[key];
})
.sort(function(a, b){
return Number(a.replace('id', '')) - Number(b.replace('id', ''));
});
...
HTML
<span ng-repeat="segment in segments">
<span ng-if="!$last">{{ myObj[segment] }}</span>
<input ng-if="$last" ng-model="myObj[segment]">
</span>
<p>{{ myObj }}</p>
Plnkr - https://plnkr.co/edit/8C9lLLdCvvzFinaOXqtd?p=preview
Original Answer
You can calculate the last non blank id in the controller and set it to a scope variable. Something like
$scope.myLast = $scope.myObj['id' + Object.keys($scope.myObj).reduce(function(a, key){
if ($scope.myObj[key]) {
var idNum = Number(key.replace('id', ''));
a = Math.max(a, idNum)
}
return a;
}, -Infinity)];
Note - you can't rely on the order of keys, so you can't actually assume that the last key is the biggest one.
Plnkr - https://plnkr.co/edit/apy8qZNJNK4Q23J5Ja4N?p=preview
I need to bind to INPUT..
So it seems ,what you have is , a list of objects where the no. of elements in the list can vary.. and you want to bind all of them to <input>
If so then this would do the job:
<div ng-init="tempName = key" ng-repeat="(key, value) in myObj">
<label for="{{key}}" >{{key.toUpperCase()}}</label>
<input name="{{key}}" id="{{key}}" type="text" ng-model="myObj[tempName]" />
</div>
here's an awesome plunkr doing just that
EDIT:
To ignore the empty fields : use this

Link data: Angular

My angular app needs to link together two different data objects, or something like that. I'm having trouble putting this into words.
Data ::::
$scope.users = [{userid: "5", name: "Bobby"},{userid: "3", name: "Fett"}];
$scope.comments = [{id: "1", content: "a comment", userid: "3"},{id: "2", content: "another comment", userid: "3"}];
Directive ::::
<article ng-repeat="comment in comments">
Posted by: {{user.name}} Comment: {{comment.content}}
</article>
Obviously, {{user.name}} isn't going to work. But the idea is to get the user name from the matching comment userid.
Not really sure where to start with this
One possible solution could be a user index to get the corresponding user:
var userIndex = {};
for (var i = 0, len = users.length; i < len; i++) {
userIndex[users[i].userid] = users[i];
}
$scope.userIndex = userIndex;
$scope.comments = comments;
Using this index you can get the username:
<article ng-repeat="comment in comments">
Posted by: {{ userIndex[comment.userid].name}} Comment: {{comment.content}}
</article>
See this plunker.

How to create a generic table template for BackboneJS use

So my template thus far:
<script id="table-row" type="x-handlebars-template">
<tr>
{{#each this}}
<td>{{.}}</td>
{{/each}}
</tr>
</script>
And my render code:
that.collection.each(function(model){
var template = Handlebars.compile( $('#table-row').html() );
that.table.append(template(model.toJSON()));
});
Any my dummy data collection:
this.collection = new Backbone.Collection([
{name: "Tim", age: 5},
{name: "Ben", age: 26},
{name: "Rob", age: 55}
]);
Now, how can I create a template that can output as many <td>s as there are object keys in my Model? ie. I have 'name' and 'age' now, but what I add 'height', I have to specifically update this template, how can I create a generic table row template?
I think you'll need a custom Handlebars helper to iterate over object keys. I haven't used it but something like this should do the trick.
Here's the code from that link:
// HELPER: #key_value
//
// Usage: {{#key_value obj}} Key: {{key}} // Value: {{value}} {{/key_value}}
//
// Iterate over an object, setting 'key' and 'value' for each property in
// the object.
Handlebars.registerHelper("key_value", function(obj, fn) {
var buffer = "",
key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
buffer += fn({key: key, value: obj[key]});
}
}
return buffer;
});

Backbone JS Nested Models with Backbone Relational

I am trying to implement Backbone with backbone-relational.
The Claim model:
define(['underscore', 'backbone', 'backbone-relational', 'models/User'], function(_, Backbone, relational, User) {
var Claim = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasOne,
key: 'a_user',
relatedModel: User
}],
defaults: {
},
initialize: function() {
},
clear: function() {
this.destroy();
this.view.remove();
}
});
return Claim;
});
The User model is a duplicate but with no relations set.
Below is the collection object:
Object
_byCid: Object
_byId: Object
_callbacks: Object
currentPage: 1
firstPage: 1
information: Object
length: 3
models: Array[3]
0: Object
1: Object
_callbacks: Object
_deferProcessing: false
_escapedAttributes: Object
_isInitialized: true
_pending: Object
_permitsUsed: 0
_previousAttributes: Object
_queue: Object
_relations: Array[1]
_silent: Object
attributes: Object
_deleted: false
_new: false
a_user: Object
_callbacks: Object
_escapedAttributes: Object
_isInitialized: true
_pending: Object
_permitsUsed: 0
_previousAttributes: Object
_queue: Object
_relations: Array[0]
_silent: Object
attributes: Object
_deleted: false
_new: false
already_in_save: false
already_in_validation: false
coll_claims: Array[0]
coll_claims_partial: true
created_at: "2012-12-12 09:00:00"
email: "cloud.strife#test.com"
firstname: "Cloud"
id: 2
lastname: "Strife"
modified_at: "2012-12-12 09:00:00"
modified_columns: Array[0]
start_copy: false
title: "Mr"
validation_failures: Array[0]
virtual_columns: Array[0]
__proto__: Object
changed: Object
cid: "c4"
collection: undefined
id: 2
__proto__: Object
already_in_save: false
already_in_validation: false
created_at: "2012-12-12 09:00:00"
fulfilment: "bank"
id: 2
manual: 0
modified_at: "2012-12-12 09:00:00"
modified_columns: Array[0]
promotion_id: 1
purchase_id: 2
start_copy: false
status: "pending"
user_id: 2
validation_failures: Array[0]
virtual_columns: Array[0]
__proto__: Object
changed: Object
cid: "c3"
collection: Object
id: 2
__proto__: Object
2: Object
length: 3
So essentially there are 3 Claim models in the collection and each Claim model has a nested model of User at the attribute key a_user.
The template looks like:
<% _.each( claims, function( item ){ %>
<tr>
<td><%= item.get("id") %></td>
<td><%= item.get("promotion_id") %></td>
<td><%= item.get("a_user").get("firstname") %></td>
<td><%= item.get("purchase_id") %></td>
<td></td>
<td><%= item.get("status") %></td>
<td><%= item.get("created_at") %></td>
</tr>
<% }); %>
However this results in the error:
TypeError: 'null' is not an object (evaluating 'item.get("a_user").get')
If I take it back to just item.get("a_user") it displays outputs [object Object]
I am just learning Backbone so any pointers are appreciated.
You shouldn't be using get within the template -- Underscore templates work with JSON, not Backbone model objects (you pass the model to the template using template(model.toJSON())), so they should be evaluated using basic dot-notation:
<tr>
<td><%= item.id %></td>
<td><%= item.promotion_id %></td>
</tr>

Resources