I am fairly new to js and angular but I was trying to get a Rails app working after watching Ryan Bates's railscast on Rails+AngularJS (http://railscasts.com/episodes/405-angularjs?autoplay=true).
I believe what I want to do is fairly simple: I have a Place model that has many Phones and I want to dynamically be able to add and save phones when creating a new Place. This type of nested form is fairly easy in Rails alone (with js only creating a new field element when a user adds a Phone), but I want to do this in Angular.
I suppose my question then breaks down into two:
1) What is the best practice of creating nested forms in Rails+AngularJS (or even forms in general)? Right now I am passing every model to Angular via JSON but is this the best way?
My second question refers to the following code.
I've attempted to do this and I am able to save a Place in angular:
places_controller.rb
respond_to :json
def create
respond_with Place.create(params[:place])
end
_form.html.erb
<div class="container-fluid" ng-controller="PlaceCtrl">
<form ng-submit="addPlace()">
<div class="row-fluid">
<div class="span2"><label>Name</label></div>
<div class="span8"><input type="text" class="input-xlarge" ng-model="newPlace.name"></div>
</div>
<input type="submit" class="btn" value="Add">
</form>
</div>
place.js.coffee
app = angular.module("test", ["ngResource"])
app.factory "Place", ($resource) ->
$resource("/places/:id", {id: "#id"}, {update: {method: "PUT"}})
app.factory "Phone", ($resource) ->
$resource("/phones/:id", {id: "#id"}, {update: {method: "PUT"}})
#PlaceCtrl = ($scope, Place, Phone) ->
$scope.addPlace = ->
place = Place.save($scope.newPlace)
console.log place
$scope.newPlace = {}
In place.js.coffee I am able to save Place to the DB in the line (place = Place.save($scope.newPlace)). I want the id of place so I can append it to all Phones that are dynamically built (because Phones has a FK of place_id that points to a Place). However if I type in place.id in console, I get an undefined message. place.name will return the place's name, but id won't work. If I look at console.log place, however, I see that the id field is returned and populated.
2) How do I get the id field of the place JSON object? I am almost positive this is possible since Chrome's console returns the id field but place.id won't give me anything.
Thank you in advance for your help. I greatly appreciate it.
I would try something like this:
$scope.addPlace = ->
place = Place.save($scope.newPlace) ->
$scope.newPlaceId = place.id
console.log place
$scope.newPlace = {}
Related
I'm working on a model driven form and I can't get it to add items to a list being displayed with ngFor. I'm currently getting an error when trying to iterate the list.I tried all solutions available.
Error
Cannot find control with path: 'categories -> 0'
ts file
private categories : any= [{name: 'Strict',selected : false},
{name: 'Moderate',selected : true},
{name: 'Flexible',selected : true}];
let allCategories: FormArray = new FormArray([]);
for (let i = 0; i < this.categories.length; i++) {
let fg = new FormGroup({});
fg.addControl(this.categories[i].name, new FormControl(this.categories[i].selected))
allCategories.push(fg)
}
form initialization
categories: allCategories,
Html part
<div formArrayName="categories">
<div *ngFor="let category of categories; let i=index">
<span formGroupName="{{i}}">
<label>
<input type="checkbox" formControlName="{{category.name}}"/>
{{category.name}}
</label>
</span>
</div>
</div>
#Jadoon, I suppose, #DineshArun meant that he wants to list all categories using reactive approach and uses for that FormArray with FormGroups for each category.
The problem is that Angular usually assigns array index as group name by default 0, 1...
But in #DineshArun's case that doesn't work. And in mine case it didn't too, but I've found the solution.
First of all, look at this question and it's marked answer. Rewrite the filling in of your formArray that way, and then assign to formControlName the raw name of the control, which you pointed in your patch() method.
Assume a situation where I've a Rails AR models as below
class User
has_one :profile
end
class Profile
belongs_to user
has_one :address
end
class Address
belongs_to :profile
end
And have a User Profile view to be constructed at the client-side. Now, how should my Backbone model look like? Should it replicate the way it is in Rails/server-side domain model? Do we have a clear reason for the way it has to be that way, or is it just subjective?
Your experience sharing is appreciated!
Usually your backbone models and collections should follow your REST API ( or any other client-side <-> server-side communication ).
For example if these ruby models are passed to the frontend with :
GET /api/user/:id
And what you got as a response is
[{ profile: { address: "21st str." } },{ profile: { address: "17th str." } }]
You would need one model
User = Backbone.Model
Users = Backbone.Collection.extend({
model: User,
url: "/api/user"
});
But if you do something more complicated in your API and have more urls in your API you could choose the structure that best fits your interaction with the client on your frontend. For example if your website doesn't need a user api at all and you pass data to the frontend with those urls:
GET /api/profile
You can have only one model
ProfileModel = Backbone.Model.extend({
url: "/api/profile"
})
And you can easily set the address like
profile = new ProfileModel();
profile.set('address', '21st str.');
Bottom line
Backbone usually should follow your URL structure of your REST API. If you do this you will enjoy the full benefits of using it's REST Ajax calls ( GET, POST, DELETE, PUT ) properly autoconfigured.
Usually what I don't do is to make my Backbone app structure to follow the database schema. That may cause a headache, because you will need to create a way your backend ( Ruby app ) to be able to provide almost the same database access as the ORM that you are using.
To keep things simple, I believe the model should represent both the server-side model and the client-side view state, distinguishing the view state attributes by a preceding _. The view state attributes are ignored by the server when saving the model.
Here's an simplified example of the workflow I use:
var animal = new View({
initialize: function(){
// define the model and default view state when view is initialized
this.model = new Model({id:3, _edit:false}, {url:'/animals'));
}
, render: function(){
var self = this;
this.undelegateEvents();
this.delegateEvents({
'click [data-trgger]': function(e){
self[$(e.currentTarget).attr('data-trigger')].apply(this);
}
});
var success = function(){
// pass the model to the template engine
self.$el.html( _.template('#edit-animals', {model: self.model}) );
}
// fetch the model from the server when view is rendered
// you could check if the model is already fetched
this.model.fetch({success:success});
}
, onSave: function(){
// save the model then:
this.model.set('_edit', false);
this.render();
}
, onEdit: function(){
this.model.set('_edit', true);
this.render();
}
});
And the template:
<% if(model.get('_edit')){ %>
<!-- the view is in 'edit' state, show input and save button -->
<div>
<input type="text" name="breed" class="form-control">
</div>
<button class="btn btn-primary" data-trigger="onSave">Save</button>
<% }else{ %>
<!-- the view is in 'read-only' state, show values and edit button -->
<button class="btn btn-default" data-trigger="onEdit">Edit</button>
<div>
<%= model.get('breed') %>
</div>
<% } %>
DEBUG: -------------------------------
DEBUG: Ember : 1.2.0
DEBUG: Ember Data : 1.0.0-beta.4
DEBUG: Handlebars : 1.1.2
DEBUG: jQuery : 1.10.2
DEBUG: -------------------------------
My model like this:
App.Order = DS.Model.extend
lists: DS.hasMany('list', async: true)
total: DS.attr 'number'
App.List = DS.Model.extend
order: DS.belongsTo 'order'
price: DS.attr 'number'
number: DS.attr 'number'
totalPrice: DS.attr 'number'
and the Order show Route:
App.OrderRoute = Em.Route.extend
model: (params)->
#store.find 'order', params.order_id
I can create and update the list in the order show page, when I create a list like this:
new_list = #store.createRecord('list', {"price": 5, "number": 1})
new_list.save().then (model) =>
#content.get('lists').pushObject model
The totalPrice is price * number but it calculate in the server, so create will return totalPrice from the server.
But the Order column total is plus all the List column totalPrice, so when I create the List my server will update Order total, but my web side could not see anythings were update. I must refresh the page will see the total update.
So, my first problem is:
How can use ember data to reload the Order total when the List is created?
And then when I update the list what is I create, the code like this:
Order show templates
{{#each lists itemController='list'}}
{{input type="text" value=price class="form-control"}}
{{input type="text" value=number class="form-control"}}
<a href="#" {{action 'editList'}} class="btn btn-primary btn-xs">
edit
</a>
{{/each}}
List Controller:
editList: ->
#content.set('price', #get('price'))
#content.set('number', #get('number'))
#content.save().then( (list)=>
return list
, ()->
alert 'Wrong!'
)
Nothing return in the server, when the list is update, and my problem is:
How can use ember data to reload the List totalPrice when the List is update? Because the totalPrice to calculate in the server.
I think all the problems can use the ember data reload() or use ember observes, but I don't know how to use them is the best way. Or have a convenient way to solve the problems?
Thanks in advance.
I suggest you try to use computed properties. That might solve this issue.
http://emberjs.com/guides/object-model/computed-properties/
I'm trying to make a searchable list of posts on a ruby on rails application that I made. I have AngularJS working on the application. All of the posts are saved on rails in #posts. How would I make AngularJS filter over that?
Here is the relevant view:
<h1>Posts</h1>
<div ng-app>
<div ng-controller = "PostCtrl">
<input placeholder = "Search Post Titles", ng-model="searchText">
<div ng-repeat="post in posts | filter:searchText">
<h2>{{post.title}}</h2>
<p>{{post.text}}</p>
</div>
</div>
</div>
I'm not sure how to fill the angular array posts with the objects in #posts.
It appears that your code works as it is. Here is a plunker that seems to recreate your code.
If you would like to filter inside the post object, you can use this syntax:
ng-repeat="post in posts | filter:{ text: searchText }"
The above will only search the values of the text property of post.
Continuing the answer of Davin Tryon, you can try as this.
First use ng-init="init()" in your Controller:
<div ng-controller = "PostCtrl" ng-init="init( <%= #posts.to_json %> )">
Then in your controller you can do:
$scope.init = (posts) ->
$scope.posts = angular.fromJson(posts)
Then posts will be a JavaScript Object which can be accessed in your scope as if you have query it using angular resource.
If you want to include the associations of posts (lets say comments i.e) you can check rails docs for to_json (or "as_json") and the :include option
Im developing a webapp that is feeded via a server. Its index presents some shops deals, having all of these deals ann id. In the .gsp, i have this code:
<div id="content">
<g:each in='${promos}' var='promo'>
<div id="up_box"></div>
<div class="deal_container">
<div class="images_container"><img src="${createLink(action:'showImage',controller:'image',id:promo.fotoPrincipal.id)}" width="120" height="105" />
</div>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="oferta">${promo.name}</div>
</g:link>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="flecha">
</div>
</g:link>
</div>
That means, when I click in one of the links, it will open an url like this:
app/promotion/openPromo/3
In case the deal with the id "3" is clicked. I have a "general" deal details page. I want that page to load always, and fulfill its details dinamically, depending the deal the user clicked. How can I do that?
I don't know if I have been clear in my explanation, if i haven't please ask.
Thank you!
Both links will be handled by the openPromo action in the PromotionController and will be passed the id of the promo as promoid.
You can then load the appropriate promo along with any other related offers you want the user to see and give these to the view. Eg:
class PromotionController {
def openPromo() {
Promo promo = Promo.get(params.promoid)
// Load any other relevant data or offers
render (view:'openPromo', model:[promo:promo, <any other model data here>]
}
}
You can then use the model data in your gsp.
You can do it adding that general info to a Template and then render it from the Controller like this
// render a template to the response for the specified model
def theShining = new Book(title: 'The Shining', author: 'Stephen King')
render(template: "book", model: [book: theShining])
You´ve got more info about the render in this link from the Grails documentation.
http://grails.org/doc/latest/ref/Controllers/render.html
It´s easy to do it, you can add a createLink like the first ones whith calls to an action who will render that template.
Hope it helps ;) and good luck!
I did it this way, after asking my workmate:
def openPromo() {
Promotion promo = Promotion.get(params.promoid)
[promo:promo]
}
Almost what you said David, but without rendering, if I name openPromo my gsp.
Thank you!