AngularJS: how do I pass an :id parameter from Rails? - ruby-on-rails

I need to get data from my Ruby on Rails application, for that I made a pretty simple AngularJS controller:
# /app/assets/javascripts/angular/comments.js.coffee
app.controller 'CommentsController', ['$http', ($http) ->
store = this
store.comments = []
$http.get('/photos/3287.json').success (data) ->
store.comments = data
return
]
It works absolutely fine with the hard-coded :id, but I'm stuck with how to make it change dynamically. What is the easiest way to get an :id from Rails?

I suppose, you wanna edit it? So, you shouldn't use AJAX, you should create factory and use Angular $resource, so you fetch it with :id, set it to $scope, and use it in future.
MORE ABOUT Angular $resource - use Angular way

I believe you can include :id in the URL and then pass the id as a parameter to the get.
# /app/assets/javascripts/angular/comments.js.coffee
app.controller 'CommentsController', ['$http', ($http) ->
store = this
store.comments = []
$http.get('/photos/:id.json', {id: photo.id}).success (data) ->
store.comments = data
return
]
EDIT: I may have misunderstood. This is how you pass an id to $http, but if you need to get the id itself use $resource as Oleg had answered.

Thanks everybody. Here is my working code.
# comments.js.coffee
app = angular.module('comments', ['ngResource'])
app.controller 'CommentsController', ['$scope', 'GetComments', ($scope, GetComments) ->
store = this
store.response = []
$scope.init = (photo_id) ->
GetComments.get({id: photo_id}).$promise.then (data) ->
store.response = data.toJSON().comments
return
return
]
app.factory 'GetComments', ['$resource', ($resource) ->
$resource('/photos/:id.json', null)
]
Also I had to add ng-init to my view like this:
# foo.html.haml
%div{'ng-controller' => 'CommentsController', 'ng-init' => "init(#{#photo.id})"}

Related

Convert React.FormEvent to BodyInit using Fable-Elmish

I am trying to upload a file using Fable-Elmish and the React Helpers. However, I can't work out how to convert the form event when a file is selected into something that I can send off to the server using Fetch. This is the view:
R.input [
ClassName "input-control"
Type "file"
OnChange (fun x -> FileUploaded x.target |> dispatch )
] []
The corresponding part of my update function:
| FileUploaded file ->
model, Cmd.ofPromise postCsv file FetchSuccess FetchFailure
And the function to call the api with fetch:
let postData input =
let formData = FormData.Create()
formData.append("file.csv", input?files)
let defaultProps =
[ RequestProperties.Method HttpMethod.POST
; RequestProperties.Body (formData |> unbox)]
promise {
return! Fable.PowerPack.Fetch.fetch ("/api/data") defaultProps
}
How can I can convert the React.FormEvent into the BodyInit that fetch needs?
Your going to need to create a new FormData object and append the file to it.
let formData = FormData.createNew
formData.append("file", file.[0])
Then change your call to postRecord to pass formData instead of file. You need fetch to encode it, but you are just passing the raw array of files.
At least that is my understanding from the fetch example on uploading a file. Also, post record looks wrong to me, is there just a "post" method you could use? postRecord implies it is expecting a record type.
I think Fetch.postRecord is your problem, it sets Content-Type header to application/json, when it should be multipart/form-data.
You need to use raw fetch API, instead of powerpack wrapper, something like this.
Upon testing your code, checking ev.target?value revealed that the event just grabs the name of the selected file. postRecord appears to be used for sending json to an endpoint. You are going to want to port: https://medium.com/ecmastack/uploading-files-with-react-js-and-node-js-e7e6b707f4ef , to what you want to do.
It turns out I wasn't using the FileList API properly. Here's the working solution for the post method:
let postData input =
let formData = FormData.Create()
formData.append("file.csv", input?files?item(0))
let defaultProps =
[ RequestProperties.Method HttpMethod.POST
;RequestProperties.Headers [unbox EncType "multipart/form-data"]
; RequestProperties.Body (formData |> unbox)]
promise {
return! Fable.PowerPack.Fetch.fetch ("/api/data") defaultProps
}

cocoon select2 ajax load of partial - select2 element doesnt load

