ment.io - model is not updating on mouse - angularjs

I am using ment.io plugin from http://jeff-collins.github.io/ment.io/#/examples with tinyMce for editor support.
All was well until i found that when i select menu item by using mouse, model is not updating automatically, though in editor it is showing selected text properly.
Further while investigation, found that model is updating when we do some key event inside editor after selection with mouse.
While select with arrow key and select using enter or tab, model is updating properly. This might be because, this is the key event which editor seeks in earlier case.
Here is link of fiddle for the scrnario https://jsfiddle.net/vikasnale/2p6xcssf/5/
<div ng-app="App">
<script type="text/ng-template" id="/tag-mentions.tpl">
<ul class="list-group user-search">
<li mentio-menu-item="Tag" ng-repeat="Tag in items" class="list-group-item">
<span class="text-primary" ng-bind-html="Tag.name | mentioHighlight:typedTerm:'menu-highlighted' | unsafe"></span>
</li>
</ul>
</script>
<textarea mentio-id="'tinyMceTextArea'" ui-tinymce="tinyMceOptions" mentio mentio-typed-text="typedTerm" mentio-require-leading-space="true" ng-model="Content" mentio-iframe-element="iframeElement"></textarea>
<mentio-menu id="hastmenu" mentio-for="'tinyMceTextArea'" mentio-trigger-char="'#'" mentio-items="tags" mentio-template-url="/tag-mentions.tpl" mentio-search="searchTags(term)" mentio-select="getTagTextRaw(item)"></mentio-menu>
<br/>
<p>Output Model: {{Content}}</p>
angular.module('App', ['mentio', 'ui.tinymce'])
.controller("Ctrl", ['$scope', 'mentioUtil',
function($scope, mentioUtil) {
$scope.getTagTextRaw = function(item) {
return '<i class="mention-tag-text" style="color:#a52a2a;">' + item.name + '</i>';
};
$scope.searchTags = function(term) {
var tagsList = [];
angular.forEach($scope.allTagList, function(item) {
if (item.id.toUpperCase().indexOf(term.toUpperCase()) >= 0) {
if (tagsList.length <= 5) {
tagsList.push(item);
}
}
});
$scope.tags = tagsList;
return tagsList;
};
$scope.allTagList = [{
"id": "ctp",
"name": "#ctp"
}, {
"id": "earningRelease",
"name": "#earningRelease"
}, {
"id": "presssrelease",
"name": "#presssrelease"
}, {
"id": "inversor-conference",
"name": "#inversor-conference"
}, {
"id": "live release",
"name": "#IACLive"
}, {
"id": "reval",
"name": "#reval"
}, {
"id": "margin",
"name": "#margin"
}, {
"id": "phonecall",
"name": "#phonecall"
}, {
"id": "Q4",
"name": "#Q4"
}];
$scope.tinyMceOptions = {
init_instance_callback: function(editor) {
$scope.iframeElement = editor.iframeElement;
},
resize: false,
width: '100%',
height: 150,
plugins: 'print textcolor',
toolbar: "bold italic underline strikethrough| undo redo",
toolbar_items_size: 'small',
menubar: false,
statusbar: false
};
}
]);
Note : This behavior is observed while using ment.io with tinymce
can't figure out the fix for this..
Please advice...

I faced the same problem and on search came across this post. Since there was no solution I thought I would dig deeper. Here are my findings, If you can add on top of this to find a solution it would be of great help.
(1) I am using MEAN framework with angularJS. And trying to implementMent.io with Tiny MCE
(2) Question: Why is it working with the codepen Ment.io example and not working with the implementation in jsfiddle ..
Answer or observation,
In the codepen implementation if you see they have included the tinymce but have never used it with the div.
also they have implemented a directive with listeners that is applied with ment.io called "contenteditable" this helps in properly replacing the values..
In the Jsfiddle example by Vikas Nale. The text area includes the tinymce editor. So as soon as we apply the Tinymce editor the model stops updating on enter key or mouse click, a space bar has to be pressed inorder for the model to be properly updates.
(3) probable reason Now I thought I will also add the contenteditable directive and it will take care of the events. But it seems that when we apply the tinymce editor, the mention menu, text area etc is placed in an iframe element. Because of which the events are not propagating correctly.
I also tried the Setup: option of tinymce. but as soon as we start that the # menu of Ment.io stops working.
this is as far as I could understand. I need to implement this in a project so any discussion, hint, pointers are welcome.

The above issue of not getting up to date text from the assigned data model was solved for me by getting the data from active editor of tinymce instead of the data model assigned to ment.io text area (see the attached screen shot).

