Skip to content

Commit

Permalink
Merge pull request #739 from govorov/angular2
Browse files Browse the repository at this point in the history
Possible solution for Angular2 syntax support
  • Loading branch information
minad committed Nov 28, 2016
2 parents 365abe4 + c2b9225 commit 53723a3
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 3 deletions.
45 changes: 44 additions & 1 deletion README.md
Expand Up @@ -559,6 +559,8 @@ This renders as:
div class="first second third"
~~~
Splat attributes prefix may be configured via `splat_prefix` option. Default value is `'*'`
#### Dynamic tags `*`
You can create completely dynamic tags using the splat attributes. Just create a method which returns a hash
Expand Down Expand Up @@ -967,7 +969,7 @@ There are a lot of them but the good thing is, that Slim checks the configuratio
| Boolean | :streaming | false (true in Rails, see below how to disable it!) | Enable output streaming, improves the perceived performance |
| Class | :generator | Temple::Generators::StringBuffer/ RailsOutputBuffer | Temple code generator (default generator generates string buffer) |
| String | :buffer | '_buf' ('@output_buffer' in Rails) | Variable used for buffer |
| String | :splat_prefix | '*' | Prefix used for splat attributes |
There are more options which are supported by the Temple filters but which are not exposed and are not officially supported. You
have to take a look at the Slim and Temple code for that.
Expand Down Expand Up @@ -1047,6 +1049,47 @@ performance. The rendering time in total will increase. If you want to disable i
Slim::RailsTemplate.set_options streaming: false
~~~
### Angular2
Slim now supports Angular2 syntax. But you need to set some configuration options:
#### `splat_prefix` option
This option tells parser what syntax to use for splat attributes.
Default value is asterisk: `splat_prefix: '*'`
Asterisk is also used in Angular2 for structural directives such as `*ngIf` and others, so default configuration causes a conflict between slim and angular2 syntax.
There are two ways to resolve it:
* Set `splat_prefix` to any custom value, double asterisk, for example: `splat_prefix: '**'`. Now structural directives should work as expected. Remember that now splat attributes should be written with new custom prefix before them.
* Use alternative directive syntax without asterisk.
#### Attribute delimeters
Angular and slim both uses brackets in their syntax. So there are also two ways:
* Use alternative syntax for binding (`bind-...` and so on)
* Limit attribute delimeters to curly braces only:
```
code_attr_delims: {
'{' => '}',
},
attr_list_delims: {
'{' => '}',
},
```
Now you can use something like this:
```
h1{ #var (bind1)="test" [bind2]="ok" [(bind3)]="works?" *ngIf="expr" *ngFor="expression" } {{it works}}
```
Will be compiled to:
```
<h1 #var="" (bind1)="test" [bind2]="ok" [(bind3)]="works?" *ngIf="expr" *ngFor="expression">
{{it works}}
</h1>
```
## Tools
### Slim Command 'slimrb'
Expand Down
9 changes: 7 additions & 2 deletions lib/slim/parser.rb
Expand Up @@ -18,7 +18,8 @@ class Parser < Temple::Parser
shortcut: {
'#' => { attr: 'id' },
'.' => { attr: 'class' }
}
},
splat_prefix: '*'

class SyntaxError < StandardError
attr_reader :error, :file, :line, :lineno, :column
Expand Down Expand Up @@ -413,9 +414,13 @@ def parse_attributes(attributes)
end_re = /\A\s*#{Regexp.escape delimiter}/
end

splat_prefix = Regexp.escape(options[:splat_prefix])
splat_regexp_source = '\A\s*' << splat_prefix << '(?=[^\s]+)'
@splat_attrs_regexp = Regexp.new(splat_regexp_source)

while true
case @line
when /\A\s*\*(?=[^\s]+)/
when @splat_attrs_regexp
# Splat attribute
@line = $'
attributes << [:slim, :splat, parse_ruby_code(delimiter)]
Expand Down
155 changes: 155 additions & 0 deletions test/core/test_splat_prefix_option.rb
@@ -0,0 +1,155 @@
require 'helper'

class TestSplatPrefixOption < TestSlim

def prefixes
['*','**','*!','*%','*^','*$']
end

def options(prefix)
{ splat_prefix: prefix }
end

def test_splat_without_content
prefixes.each do |prefix|
source = %Q{
#{prefix}hash
p#{prefix}hash
}

assert_html '<div a="The letter a" b="The letter b"></div><p a="The letter a" b="The letter b"></p>', source, options(prefix)
end
end

def test_shortcut_splat
prefixes.each do |prefix|
source = %Q{
#{prefix}hash This is my title
}

assert_html '<div a="The letter a" b="The letter b">This is my title</div>', source, options(prefix)
end
end

def test_splat
prefixes.each do |prefix|
source = %Q{
h1 #{prefix}hash class=[] This is my title
}

assert_html '<h1 a="The letter a" b="The letter b">This is my title</h1>', source, options(prefix)
end
end

def test_closed_splat
prefixes.each do |prefix|
source = %Q{
#{prefix}hash /
}

assert_html '<div a="The letter a" b="The letter b" />', source, options(prefix)
end
end

def test_splat_tag_name
prefixes.each do |prefix|
source = %Q{
#{prefix}{tag: 'h1', id: 'title'} This is my title
}

assert_html '<h1 id="title">This is my title</h1>', source, options(prefix)
end
end


def test_splat_empty_tag_name
prefixes.each do |prefix|
source = %Q{
#{prefix}{tag: '', id: 'test'} This is my title
}

assert_html '<div id="test">This is my title</div>', source, options(prefix)
end
end

def test_closed_splat_tag
prefixes.each do |prefix|
source = %Q{
#{prefix}hash /
}

assert_html '<div a="The letter a" b="The letter b" />', source, options(prefix)
end
end

def test_splat_with_id_shortcut
prefixes.each do |prefix|
source = %Q{
#myid#{prefix}hash This is my title
}

assert_html '<div a="The letter a" b="The letter b" id="myid">This is my title</div>', source, options(prefix)
end
end

def test_splat_with_class_shortcut
prefixes.each do |prefix|
source = %Q{
.myclass#{prefix}hash This is my title
}

assert_html '<div a="The letter a" b="The letter b" class="myclass">This is my title</div>', source, options(prefix)
end
end

def test_splat_with_id_and_class_shortcuts
prefixes.each do |prefix|
source = %Q{
#myid.myclass#{prefix}hash This is my title
}

assert_html '<div a="The letter a" b="The letter b" class="myclass" id="myid">This is my title</div>', source, options(prefix)
end
end

def test_splat_with_class_merging
prefixes.each do |prefix|
source = %Q{
#myid.myclass #{prefix}{class: [:secondclass, %w(x y z)]} #{prefix}hash This is my title
}

assert_html '<div a="The letter a" b="The letter b" class="myclass secondclass x y z" id="myid">This is my title</div>', source, options(prefix)
end
end

def test_splat_with_boolean_attribute
prefixes.each do |prefix|
source = %Q{
#{prefix}{disabled: true, empty1: false, nonempty: '', empty2: nil} This is my title
}

assert_html '<div disabled="" nonempty="">This is my title</div>', source, options(prefix)
end
end

def test_splat_merging_with_arrays
prefixes.each do |prefix|
source = %Q{
#{prefix}{a: 1, b: 2} #{prefix}[[:c, 3], [:d, 4]] #{prefix}[[:e, 5], [:f, 6]] This is my title
}

assert_html '<div a="1" b="2" c="3" d="4" e="5" f="6">This is my title</div>', source, options(prefix)
end
end

def test_splat_with_other_attributes
prefixes.each do |prefix|
source = %Q{
h1 data-id="123" #{prefix}hash This is my title
}

assert_html '<h1 a="The letter a" b="The letter b" data-id="123">This is my title</h1>', source, options(prefix)
end
end

end

0 comments on commit 53723a3

Please sign in to comment.