I have a rails app that uses cocoon
= link_to_add_association
to call the partial form
on the main form I have the coffee script to load all of the data for the select2 elements
when the ajax partial is inserted the select2 element doesnt appear. I need to instantiate it.
This is my form coffee/js
$(document).ready ->
$(".select2").each (i, e) ->
select = $(e)
options = {}
if select.hasClass("ajax")
options.ajax =
url: select.data("source")
dataType: "json"
data: (term, page) ->
q: term
page: page
per: 10
results: (data, page) ->
results: data
options.placeholder = "Select a value"
options.allowClear= "true"
options.dropdownAutoWidth = "true"
options.initSelection = (element, callback) ->
data = {id: element.val().split('||')[0], text: element.val().split('||')[1]};
callback data
select.select2 options
return
If I use the coocon - insert after binding to
$('body').bind 'cocoon:after-insert', (e, inserted_item) ->
$(".select2").each (i, e) ->
select = $(e)
options = {}
if select.hasClass("ajax")
options.ajax =
url: select.data("source")
dataType: "json"
data: (term, page) ->
q: term
page: page
per: 10
results: (data, page) ->
results: data
options.placeholder = "Select a value"
options.allowClear= "true"
options.dropdownAutoWidth = "true"
options.initSelection = (element, callback) ->
data = {id: element.val().split('||')[0], text: element.val().split('||')[1]};
callback data
select.select2 options
return
I get all elements on the page refreshed - naturally as I call all select2 objects. I didnt write this code for the select2 js.
All of the existing form elements are ok, but those elemeted added dynamically get refreshed - so they loose the values they have.
I want to only select the element added and make it work.
if i try
$('body').bind 'cocoon:after-insert', (e, inserted_item) ->
$(inserted_item).find(".select2").select2
return
It doesnt work either
Tried many options but my hair is thin now and I am needing help. JS is my arch enemy I really find it a pain .....
HELP!
$(document).ready ->
$('body').bind "cocoon:after-insert", (e, inserted_item) ->
select=$(inserted_item).find(".select2.ajax")
options = {}
if select.hasClass("ajax")
options.ajax =
url: select.data("source")
dataType: "json"
data: (term, page) ->
q: term
page: page
per: 10
results: (data, page) ->
results: data
options.placeholder = "Select a value"
options.allowClear= "true"
options.dropdownAutoWidth = "true"
options.initSelection = (element, callback) ->
data = {id: element.val().split('||')[0], text: element.val().split('||')[1]};
callback data
select.select2 options
return
Omg. hours on this and then I get it ....

Where do I initialize backgrid in my Backbone/Rails app?

I have a Backbone.js app and am trying to integrate with Backgrid, but I am having trouble understanding where I should be calling new Backgrid. I tried calling it in my view after things get rendered but appending the grid doesn't work because things aren't actually rendered yet. Here is some code:
SpreadsheetIndex.js.coffee
D3.Views.SpreadsheetsIndex = Support.CompositeView.extend
initialize: (options) ->
this.tables = options.tables
this.resources = options.resources
_.bindAll(this, 'render')
render: ->
this.renderTemplate()
this.renderSpreadsheets()
resources = this.resources
this.tables.each (table) ->
subset = resources.subcollection
filter: (resource) ->
resource.escape('table_id') == table.escape('id')
grid = new Backgrid.Grid
columns: table.attributes.columns
collection: subset
$("#"+table.escape('id')).append(grid.render().$el);
return this
renderTemplate: ->
this.$el.html(JST['spreadsheets/index']({ spreadsheets: this.tables }))
renderSpreadsheets: ->
self = this
self.$('tbody').empty();
spreadsheets/index.jst.ejs
<% spreadsheets.each(function(spreadsheet) { %>
<h4><%= spreadsheet.escape('name')%></h4>
<div id='<%= spreadsheet.escape('id') %>'></div>
<% }) %>
The issue is that the $("#"+table.escape('id')) selector does not select anything because the template hasn't rendered yet. It feels like I'm putting this in the wrong place. What am I doing wrong?
I'd guess that you want to use the view's #$ method to instead of $ to localize the selector to the view's el:
this.tables.each (table) =>
#...
#$("#"+table.escape('id')).append(grid.render().$el);
Note that -> has become => (to get the right #/this) and it now uses #$ instead of $.
While I'm here, you can do a couple other things to make your code more ideomatic:
Say class D3.Views.SpreadsheetsIndex extends Support.CompositeView instead of the JavaScripty D3.Views.SpreadsheetsIndex = Support.CompositeView.extend.
Use # instead of this, for example #tables = options.table rather than this.tables = options.table.
You can use string interpolation instead of + if you think it is cleaner:
#$("##{table.escape('id')}")
You rarely need bindAll, instead of _.bindAll(this, 'render') you could define render as render: => to get the binding to happen automatically.
You rarely need the var self = this trick in CoffeeScript, you can usually use a => instead. And you don't need either one here:
renderSpreadsheets: ->
self = this
self.$('tbody').empty();
you can just renderSpreadsheets: -> #$('tbody').empty()

watch expression is not picking up on model change from ng-change

I have the following
Rails HAML:
= select_tag "some-class",
options_for_select([['None', '']], ''),
{ class: 'some-other-class',
'ng-model' => 'someModel',
'ng-options' => 'option.name for option in someList',
'ng-change' => 'updateSelected()'}
Angular Controller:
scope.updateSelected = ->
#logic for updating model lives here. Model updates successfully by using some values defined within scope. Includes the following:
scope.someModel = "some_new_value"
Angular Directive:
SomeClassDirective= ->
restrict: 'C'
link: (scope, element, attrs) ->
monitorFormFields = (newValue, oldValue) ->
console.log "this is the inner function call"
#logic for setting the inner _destroy field lives here
scope.$watch 'someModel', monitorFormFields
However, when the Select List value is changed, 'this is the inner function call' never prints.(it does print when the directive first initializes, ie at page load). My question therefore is: Why isn't the $watch expression triggering, and how do I get it to trigger?
Thanks!
With this HTML:
<select class="some-class" ng-model="someModel"
ng-options="option.name for option in someList"></select>
Here is a directive that will watch for a change to someModel:
myApp.directive('someClass', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
var monitorFormFields = function (newValue, oldValue) {
console.log("this is in the inner function call");
}
scope.$watch('someModel', monitorFormFields);
}
}
});
Controller:
$scope.someList = [{ name: 'name1' }, { name: 'name2' }];
Note that you don't need to call a controller method to update someModel -- Angular does that automatically for us because of the ng-model attribute. So, the directive only needs to $watch for a change to that $scope property.
Fiddle.
I would like to from the element fetch a sibling with [_destroy] in the name and set it to either "0" or "1" depending on the value of the select box.
A more Angular approach would be to have model properties control whether "0" or "1" is displayed. E.g., in your controller:
$scope.destroy1 = "0";
$scope.destroy2 = "0";
In your HTML:
<div>{{destroy1}}</div>
<div>{{destroy2}}</div>
In monitorFormFields() you can change the values of these scope properties, and the view will automatically update -- there is no need to "find" siblings or update .val()ues.