Related

Is it possible to filter JSON in Angular using multiple drop down menus

I have built an app for a client using my basic / beginner knowledge of Angular. It took me a lot longer than most would but it has been a great learning experience for me.
The app imports JSON data which displays products with each returned result linking to a page with more information.
The products are manually filtered i.e. I Have built pages for all possible filters (the long hard way). This is displayed as 4 initial options then you go to another page with another 4 options based off the previous option then a final page with 4 further options from here a list is returned based on the the previous selections. This way has led to a large collection of JSON files all based off the filter combinations available.
Background story aside my client now wants to be able to filter all data using drop downs on the opening page. Is this possible?
My JSON data looks like below as an example
{"Level":"student","Style":"barb","Handle":"left","ItemCode":"5.25: 606603, 5.75: 606607","title":"Jaguar Prestyle Relax Leftie","Size":"5.25, 5.75","Price":"£50.00","Description":"One micro-serrated to prevent hair bunching or slippage, these are a particularly good choice for barbers. These scissors are ergonomically designed to reduce strain on your hands and wrists, with an offset handle, finger rest and finger ring inserts for extra comfort. Made from stainless steel with a matt satin finish.","picture":"img/stubarbleft/1.jpg"}
What I would like to do is display the returned data which is not an issue I can do that part. At the top of the page I would like to have a set of dropdown filters so the top one filters the returned data but LEVEL then the second one by STYLE (keeping the level the same as selected) and third option by HANDLE (again keeping the previous options in mind)
Is this possible to do. I am out of my depth a bit but trying to learn as I go.
Thanks for your time guys
Sure, it's possible. I've prepared a self explanatory code snippet that should help you out.
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', ['$scope',
function($scope) {
$scope.model = {
selectedStyle: "",
selectedLevel: "",
filterObject: { style : "", level : ""},
recordDetails: undefined,
options: {
styles: ["", "style1", "style2"],
levels: ["", "level1", "level2"]
},
data: [{
"id": 1,
"level": "level1",
"style": "style1",
"price": 100
}, {
"id": 2,
"level": "level2",
"style": "style2",
"price": 200
}, {
"id": 3,
"level": "level1",
"style": "style2",
"price": 300
}, {
"id": 4,
"level": "level2",
"style": "style1",
"price": 400
}]
};
$scope.showDetails = function (record) {
$scope.model.recordDetails = record;
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<select name="styleSelect" id="styleSelect" ng-options="option for option in model.options.styles" ng-model="model.filterObject.style"></select>
<select name="levelSelect" id="levelSelect" ng-options="option for option in model.options.levels" ng-model="model.filterObject.level"></select>
Current filter object: {{model.filterObject}}
<div ng-repeat="record in model.data | filter:model.filterObject">
<a href="#" ng-click="showDetails(record)">
<ul>
<li>{{record.id}}</li>
<li>{{record.level}}</li>
<li>{{record.style}}</li>
</ul>
</a>
</div>
<div>
Record details:
{{model.recordDetails}}
</div>
</div>
Please let me know whether anything needs further explanation.

Dropdown depends on other dropdown - angularjs

I'm having data in the form given below
var servers = [
{
"name": "server1",
"version":
[
"10.x"
]
},
{
"name": "server2",
"version":
[
"1", "2"
]
}
]
I want to have two drop down.
First dropdown will display "name".
When user selects name from the first dropdown, second dropdown should be populated with corresponding "version".
Non-working jsfiddle link: http://jsfiddle.net/fynVy/174/
You need to tweak your HTML template, so that the first drop down is displaying the server name, and that the options for the 2nd drop down are based upon the versions in the selected drop down (ngModel of the first drop down).
<div ng-controller="MyCntrl">
<select ng-model="server" ng-options="x.name for x in servers"></select>
<select ng-model="version" ng-options="val for val in server.version"></select>
</div>
working fiddle here

AngularJS custom directive within ng-repeat with dynamic attributes and two way binding

I'm banging my head on the wall over this for days and finally decided to post this question since I can't find an answer that matches what I'm trying to do.
Context: I'm building a dynamic form building platform that describes form elements in a JSON structure like this -
{
"name": "email",
"type": "email",
"text": "Your Email",
"model": "user.profile.email"
}
And then in the View I have a recursive ng-repeat that includes the field template like this -
<script type="text/ng-template" id="field.html">
<div ng-if="field.type === 'email'" class="{{field.class}}">
<p translate="{{field.text}}"></p>
<input type="{{field.type}}" name="{{field.name}}" class="form-control" dyn-model="{{field.model}}">
</div>
</script>
As you see, I use a custom directive dynModel to create the ng-model attribute with interpolated value of the model from the string value. So far do good.
Now I have a more complex scenario in which I have a collection of fields that can be added or removed by clicking on Add button or removeMe button. See below -
{
"name": "urls",
"type": "collection",
"text": "Your Profile URLs",
"model": "user.profile.urls",
"items": [
{
"name": "url",
"type": "url",
"text": "Facebook URL",
"model": "url"
},
{
"name": "url",
"type": "url",
"text": "Facebook URL",
"model": "url"
}
],
"action_button": {
"name": "add",
"type": "action",
"action": "addURL"
}
}
<div ng-if="field.type === 'collection'">
<button class="btn btn-info" dyn-click click-action="{{field.action_button.action}}" click-model="{{field.model}}">{{field.action_button.text}}</button>
<div dyn-ng-repeat="item in {{field.model}}" >
<div ng-repeat="field in field.items" ng-include src="'field.html'"></div>
</div>
</div>
As you'll notice, I have another custom directive that takes care of interpolation of {{field.model}} from the previous ng-repeat (not shown).
Now to the crux of the issue. As you see in the template, I have nested ng-repeats, the first one iterates through user.profile.urls and the second one iterates through the field parameters in JSON and creates the HTML tags, etc. One of those fields is a button (action_button) that is used to add more URLS to the list. When I click the button, I want it to trigger a function in my controller and effectively add a new child to the parent model (user.profile.urls). I then also want each URL, existing and new to have a remove button next to them that will be dynamic and will remove that particular item from the model.
If you see the code above, I have a custom directive dyn-click that reads in the
click-action="{{field.action_button.action}}"
That contains the function name (addURL) to be called that resides in my controller and the model
click-model="{{field.model}}"
(user.profile.urls) to which the new item is to be added. This is not working. The reason for this complexity is that I have multiple levels of nesting and at each level there are dynamic elements that need to be interpolated and bound. The directive dyn-click looks like this right now -
exports = module.exports = function (ngModule) {
ngModule.directive("dynClick",function() {
return {
restrict: 'A',
link: function(scope,element,attrs) {
$(element).click(function(e, rowid){
scope.clickAction(scope.clickModel, scope.$index);
});
}
};
});
};
With this code, when I click on the rendered form's Add button, the code in the $(element).click method above gets executed giving the following error -
Uncaught TypeError: undefined is not a function
I have tried a few different things with scope:{} in the dyn-click directive, with different errors and none of them have worked completely with two way binding of the model and calling the function as expected.
Help!
EDIT-1 - please see the comments:
$(element).click(function(e, rowid){
scope.$eval(attrs["clickAction"])(scope.$eval(attrs["clickModel"]), scope.$index);
});
EDIT-2: The plunker is here - http://plnkr.co/edit/DoacjRnO61g4IYodPwWu?p=preview. Still tweaking it to get it right, but you guys should be able to see the necessary pieces. Thanks!
EDIT-3: Thanks Sebastian. The new plunker is here - http://plnkr.co/edit/Z6ViT7scubMxa17SFgtx?p=preview . The issue with the field.items ng-repeat still exists. For some reason the inner ng-repeat is not being executed. Any ideas? Josep, Sebastian?

Angular JS: Handling different UI components from each views repeated using ng-repeat when the view look-feel are same

Apologies for the big Title, I couldn't come up with any thing better. Let me explain the issue I am having.
I have to render three cards where each of them share same look-feel, means each of them have a header section and a body section. I am using ng-repeat to get data from model and render these cards.
Code:
<div class="card">
<h1>{{card.title}}</h1>
<div ng-repeat="widget in card.widgets" class="widget">
<h2>{{widget.title}}</h2>
{{widget.type}}
</div>
</div>
Now, each of these Card's body should have different UI in it. For example, One card's body might have a chart using Hight Charts, Another one might just want to use a UI from jQuery UI library, etc..
How would I achieve it when I am looping using ng-repeat? Let me know if my starting direction is correct?
The model data look like this:
[
{
"id": "c001",
"title": "CARD 1",
"widgets": [
{
"title": "title 1.1",
"type": "line-graph"
},
{
"title": "title 1.2",
"type": "bar-chart"
}
]
},
{
"id": "c002",
"title": "CARD 2",
"widgets": [
{
"title": "title 2.1",
"type": "graph"
},
{
"title": "title 2.2",
"type": "bar-chart"
}
]
},
{
"id": "c003",
"title": "CARD 3",
"widgets": [
{
"title": "title 3.1",
"type": "line-graph"
},
{
"title": "title 3.1",
"type": "bar-chart"
}
]
}
]
Looking for help on this.
Thank You.
The solution for your problem is to combine both ng-switch and ng-repeat to achieve the effect you are attempting to get. Basically you would repeat over the items and switch between its type. Angular only includes the part of the DOM which matches the switch.
Here is a rough idea / html of what you should be doing.
<div class="card">
<h1>{{card.title}}</h1>
<div ng-repeat="widget in card.widgets" class="widget">
<h2>{{widget.title}}</h2>
<div ng-switch on="widget.type">
<div ng-switch-when="line-graph">
<!--do something here relevant to line graph-->
<div line-graph="widget.data"></div>
</div>
<div ng-switch-when="graph">
<!--do something here relevant to graph-->
<div graph="widget.data"></div>
</div>
<!-- and so on... add more if you need-->
</div>
</div>
</div>
I'm newer to Angular, but I believe you could encapsulate all of it through a directive. The idea would be we would have our own directive that we add to a div and then pass in the type and from there the directive could handle the logic and create the appropriate chart/element/whatever and do all the creation in the javascript file for the directive.
<div chart="line-graph"></div>
<div chart="bar-chart"></div>
module.directive('chart', function(){
return{
//logic to build various charts with different libraries here
}
});
It might be a pretty complicated directive to write, but it would be an elegant way to write it. #ganaraj did have that above with div graph="widget.data" inside of the ng-switch, but didn't really mention the directive part, just the switching. That would make the individual directives simpler, so may be the better overall approach, if each type is going to be vastly different.
This post below from simpulton is really good covering the directives portion of it. You can even make it a step further and make it more like a widget where the tag could be (his "act five: widget directive") and then to that pass in the type and the data to go with it. He is writing a directive to do animantion on some cirlces created in CSS, so there's no reason you couldn't use it to apply some highchart or jQuery UI code. It's really well written and includes code in jsfiddle so you can see it work as well.
http://onehungrymind.com/angularjs-directives-basics/

AngularJS filter on empty string

I have a simple Angular filter setup between a select and a list. When a user selects an item from the dropdown, the list is updated to show the matching result. The problem is that I have a "Select..." option first in the dropdown with no value, and when that is the selected value, all items are shown. I suppose that makes sense, but I want the opposite. If the selected option has no value, I don't want to show the list. Here are some relevant bits, followed by a link to a full fiddle:
The dropdown:
<select class="world-list" ng-model="selectedWorld" ng-options="world.worldId as world.worldName for world in allWorlds">
<option value="">Select...</option>
</select>
The list:
<ul class="unstyled" id="charList">
<li ng-repeat="char in characters | filter:selectedWorld">
{{char.charName}} - {{char.charRace}} {{char.charClass}}
</li>
</ul>
And here is a link to the full fiddle, which contains my JSON structures that drives all this: http://embed.plnkr.co/6XUmC5efO0Y1BRNLRUig
What I'm trying to do is rather simple, and I'm pretty new to Angular so I'm sure I'm missing something obvious. Checked the Jabbr room, nobody on. :(
Anyway, thanks for any and all help!
One way to accomplish this is to filter by specific properties of the iteration object:
<ul class="unstyled" id="charList">
<li ng-repeat="char in characters | filter:{worldId: selectedWorld}">
{{char.charName}} - {{char.charRace}} {{char.charClass}}
</li>
</ul>
Plunker
I noticed in your code "script.js" you don't seem to be accounting for the blank option.
I did not test this (I probably should prior to posing this answer), but it should work by placing the "blank" option within your script.js.
app.controller('WorldCtrl', function ($scope, $http) {
$scope.allWorlds = [{
worldId: "",
worldName: ""
},
{
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff",
worldName: "World 1"
},
{
worldId: "81211982-5613-4f9c-b704-7b6fa35faf84",
worldName: "World 2"
},
{
worldId: "df41208e-e8d2-46c9-8299-8f37632a51f8",
worldName: "World 3"
}];
$scope.characters = [{
charClass: "Rogue",
charName: "Vaes",
charRace: "Human",
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff"
},
{
charClass: "Warrior",
charName: "Azash",
charRace: "Orc",
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff"
},
{
charClass: "Mage",
charName: "Anele",
charRace: "Ogre",
worldId: "81211982-5613-4f9c-b704-7b6fa35faf84"
}];
});

Resources