AngularJS nested directive with ng-include - directive

I have problem with nested directive in ng-include. Here's the situation:
I have template in slim like this:
ng-include src="includePath" quick-edit=""
This includes file with template which has nested ng-includes, code:
.container ng-init="initialize()" style="padding:20px 50px"
.row
.col-xs-6.col-xs-offset-3
img ng-src="{{shop.logo}}"
.row ng-repeat="section in mainPageSections"
section
ng-include src="section.source"
Everything works fine but in first ng-include I have directive quick-edit which looks:
'use strict'
app = angular.module('saasPlatform')
app.directive 'quickEdit', ['$rootScope', '$compile', ($rootScope, $compile) ->
restrict: 'A'
link: (scope, element, attrs) ->
$rootScope.$on '$includeContentLoaded', (event, templateName) ->
$sections = element.find('.row > section')
for item in $sections
$section = angular.element item
$section.attr('editable','')
$section.attr('editable-type','section')
return
element.html $compile(element.contents())(scope)
]
## editable directive ##
app.directive 'editable', [()->
restrict: 'A'
link: (scope, element, attrs) ->
console.log 'editable'
]
Problem is that directive editable doesn't work.

Related

React-Rails: Use components with translations I18n

I have added to my project react-rails gem and I want to use for translated components.
I cannot put in the precompiled assets erb templates, but still I am trying to create components, make them available in all the project and then use them in some partial with some translation.
Working Scenario
# app/view/example/_react_component.coffee.erb
DOM = React.DOM
FormInput = React.createClass
displayName: "FormInput"
render: ->
DOM.div
className: 'row control-group'
DOM.div
className: 'form-group col-xs-12 floating-label-form-group controls'
DOM.label
htmlFor: #props.id
#props.label
DOM.input
id: #props.id
className: 'form-control'
placeholder: #props.placeholder
type: #props.type
DOM.p
className: 'help-block text-danger'
formInput = React.createFactory(FormInput)
window.ValidationFormInput = React.createClass
displayName: "ValidationFormInput"
getInitialState: ->
{ }
render: ->
formInput
id: "<%= t('validation_form.id') %>"
label: "<%= t('validation_form.label') %>"
placeholder: "<%= t('validation_form.placeholder') %>"
type: 'text'
validationFormInput = React.createFactory(ValidationFormInput)
# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>
Desired Scenario (not working)
# app/assets/javascripts/components/form_input.coffee
DOM = React.DOM
FormInput = React.createClass
displayName: "FormInput"
render: ->
DOM.div
className: 'row control-group'
DOM.div
className: 'form-group col-xs-12 floating-label-form-group controls'
DOM.label
htmlFor: #props.id
#props.label
DOM.input
id: #props.id
className: 'form-control'
placeholder: #props.placeholder
type: #props.type
DOM.p
className: 'help-block text-danger'
formInput = React.createFactory(FormInput)
# app/view/example/_react_component.coffee.erb
window.ValidationFormInput = React.createClass
displayName: "ValidationFormInput"
getInitialState: ->
{ }
render: ->
formInput
id: "<%= t('validation_form.id') %>"
label: "<%= t('validation_form.label') %>"
placeholder: "<%= t('validation_form.placeholder') %>"
type: 'text'
validationFormInput = React.createFactory(ValidationFormInput)
# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>
I guess that the issue is related to the scope of the definition of my component, but I cannot figure out how to make the component available for any partial.
Thank you in advance
Edit
In order to make the translations available, I found the gem I18n-js. After installing, I can easily run a rake task to create a js version of my config/locales/* translations
Excellent question.
There are a few ways to do this.
1- Usually, this is not just a question about how to pass data from Rails to React but rather how to generally pass data to Javascript. You can store the data in a meta in the header and access it from Javascript. This way you can still have your JS compressed and fast. (Instead of js.erb etc)
2- Passing all the translations to the react component. Basically, you can pass arguments to the react component, one of which is the translations. If it's a few translations, it's fine but if your list grows, the load would be heavy on your page.
3- Make your own Javascript translator. Here's a CoffeeScript example that I have created; make sure to add it in your assets' list before the other files.
In my code, I'm pulling the locale from meta (as you can see in the code). Feel free to edit this.
class Translator
#en = {
internet_connection_lost: "Your internet connection has been lost"
attempting_to_reconnect: "Attempting to reconnect!"
failed_to_reconnect: "Failed to reconnect..."
connection_success: "Connected"
reconnecting: "Reconnecting..."
bid_received: "Bid received. New balance $$bid_balance$$"
}
#ar = {
internet_connection_lost: "لقد فقدت الاتصال بالإنترنت"
attempting_to_reconnect: "نحاول إعادة الاتصال!"
failed_to_reconnect: "لم تنجح إعادة الاتصال بالشبكة..."
connection_success: "متصل بشبكة الإنترنت"
reconnecting: "إعادة الاتصال جارية..."
bid_received: "تم تلقي العرض. رصيد جديد $$bid_balance$$"
}
#get_translations: (locale) ->
switch (locale)
when 'en'
#en
when 'ar'
#ar
#translate: (val, interpolation) ->
# get locale from meta
locale = $("meta[name='locale']").attr("content") || "en"
translation = Translator.get_translations(locale)[val]
if interpolation
console.log "#{JSON.stringify(interpolation)}"
for k,v of interpolation
console.log "#{translation} : #{k} : #{v}"
translation = translation.replace(k, v)
return translation
window.Translator = Translator
And this is how you can use the Translator
message = Translator.translate(
"bid_received", { "$$bid_balance$$": 10 }
)

Inject data into Sass variables in Ember application

I'm working on an application that renders many, separate "sites" as subdirectories- e.g. /client/1, /client/2, etc. For each of these, two color values can be specified in the admin portion of the application.
I'd like to know if there's a method to inject the values, which were initially posted to and then retrieved from the back-end API by Ember, into a SCSS file for preprocessing?
I've found no solution thus far.
In our Ember/Rails application, we are generating CSS files for each client based on some settings in the database. For example, our Tenant model has two fields:
{
primary_color: 'ff3300',
secondary_color: '00ff00'
}
We expose routes
scope '/stylesheets', format: 'css' do
get 'tenant/:tenant_id', to: 'stylesheets#tenant_css'
end
And our controller looks something like this:
class StylesheetsController < ApplicationController
layout nil
rescue_from 'ActiveRecord::RecordNotFound' do
render nothing: true, status: 404
end
def tenant_css
# fetch model
tenant = Tenant.find(params[:tenant_id])
# cache the css under a unique key for tenant
cache_key = "tenant_css_#{tenant.id}"
# fetch the cache
css = Rails.cache.fetch(cache_key) do
# pass sass "params"
render_css_for 'tenant', {
primary_color: tenant.primary_color,
secondary_color: tenant.secondary_color
}
end
render_as_css css
end
protected
# our renderer, could also add a custom one, but simple enough here
def render_as_css(css)
render text: css, content_type: 'text/css'
end
# looks for a template in views/stylesheets/_#{template}.css.erb
def render_css_for(template, params = {})
# load the template, parse ERB w params
scss = render_to_string partial: template, locals: { params: params }
load_paths = [Rails.root.join('app/assets/stylesheets')]
# parse the rendered template via Saas
Sass::Engine.new(scss, syntax: :scss, load_paths: load_paths).render
end
end
This way, you can link to /stylesheets/tenant/1.css which will render the CSS for the tenant using the Sass engine.
In this case, in views/stylesheets/_tenant.css.erb, you'd have something like this (it's an ERB file but you can use Sass in there now):
#import "bootstrap-buttons";
<% if params[:primary_color].present? %>
$primary-color: <%= params[:primary_color] %>;
h1, h2, h3, h4, h5, h6 {
color: $primary-color;
}
<% end %>
<% if params[:secondary_color].present? %>
$secondary-color: <%= params[:secondary_color] %>;
a {
color: $secondary-color;
&:hover {
color: darken($secondary-color, 10%);
}
}
<% end %>
You'll note that I can now use #import to import anything in your stylesheet path for the Sass engine (in this case, I can utilize some helpers from Bootstrap Sass lib).
You'll want to have some sort of cache cleaner to wipe the cache when your model backing the CSS is updated:
class Tenant < ActiveRecord::Base
after_update do
Rails.cache.delete("tenant_css_#{id}")
end
end
So that's the Rails side in a nutshell.
In Ember, my guess is you'll want to load the stylesheet based on an ID, so that stylesheet cannot be hard-coded into "index.html". Ember CSS Routes addon might serve you well, but I found that it just appends <link> to the header, so if you need to swap CSS stylesheets at any time, this won't work. I got around this in a route like so:
afterModel(model, transition) {
// dynamically form the URL here
const url = "/stylesheets/tenant/1";
// build link object
const $link = $('<link>', { rel: 'stylesheet', href: url, id: 'tenant-styles' });
// resolve the promise once the stylesheet loads
const promise = new RSVP.Promise((resolve, reject) => {
$link.on('load', () => {
$link.appendTo('head');
resolve();
}).on('error', () => {
// resolve anyway, no stylesheet in this case
resolve();
});
});
return promise;
},
// remove the link when exiting
resetController(controller, isExiting, transition) {
this._super(...arguments);
if (isExiting) {
$('#tenant-styles').remove();
}
}
You could also add a blank element in the <head> and then use Ember Wormhole to format a <link> tag and render into the "wormhole".
Edit
You could also look into rendering Sass directly in the client application. For something as simple as two colors, this wouldn't have much of performance impact, especially if you used a service worker or similar to cache the results.

Ember button not staying disabled

I have an ember form where the button is supposed to be disabled for the creation of the object. The first two forms I did this on worked fine, this one is giving me issues. The button is disabled but then renables before the controller action is done.
Goal: Prevent double click on the Create button from creating a duplicate object
I attempted to do this by disabling the button after the first click
View (just the button piece)
<button type="submit" class="btn btn-primary" type="submit" id="submit-attribute"{{action 'submit' newAttribute}} {{bind-attr disabled="isProcessing"}}>Create</button>
Controller (in CoffeeScript)
App.SpecificationNewAttributeController = Ember.ObjectController.extend
isProcessing: false
newAttribute: Ember.Object.create({
name: ""
datatype: ""
group: ""
})
datatypes: ['number', 'range', 'list', 'boolean', 'date']
actions:
submit: (content) ->
#set "isProcessing", true
specification = controller.get('content')
specId = specification.get('identifier')
revision = specification.get('revision')
specificationAttribute = #store.createRecord "SpecificationAttribute",
name: content.name
group: content.group
datatype: content.datatype
specification: specification
specificationIdentifier: specId
revision: revision
specificationAttribute.save().then ((specificationAttribute) =>
attributeId = specificationAttribute.get("id")
specification.get('specificationAttributes').addObject specificationAttribute
specification.save().then ((specification) =>
specification.reload()
groups = specification.get('specificationGroups')
group = groups.where(display_name: content.group)[0]
controller.transitionToRoute('specification', specification).then ->
$.growl.notice title: "Specification", message: "New Attribute Added"
)
#set "isProcessing", false
)
Answering this myself. It is a lame mistake but maybe it will help someone. I needed to move the #set "isProcessing", false into the inner save().then block:
actions:
submit: (content) ->
#set "isProcessing", true
controller = #controllerFor('specification')
specification = controller.get('content')
specId = specification.get('identifier')
revision = specification.get('revision')
specificationAttribute = #store.createRecord "SpecificationAttribute",
name: content.name
group: content.group
datatype: content.datatype
specification: specification
specificationIdentifier: specId
revision: revision
specificationAttribute.save().then ((specificationAttribute) =>
attributeId = specificationAttribute.get("id")
specification.get('specificationAttributes').addObject specificationAttribute
specification.save().then ((specification) =>
specification.reload()
groups = specification.get('specificationGroups')
group = groups.where(display_name: content.group)[0]
#set "isProcessing", false
controller.transitionToRoute('specification', specification).then ->
$.growl.notice title: "Specification", message: "New Attribute Added"
)
)

render :partial not working as expected

In my index view of products, I have an "Add to cart" that calls javascript function addToCart:
addToCart: function() {
$.ajax({type: 'GET',
url: 'store/add_to_cart/2', // fixed id of product
timeout: 5000,
success: function() { alert('Added !'); },
error: function() { alert('Error !'); }
});
}
def add_to_cart // not working
begin
prod = Product.find(params[:id])
#cart = find_cart
#cart.add_product(prod)
render :partial => 'cart', :object => #cart if request.xhr?
end
end
With this add_to_cart, it renders the partial but also renders the default view for this method - add_to_cart.html.haml -
But if I do it like the following, it renders only the partial.
Could anybody explain me why it is different?
def add_to_cart // working fine
begin
prod = Product.find(params[:id])
#cart = find_cart
#cart.add_product(prod)
if request.xhr?
render :partial => 'cart', :object => #cart
else
redirect_to_index
end
end
end
Thanks for your help !!
The problem is that in that line rails get's confused where are the params for the render call and where is the statement.
You should probably try going like render(:partial => 'cart', :object => #cart) if request.xhr?.
And another thing. If you're using a local variable in the partial, it's better to use locals: {cart: #cart} instead of your :object. Or, if you are following the conventions and that cart partial is in app/view/carts/_cart.html* you can just say render #cart.

Get text value passed to valueBinding in Ember.TextField (ember-validations)

I'm writing an extension to Ember.TextField to change the class of the text field if the parent object is invalid. I'm using the ember-validations library to do the validations on my ember-data object. I have this working right now, but I'd love to be able to do it without passing in the validationMethod variable. How can I get just the text string passed in to valueBinding from within the view?
address.js.coffee
App.Address = DS.Model.extend(Ember.Validations,
street_1: DS.attr('string')
street_2: DS.attr('string')
...
validations:
street_1:
presence: true
)
validated_text_field.js.coffee
Ember.ValidatedTextField = Ember.TextField.extend(
validationMethod: null
classNameBindings: 'error'
focusOut: ->
object = #get('controller').get('content')
object.validateProperty(#get('validationMethod'))
error: (->
object = #get('controller').get('content')
if object.get("isValid")
return false
else
error_keys = object.get("validationErrors." + #get('validationMethod') + ".keys")
if error_keys
return error_keys.length > 0
else
return false
).property('controller.content.isValid')
)
edit.handlebars
{{view Ember.ValidatedTextField validationMethod="street_1" valueBinding="street_1" id="street_1" placeholder="Street 1" required="true"}}
...
Although I can't suggest it because it's internal code, you can get the strings from the bindings themselves.
validationMethod = #get("valueBinding._from").split(".").get("lastObject")
Check out how we handle the label for Ember-Bootstrap for another example: https://github.com/emberjs-addons/ember-bootstrap/blob/master/packages/ember-bootstrap/lib/forms/field.js