Need Help to write a jasmine spec

Hello I am trying to write a jasmine test for a backbone view and one of its function. I want to test the correct behavior of the function in the case a user checks a checkbox in the rendered view then submit.
Here is the tests :
describe("buildCI()", function() {
describe("with a category selection allowed's quidget model", function() {
it("returns a CoacheeIssue model with the selected categories", function() {
selection_allowed = true;
lcc_selection_allowed = false;
var view = new Rails3DeviseRspecCucumber.Views.CategoryPicker({
collection: categoriesCollection,
answers: answers,
category_ids: category_ids,
credentials: credentialsCollection,
user_hash: user_hash,
selection_allowed: selection_allowed,
lcc_selection_allowed: lcc_selection_allowed
});
// render the view so we can manipulate its DOM elements
view.render();
elDebug = $(view.$el);
// Check programmatically a category checkbox
$(elDebug.find('input.category-checkbox#5061c6a48624da6f4100000a')[0]).prop('checked', true);
// call the buildCI() function and check the result
result = view.buildCI();
console.log(result);
expect(result.get('categories')).toContain('category one');
expect(result.get('categories')).not.toContain('category two');
})
})
Unfortunately the test fails with this message : Expected [ ] to contain 'category one'.
I know it is not a coding error, because it is working in live, I would just like to know how to test it.
Here is the function :
buildCI: () ->
# prepare the category_ids and categories (names) attributes
if #selection_allowed
selectedCategories = []
for checkbox in $('input.category-checkbox')
checkboxEl = $(checkbox)
if checkbox.checked
selectedCategories.push(_.find(#collection.models, (model) ->
model.id == checkboxEl.attr('id')
))
category_names = _.map(selectedCategories, (category) -> category.get('name'))
category_ids = _.map(selectedCategories, (category) -> category.get('_id'))
else
category_names = _.map(#collection.models, (category) -> category.get('name'))
category_ids = _.map(#collection.models, (category) -> category.get('_id'))
return new Rails3DeviseRspecCucumber.Models.CoacheeIssue({
is_solved: false, status: 'active', solution_value_estimate: '',
answers: #answers, categories: category_names, category_ids: category_ids
})
Thanks in advance
Is your selector too strict? I notice that it is:
$(elDebug.find('input.category-checkbox#5061c6a48624da6f4100000a')[0]).prop('checked', true);
but perhaps you only want it to be just:
$(elDebug.find('input.category-checkbox')[0]).prop('checked', true);

Resources