Wrap text in a Vue component dynamically - javascript

Problem
I want to wrap some plain text in a Vue component dynamically, using mouseUp after selecting the text.
For example: <div> Hello World! </div> => <div> <Greeting/> World! </div>
Aprox solution
Right now, I'm wrapping it using document.createElement("span") and surrounding the range of the selection with it.
I have found similar questions like the next one, but I'm trying to avoid render level:
How can I dynamically wrap a substring with a component in Vue.js?

Well, I finally managed to solve it. (Before submitting, I spend a lot of time figuring out. I don't know how I solved it so fast)
Solution
var ComponentClass = Vue.extend(Annotation);
var instance = new ComponentClass();
instance.$mount();
this.range.deleteContents();
this.range.insertNode(instance.$el);
Where: Annotation is a Vue component and the Range is the selection range obtained previously.
I hope this help someone else!

Related

Angular component creation best practices

I'm often in doubt when I want a new behaviour of a component.
Let's make a simple example, I have <app-title> component:
<div>
<h1>{{title}}</h1>
</div>
Some time after, inside another page I need to put a button alongside the title. The problem is, should I create a new title component or should I parametrize the existing one?
I can edit <app-title> to look like this:
export class AppTitleComponent implements OnInit {
#Input() showButton: boolean;
title = 'App title';
constructor() {}
ngOnInit() {}
}
<div>
<h1>{{title}}</h1>
<button *ngIf="showButton">{{buttonTitle}}</button>
</div>
This is a simple example and may be obvious but using Angular I always have this problem, think about complex components: the #Input() would become many using this method, but creating a new component would increase files and complexity.
From this example you could say to create two components, one for title and another for button but that's only because this is a very simple case. Think about changing a component from "compact" mode to "expanded" and viceversa. On the one hand you may need to have the large component and on the other hand have it smaller in size and showing less information
Is there some guideline about this?
Thanks
I think it's important thinking about behavior within the context of your component. Is the button core to behavior of the title component? Does it make sense to not only display the button, but also handle its events within the context of the title component? If the answer is no, then at some granular level I'd split the components.
Here are some other things you can consider:
Anticipating that your title component may need some content wrapped with the title, you can use transclusion:
<div>
<h1>{{title}}</h1>
<ng-content></ng-content>
</div>
Then, in the parent, you'd do something like this:
<div>
<app-title-component title='title'>
<button>Some Button Text</button>
</app-title-component>
</div>
You could write a wrapper component that packages the title and the button together... ie:
<div>
<app-title-component></app-title-component>
<button>Some Button Text</button>
</div>
You could, as suggested, parameterize the configuration. I recommend thinking about if the behavior that you are parameterizing is part of the core behavior of the component. For example, if you want to paramaterize whether or not a legend shows on a chart, that makes sense because the legend is a core feature of the chart. But I probably wouldn't parameterize whether or not the chart should be followed by a raw data sheet. Instead I would create a new component for that and render them in sequence, because the data sheet is not part of the core behavior of the chart, even though sometimes I'd want to put them next to each other.
At the end of the day, you have to think about the decision in terms of your app, your app's future usability, and developer ease (e.g.- will it make sense to a future developer that this button is packaged with title).
I hope you find this helpful.

React - dangerouslySetInnerHTML being displayed as plain text

I've created a React app that displays job postings. When I click on a job post in the list, that particular job post's details are displayed.
I'm trying to display the body of the job post in the same way that it has been formatted at the source. To do that, I've tried using the following code:
<div id="apply-now" className="apply-section">
<h3>How to apply.</h3>
<div dangerouslySetInnerHTML={{ __html: currentJob.description }}></div>
</div>
However, when the component is rendered, the content is not formatted and is displayed in plain text without the appropriate formatting.
Here's an example that hasn't been formatted:
The word __"Application"__ should be in bold, the website link should be an actual link, as should "Click here for an application form" link.
The data I'm getting back from the API call is the same as above:
Join us! - We are looking forward to your application via the "Application" form Your contact: Ralph Ullmann Telefon: 089/ 5511 333783 https://www.consorsfinanz.de/karrier Click here for the application form!"
^^ Even above on StackOverflow it works! But I can't get it to work in my app!
I've been tearing my hair out for hours trying to fix this and I can't even understand why it doesn't work.
This sounds like an algorithm challenge. Sorry thought it could be done in an elegant way, but regex would be the better option
Use regex to match anything inbetween two underscores
In each match, replace remove the underscores and wrap each element in strong tags
const mdBoldToStrong = (text) => {
const surroundingUnderscores = new RegExp(/__(.*?)__/g)
return text
.replace(surroundingUnderscores, (word) => {
return `<strong>${word.replace(/__/g, '')}</strong>`
})
}
console.log(mdBoldToStrong('__Application__'))
console.log(mdBoldToStrong('Text__Application__Text__'))
console.log(mdBoldToStrong('__Text____Application__Text'))
Please let me know if you need any clarification.

Create new DOM element if a specific height was reached with part of the old content from DOM element before

Update: 27th December 2016
I did change the heading, since every DOM element could be the target (it actually doesn't matter if it is a <p> element or not).
I've provided some more informations about what I'm using and what I'm trying to achieve. Maybe there are native electron ways to achieve this? Or libs which could help me too?
Product: I'm going to extract tgz files with XMLs in it. Those XMLs will be used to automatically fill tables in the finished product. After that the tables and paragraphs will be editable where users can add new rows to the tables and also add new paragraphs to the page.
Framework: I'm using electron to fire the whole thing up.
Backend: NodeJS 7.x.x to make use of ES6 features
Libraries: jQuery, Bootstrap, Angular, Materialize, lodash, async, moment
Please keep in mind that I already did achieve all of my product needs. My original question was and still is if there is a more performant way of doing this:
I have a html page which can have 'n' containers called pages. A page can hold multiple <p> elements. This <p> elements are set to contenteditable="true".
Now I'm trying to create a javascript function which is checking the single page height with something like this:
// Set max container height to 10cm.
let containerMaxHeight = 377.95276 // 1 cm = 37.795276px;
if(containerElement.clientHeight > containerMaxHeight){
/**
* do desired stuff.
*/
}
everything easy so far. The function is getting the innerHTML of the <p> element which is currently beeing edited and "break the site" into a new site if the page height is above the limit. I have thought out a recursion wich is removing words (most of the time 1-3) of the old <p> element and inserting them to a newly created page with a <p> element until the maximum height of the old page is set to its maximum value.
Here is an example of my recursion (simplified) which is removing words from the end of innerHTML like this:
let lastWordToBeRemoved = oldParagraphElement.split("\\s+").pop();
// append old value to new <p>
newParagraphElement.innerHTML += lastWordToBeRemoved;
// remove last Word from old <p>
oldParagraphElement.innerHTML.slice(0, -lastWordToBeRemoved.length);
/**
* Recheck height of old page container if it is above the
* maximum redo above code
*/
I've startet out with this example:
https://delight-im.github.io/HTML-Sheets-of-Paper/
as you can see the pages are getting bigger and bigger if you edit them. I've already prevented that with my JS function.
Now that you have an idea of what I'm doing: Is there a more performant and or elegant way of doing this? I'm highly interested to hear how you would solve this problem.
If there is anything still unclear let me know, I will update my answer.
Thank you in advance!
Regards,
Megajin
Instead of splitting words, I think you should insert another p element into the expected position instead. Then you can easily move the exceeding paragraph into the new page. For example
paragraphElement.innerHTML = paragraphElement.innerHTML.replace(lastWordToBeRemoved, '</p><p class="exceeding-paragraph">' + lastWordToBeRemoved);
newPage.insertBefore(oldPage.querySelector('.exceeding-paragraph'), newPage.firstElementChild);

TinyMCE: How to insert content and select it?

How does one insert, or better, replace the current selection with some content and then select it?
Here's my text: Hello nice world!
As you can see nice is selected by the user. Now he clicks a button and this code is run:
editor.execCommand('mceReplaceContent', 'nasty');
This works just fine, the result is: Hello nasty world, but nothing is selected.
How do I make it automatically select nasty in the result content?
This seems like a very natural thing for one to want to do, but can't seem to find a straight-forward solution. I need this to work in mostly two cases 1) I am wrapping the selected text in a f.e. span element or 2) I am removing the wrapping span element.
I know there are better ways of dealing with nodes, but I'm more concerned about the pure text scenario right now.
Thanks in advance!
P.S. I am using TinyMCE 3 not 4.
I found this in the docs (API 3.x)
// Sets some contents to the current selection in the editor
tinyMCE.activeEditor.selection.setContent('Some contents');
// Selects the first paragraph found
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
The setContent function does practically the same as execCommand('mceReplaceContent'). I did not found something like the easy DOM properties selectionStart & selectionEnd.

Getting classNameBindings working with a button #view in handlebars with ember.js

I'm trying to get a class to appear on a specific button withing a handlebar view based on a property binding. I'm doing something that is like the Todo app that ember.js has on their site (http://emberjs.com/examples/todos/) and I'm trying to make the "Clear Completed" button disappear based on the value of a property.
I have a jsfiddle showing kind of what I'm going for here (http://jsfiddle.net/boushley/XEdNg/). If I add a className inside of the #view tag it shows up fine. But if I add a clasNameBindings it doesn't work the way I expect. Am I going about this wrong or is something broken here?
Aaron
I remember I had similar problems with classNameBindings too.
Try this instead: http://guides.sproutcore20.com/using_handlebars.html#binding-class-names-with-bindattr.
// Javascript for the view
App.AlertView = SC.View.extend({
priority: "p4",
isUrgent: true
});
// Template
<div {{bindAttr class="priority"}}>
Warning!
</div>
// Emits the following HTML
<div class="p4">
Warning!
</div>
If you just want to show/hide the button, then try binding the isVisible property.
isVisibleBinding: 'App.pageController.myProperty'
Hope this helps.
I discussed this here https://github.com/emberjs/ember.js/issues/322 and one of the people on the project helped with this, giving a clear way of how to perform this here https://github.com/emberjs/ember.js/issues/322#issuecomment-3332477 (http://jsfiddle.net/aNZSD/) In essence I needed to create a view that extends ButtonView and place the properties I want on this new view. This is the way it should work, I just wasn't seeing this properly.
Hope this makes it clearer for someone else.

Categories