[ZEPPELIN-689] Add AngularJS z object and z.angularBind()#740
[ZEPPELIN-689] Add AngularJS z object and z.angularBind()#740doanduyhai wants to merge 4 commits into
Conversation
b78d12d to
4b09e93
Compare
c2e7a42 to
4d883c5
Compare
|
Anyone can have a look into this PR please ? I continue rebasing it from the master to avoid conflicts when merging and I don't want to do it 10x for nothing |
|
Hi @doanduyhai. Sorry for the late response. I've got some feedbacks here. two different feature in a function.I think it's more clear to provide passing paragraph id.It's passing paragraph id or ids using paragraph / paragraphs field in Parameters. z.angularBind('name1', value1, { paragraph : '20160126-153136_1060166247'})
z.angularBind('name2', value2, { paragraph : '20160126-153136_1060166247'})
z.angularBind('name3', value3, { paragraph : '20160126-153136_1060166247'})
...Second one is notebook portability, the code now depends on paragraph id and it's not portable anymore. If you export notebook and import notebook, the code will not work anymore unless user update all paragraph id in the code. Considering easy of use and notebook portability, i think following style of API would help. // we can get paragraph id from any html element in the paragraph.
// This way, paragraph id is no longer hardcoded so notebook becomes more portable.
var z = getZeppelinContext(htmlElement)
// user can avoid passing paragraph id repeatedly.
z.angularBind('name1', value1)
z.angularBind('name2', value2)
z.angularBind('name3', value3)front-end z.angularBind() call creates/updates angularObject into all interpreters binded.z.angularBind() updates angularObject of all interpreters binded. Advantage of having API in both front-end, back-end sideIt's more general question. Basically, this PR brings the API that currently only back-end side provides. Could you explain advantage of having API in both front-end and back-end side ? |
|
Hello @Leemoonsoo
The reason why I add the If we remove the <input type="text" ng-click="z.angularBind(....); z.runParagraph(...)" >or worst, the user should manually click on the target paragraph like in the screenshot, it's far from being user-friendly.
I do agree with your on this point to some extent. Yes, it forces the user to do a manual copy and paste but once it's done, you don't need to update it again (we'll see this point below). But this issue also applies to
You are absolutely right about this point. Indeed it's vey annoying that exporting and importing the same note make you loose the paragraph id. For this I see 2 solutions:
Allow me to disagree with this idea, if there are 10 paragraphs in the note with 10 different interpreters, it will create a lot of pollution. There is no reason that we push the Angular variable to many interpreters that are not used by the target paragraph(s). Another argument against binding the variable to all interpreters used by the note is because Angular variables are persisted in
Yes, this is part of the goal Usability Improvement in the roadmap. As an user, I want to be able to design a custom (with my own CSS etc ...) HTML form with my own controls (input text, drop down, calendar component, carrousel etc ...) and be able to push Angular variable to back-end. Without this PR, to do this, I'll need to write Scala code like import org.apache.zeppelin.display.angular.paragraphscope._
import AngularElem._
<div>
<div class="alert alert-info">This is an alert panel</div>
<form class="form-inline">
<div class="form-group">
<input type="text" class="form-control" placeholder="Input Text"></input>
</div>
<button type="submit" class="btn btn-primary">Click Me</button>
</form>
</div>.onEvent("ng-click", () => {
//my callback routine
}).displayIf I were a web developer using Zeppelin, I don't understand why I am forced to use Scala and the Spark interpreter to produce AngularJS and HTML source code... Isn't it more natural to write plain Javascript and AngularJS code if you want to render HTML and bind AngularJS ? I have given many talks about Zeppelin since 6 months and everytime I build a beautiful pure HTML form using Bootstrap, people in the audience ask me why I have another paragraph with source code like this: z.angularBind(...)
z.runParagraph(...)It seems so un-natural for users to have Scala code in a separate paragraph just to make the HTML form work. If we want to extend the user-base of Zeppelin, we need to provide user natural ways of creating Angular variable and not force them to use the Spark interpreter for that. There is also a demande on the user-mailing list recently to be able to update Angular variable from the front-end: So the demand for controlling Angular variable from the front-end is real, not just some fancy idea I have in mind.
Totally agree that if one user try to bind AngularJS using the back-end API with ZeppelinContext or the new Angular API you introduce and the front-end API I propose, it will make things complicated. But again, it's the user responsibility to make clean code. You can never stop people from doing stupid things right ? To conclude, what I propose is to make another PR, before we progress on this one, to persist the paragraph Id with the note content so that we don't have anymore export/import problem. WDYT ? |
|
I had a look into the |
two different feature in a function.
Idea behind AngularDisplaySystem was, give possibility to interpreter (back-end) interact with front-end. By leveraging two way binding and watcher of AngularJS, AngularDisplaySystem could able to work without introducing any Zeppelin API in front-end side. Current api is designed like
in my understanding, this PR trying to change it, to
In this perspective, i'm not sure it's good to embed run paragraph inside of z.angularBind() function in front-end side, while corresponding api in backend side does not. Also not every case uses angular displays system to let user click and run other paragraph. So, to me, it's less ambiguous and more consistent to have two apis. z.angularBind() and z.runParagraph(). passing paragraph id.
Right, if user use just hardcoded paragraph id to call existing In the similar manner, will give an option to avoid use hardcoded paragraph id.
paragraph id can not be simply changed, while of some features (rest api to run paragraph, iframe link, ...) and a lot of internal code (angulardisplay system, job scheduler, etc) assumes paragraph id is immutable. front-end z.angularBind() call creates/updates angularObject into all interpreters binded.
Right, that's what i wanted to say actually :-) Advantage of having API in both front-end, back-end side
The question from the mailing list, "pass variable defined in a angular interpreter to another paragraph which is scala interpreter", i can convert it to "how to initiate angularObject binding from the front-end code?" Because passing variable from front end to back end is already possible once they're binded. |
Ok, I see your point now. The table showing back-end and front-end API is quite clear. So I'll remove the
Your idea can work if we have a paragraph defining HTML element. But what if I want to bind Angular value to a SparkSQL paragraph or Cassandra paragraph ? %sql
SELECT * FROM my_table WHERE id = ${id}%cassandra
SELECT * FROM table WHERE key=${key}There is no HTML element so I cannot retrieve the paragraph Id using DOM JS function... The only way is to rely on paragraph id, or maybe paragraph title but we don't have unicity guarantee when using title ...
No no, I think there is a misunderstanding here. I don't want to change the paragraph id. What I want is that on Import, we keep the original paragraph id and don't generate a new one every time so that To enable this behavior, there is very few change to the code base, see the diff here The result seems working well with import/export and clone
So there is no problem, we both agree that
Exactly, so So to conclude, we indeed agree on many things. I propose the following next steps:
WDYT @Leemoonsoo ? |
|
@Leemoonsoo what do you think about my last proposal ? |
You're right in that case. Then we can just give user a tip to get paragraphId from HTML element in documentation.
Understood. Sounds nice. (what happen i import the same note again?)
And only one interpreter.
Sounds reasonable. Thank you for all the explanation |
d0db048 to
9b3cd03
Compare
|
Just a quick question in case I missed something, why are we limiting the angularBind to paragraph level only? |
|
Yes @corneadoug because:
|
You can re-importe the same note many times, the note id is re-generated each time and is different for each import. But the paragraphId remains the same. Remark: in the patch I mentioned above, I change the source code to re-use the paragraphId on note import BUT the jobId (the So in the last update of this PR, |
|
Is the PR now ok for merge ? I've modified it to take into account all the remarks so far |
| getClassName(), (Map) property); | ||
|
|
||
| // Push angular object loaded from JSON file to remote interpreter | ||
| pushAngularObjectRegistryToRemote(client); |
There was a problem hiding this comment.
init() will be invoked multiple times if there're multiple interpreters in the same interpreter group.
I think it's better make it call only once after interpreter process created. Except for this code looks good to me.
|
@doanduyhai Do you mind add someinfo about this new api to https://github.com/apache/incubator-zeppelin/blob/master/docs/displaysystem/angular.md? |
|
Don't worry about documentation, this will be added. Indeed if you look at the epic ZEPPELIN-635, I split all changes into isolated tasks. I will add a task for the change to re-use paragraph Id on note import as well as another task for global documentation of those new features ( Today, ASF JIRA is down so I cannot create those extra JIRAs but as soon as the ASF JIRA is back online, I'll do. I only consider the JIRA ZEPPELIN-635 finished once we merge all sub-tasks In the meantime, if no one has objection or other remarks, can we merge this PR so that I can progress on the other PRs ? |
|
Documentation JIRA: https://issues.apache.org/jira/browse/ZEPPELIN-742 |
|
@doanduyhai Could you take a look a comment for |
…ble using the paragraph scope then note scope
9b3cd03 to
282e217
Compare
|
Sorry, was pretty busy this week. So regarding your remark on calling Please review and validate |
282e217 to
cbcaa85
Compare
|
@doanduyhai Thanks for this new api and improvement. |
cbcaa85 to
4aa83f5
Compare
### What is this PR for?
Add client-side `z` object with method `angularBind()`
Leemoonsoo
Compared to the original implementation, I have simplified a lot.
Now, you can only bind angular variable to one unique scope, which is the **paragraph**. I just remove the note scope and also remove the `interpreter` parameter.
Indeed, when passing a `paragraphId`, on the server-side, we can retrieve the `Paragraph` object with the `noteId` + `paragraphId` so we can now which interpreter is currently being used.
The signature of the `angularBind(varName, value, paragraphId)` method has also been greatly simplified.
_This is a sub-task of epic **[ZEPPELIN-635]**_
### What type of PR is it?
[Improvement]
### Todos
* [ ] - Code review
* [ ] - Simple test
### Is there a relevant Jira issue?
**[ZEPPELIN-689]**
### How should this be tested?
* `git fetch origin pull/740/head:AngularJSBind`
* `git checkout AngularJSBind`
* `mvn clean package -DskipTests`
* `bin/zeppelin-daemon.sh restart`
* Create a new note
* In the first paragraph, put the following code
```html
%angular
<form class="form-inline">
<div class="form-group">
<label for="superheroId">Super Hero: </label>
<input type="text" class="form-control" id="superheroId" placeholder="Superhero name ..." ng-model="superhero"></input>
</div>
<button type="submit" class="btn btn-primary"
ng-click="z.angularBind('superhero', superhero, 'PUT_HERE_PARAGRAPH_ID')"> Bind Angular</button>
</form>
```
* Create a second paragraph with the following code:
```scala
z.angular("superhero")
````
* Retrieve the paragraphId of the second paragraph
* In the first paragraph, replace the text **PUT_HERE_PARAGRAPH_ID** by the correct paragraph id
* In the input text, put "Superman" and click on the **Bind Angular** button
* Execute the second paragraph to see that the _superhero_ variable is now set to **Superman**
### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? --> **No**
* Is there breaking changes for older versions? --> **No**
* Does this needs documentation? --> **Yes**
[ZEPPELIN-635]: https://issues.apache.org/jira/browse/ZEPPELIN-635
[ZEPPELIN-689]: https://issues.apache.org/jira/browse/ZEPPELIN-689
Author: DuyHai DOAN <doanduyhai@gmail.com>
Closes apache#740 from doanduyhai/ZEPPELIN-689 and squashes the following commits:
4aa83f5 [DuyHai DOAN] [ZEPPELIN-689] Implement z.angularBind() function
aabc1bc [DuyHai DOAN] [ZEPPELIN-689] ZeppelinContext angular() method should look for variable using the paragraph scope then note scope
954b8f6 [DuyHai DOAN] [ZEPPELIN-689] Make AngularObject constructor public because of serialization issue
3cac8d2 [DuyHai DOAN] [ZEPPELIN-689] Add Thrift RPC method angularRegistryPush()


What is this PR for?
Add client-side
zobject with methodangularBind()@Leemoonsoo
Compared to the original implementation, I have simplified a lot.
Now, you can only bind angular variable to one unique scope, which is the paragraph. I just remove the note scope and also remove the
interpreterparameter.Indeed, when passing a
paragraphId, on the server-side, we can retrieve theParagraphobject with thenoteId+paragraphIdso we can now which interpreter is currently being used.The signature of the
angularBind(varName, value, paragraphId)method has also been greatly simplified.This is a sub-task of epic ZEPPELIN-635
What type of PR is it?
[Improvement]
Todos
Is there a relevant Jira issue?
ZEPPELIN-689
How should this be tested?
git fetch origin pull/740/head:AngularJSBindgit checkout AngularJSBindmvn clean package -DskipTestsbin/zeppelin-daemon.sh restartz.angular("superhero")Screenshots (if appropriate)
Questions: