how can I make a $(document).ready using coffeescript to call my functions? - ruby-on-rails

I am trying to find a way to "call" my functions, for example, I have this function
initSlider = ($element)->
$element.find('.slider').each ->
$this.slider
... some functions
that prints on my web this
initSlider();
root = typeof exports !== "undefined" && exports !== null ? exports : this;
root.foo = function() {
return function($element) {
$element.find('.slider').each(function() {
$this = $(this);
$this.slider({
... and so on
and what I basically want to do its something like initSlider() so, the function can be executed... I tried just like that initSlider() on my file, but does nothing...
Thanks

$ -> initSlider()
would result in
$(function(){ initSlider() })
that, of course, presume that initSlider is in the global scope
another option is doing something like
initSlider = ($elm) ->
// your code indented
$ -> initSlider($elm)

Related

"process is not defined" in gatsby-browser?

I'm trying to insert an environment variable "GATSBY_EX_ENV" inside a window property to make it available to external scripts.
and GATSBY_EX_ENV is defined in my environment files alongside numerous other env variables which are accessed at runtime within React components. So I know it's declared correctly and that our env variables are generally working.
Right now in my gatsby-browser.js I am doing this:
export const onClientEntry = () => {
window.onload = () => {
var EXT = location.host
.split('.')
.filter(x => x !== 'www' && x && x !== DOMAIN && x !== 'com')[0];
// additional parsing
window._external.properties = { env: EXT };
}
}
This does work, but it's clunky and brittle. I want to replace that URL parsing with:
window._external.properties = { env: process.env.GATSBY_EX_ENV }
But when I do, my local develop site fails to load with the error "process is not defined". Should I be placing this code in a different method?

Angular Rails Karma testing Controller with factory (with backend api calls) dependency

I have a projectFactory:
#app.factory "projectFactory", ['$http', ($http) ->
factory = {}
factory.loadProject = (projectId) ->
$http.get( endpoint(projectId) )
(endpoint is a method that generates the backend api url)
I then have a projectCtrl that is dependent on that factory:
#app.controller 'ProjectCtrl', ['$scope','$routeParams', 'projectFactory', ($scope, $routeParams, projectFactory) ->
$scope.projectId = $routeParams.projectId
$scope.loadProject = (projectId) ->
projectFactory.loadProject(projectId)
.success((data)->$scope.project = data.project)
I then have my project_control_spec test:
'use strict'
describe "ProjectCtrl", ->
beforeEach module 'app'
ProjectCtrl = {}
$scope = {}
projectFactory = {}
beforeEach ->
module($provide) ->
$provide.factory "projectFactory", projectFactory
module inject($controller, $rootScope) ->
$scope = $rootScope.$new()
ProjectCtrl = $controller 'ProjectCtrl', {
$scope : $scope,
$routeParams: {projectId: 1},
}
it "should instantiate a PC", ->
expect(ProjectCtrl).toBeDefined()
it "should have access to the projectId via the routeParams", ->
expect($scope.projectId).toEqual(1)
it "should have access to projectFactory", ->
expect($scope.projectFactory).toBeDefined()
it "should create $scope.project when calling loadProject", ->
expect($scope.project).toBeUndefined();
expect($scope.loadProject(1)).toBe(1)
expect($scope.project).toEqual({//a project object})
I am getting the error ReferenceError: Can't find variable: $provide, when trying to require my projectFactory
You cannot inject $provide on line module inject($controller, $rootScope, $provide) ->. It is also not used or needed in any case.
You should also test this case with $httpBackend. Check the first example.
What if you try this ?
(It is in javascript, sorry I can't write coffee yet)
beforeEach( inject (function( $injector ){
$provide = $injector.get('$provide');
// ...
}));
I'm not familiar with CoffeeScript, but this is how I would do it in plain old JS:
var projectFactory = {};
beforeEach(function () {
module('app', function ($provide) {
$provide.factory('projectFactory', projectFactory);
});
});
When taking some of your code and running it through a Coffee to JS interpreter, I get the following result:
describe("ProjectCtrl", function() {
var $scope, ProjectCtrl, projectFactory;
beforeEach(module('app'));
ProjectCtrl = {};
$scope = {};
projectFactory = {};
beforeEach(function() {
module($provide)(function() {
$provide.factory("projectFactory", projectFactory);
});
});
});
Basically you're trying to load a second module called $provide, when what you actually want to do is open up a config block for the first module (app) and inject $provide into the configuration block.
If you were using angular.module for your actual implementation of app, it'd look something like this:
angular.module('app', []).config(function ($provide) {
$provide.value('..', {});
$provide.constant('...', {});
/** and so on **/
});
Whereas in your specs when using angular-mocks, a config block gets set like this:
module('app', function ($provide) {
$provide.value('..', {});
/** and so on **/
});
I hope that helps. I'm not sure how you would actually write this in CoffeeScript, but I'm sure you can figure that one out. When taking the result of the Coffee to JS interpreter of your code, I received the same result - there is no variable $provide.

jQuery Drop Down has no method 'val'

Below is a simple jQuery if statement I'm trying to use on my ASP MVC web page, but I continually get the error
Object #drpProducerType has no method 'val'
I also tried using the :selected syntax, but got the same results. Can anyone see what I'm doing wrong?
$(document).ready(function () {
var value = ('#drpProducerType:selected').val();
var value2 = ('#drpProducerType').val();
alert(value);
if (('#drpProducerType').val() == "S") {
alert(('#drpProducerType').val());
$('#DSSfields').removeClass('noSee');
$('#DSSfields').addClass('seeMe');
}
});
You are missing $
if ($('#drpProducerType').val() == "S") {
alert($('#drpProducerType').val());
$('#DSSfields').removeClass('noSee');
$('#DSSfields').addClass('seeMe');
}
You forgot the $ on your jQuery selector. Change it to:
if ($('#drpProducerType').val() == "S") {

Error: Unknown provider: aProvider <- a

I'm using AngularJS in a Ruby on Rails 3.2.8 project with assets.
When I load up my form which is using AngularJS on my development machine I don't have a problem. However when I load the same form up on my production server I get this error in the Javascript console:
Error: Unknown provider: aProvider <- a
I've tracked it back to my coffeescript file where I setup AngularJS for use within a form:
$ (event) ->
$("#timesheet_description").autocomplete({source: '/autocomplete/work_descs'})
# Create AngularJS module
app = angular.module 'timesheetApp', []
# Create a AngularJS controller
app.controller "TimesheetCtrl", ($scope) ->
$scope.costed_amount = 0
# Bind my module to the global variables so I can use it.
angular.bootstrap document, ["timesheetApp"]
If I comment all this out the page will load without errors and without AngularJS abilities.
Is the problem due to Rails assets compiling and minify?
Is there a way to fix this and still use coffeescript and Rails assets?
AngularJS, when using the style you're using right now (called pretotyping), uses the function argument names to do dependency injection. So yes, minification does break this completely.
The fix is simple, though. In every case where you need injection (are using '$xxx') variables, do this:
app.controller "TimesheetCtrl", ['$scope', ($scope) ->
$scope.costed_amount = 0
]
Basically, replace all function definitions with an array. The last element should be the function definition itself, and the first ones are the $names of the objects you want injected.
There's some more (albeit not clear enough) info on the docs.
If you miss the array notation somewhere , to locate this we need to modify the angular code little bit, but its very quick solution.
change is console.log("Array Notation is Missing",fn); ( line no 11 from function start)
Find out annotate function in angular.js (non-minified)
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
console.log("Array Notation is Missing",fn);
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}
To minify angular all you need is to do is to change your declaration to the "array" declaration "mode" for example:
From:
var demoApp= angular.module('demoApp', []);
demoApp.controller(function demoCtrl($scope) {
} );
To
var demoApp= angular.module('demoApp', []);
demoApp.controller(["$scope",function demoCtrl($scope) {
}]);
How to declare factory services?
demoApp.factory('demoFactory', ['$q', '$http', function ($q, $http) {
return {
//some object
};
}]);

Lift - Autocomplete with Ajax Submission

I would like to use an autocomplete with ajax. So my goal is to have:
When the user types something in the text field, some suggestions provided by the server appear (I have to find suggestions in a database)
When the user presses "enter", clicks somewhere else than in the autocomplete box, or when he/she selects a suggestion, the string in the textfield is sent to the server.
I first tried to use the autocomplete widget provided by lift but I faced three problems:
it is meant to be an extended select, that is to say you can originally only submit suggested values.
it is not meant to be used with ajax.
it gets buggy when combined with WiringUI.
So, my question is: How can I combine jquery autocomplete and interact with the server in lift. I think I should use some callbacks but I don't master them.
Thanks in advance.
UPDATE Here is a first implementation I tried but the callback doesn't work:
private def update_source(current: String, limit: Int) = {
val results = if (current.length == 0) Nil else /* generate list of results */
new JsCmd{def toJsCmd = if(results.nonEmpty) results.mkString("[\"", "\", \"", "\"]") else "[]" }
}
def render = {
val id = "my-autocomplete"
val cb = SHtml.ajaxCall(JsRaw("request"), update_source(_, 4))
val script = Script(new JsCmd{
def toJsCmd = "$(function() {"+
"$(\"#"+id+"\").autocomplete({ "+
"autocomplete: on, "+
"source: function(request, response) {"+
"response("+cb._2.toJsCmd + ");" +
"}"+
"})});"
})
<head><script charset="utf-8"> {script} </script></head> ++
<span id={id}> {SHtml.ajaxText(init, s=>{ /*set cell to value s*/; Noop}) } </span>
}
So my idea was:
to get the selected result via an SHtml.ajaxText field which would be wraped into an autocomplete field
to update the autocomplete suggestions using a javascript function
Here's what you need to do.
1) Make sure you are using Lift 2.5-SNAPSHOT (this is doable in earlier versions, but it's more difficult)
2) In the snippet you use to render the page, use SHtml.ajaxCall (in particular, you probably want this version: https://github.com/lift/framework/blob/master/web/webkit/src/main/scala/net/liftweb/http/SHtml.scala#L170) which will allow you to register a server side function that accepts your search term and return a JSON response containing the completions. You will also register some action to occur on the JSON response with the JsContext.
3) The ajaxCall above will return a JsExp object which will result in the ajax request when it's invoked. Embed it within a javascript function on the page using your snippet.
4) Wire them up with some client side JS.
Update - Some code to help you out. It can definitely be done more succinctly with Lift 2.5, but due to some inconsistencies in 2.4 I ended up rolling my own ajaxCall like function. S.fmapFunc registers the function on the server side and the function body makes a Lift ajax call from the client, then invokes the res function (which comes from jQuery autocomplete) on the JSON response.
My jQuery plugin to "activate" the text input
(function($) {
$.fn.initAssignment = function() {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
assign(ui.item.value, function(data){
eval(data);
});
event.preventDefault();
$(this).val("");
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
My Scala code that results in the javascript search function:
def autoCompleteJs = JsRaw("""
function search(term, res) {
""" +
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
assigneeCandidates(terms)
else
Nil
JsonResponse(JArray(_candidates map { c => c.toJson }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');" +
"""
}
""")
Update 2 - To add the function above to your page, use a CssSelector transform similar to the one below. The >* means append to anything that already exists within the matched script element. I've got other functions I've defined on that page, and this adds the search function to them.
"script >*" #> autoCompleteJs
You can view source to verify that it exists on the page and can be called just like any other JS function.
With the help of Dave Whittaker, here is the solution I came with.
I had to change some behaviors to get:
the desired text (from autocomplete or not) in an ajaxText element
the possibility to have multiple autocomplete forms on same page
submit answer on ajaxText before blurring when something is selected in autocomplete suggestions.
Scala part
private def getSugggestions(current: String, limit: Int):List[String] = {
/* returns list of suggestions */
}
private def autoCompleteJs = AnonFunc("term, res",JsRaw(
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
getSugggestions(terms, 5)
else
Nil
JsonResponse(JArray(_candidates map { c => JString(c)/*.toJson*/ }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');"))
def xml = {
val id = "myId" //possibility to have multiple autocomplete fields on same page
Script(OnLoad(JsRaw("jQuery('#"+id+"').createAutocompleteField("+autoCompleteJs.toJsCmd+")"))) ++
SHtml.ajaxText(cell.get, s=>{ cell.set(s); SearchMenu.recomputeResults; Noop}, "id" -> id)
}
Script to insert into page header:
(function($) {
$.fn.createAutocompleteField = function(search) {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
$(this).val(ui.item.value);
$(this).blur();
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
Note: I accepted Dave's answer, mine is just to provide a complete answer for my purpose

Resources