Entering in ui-select a value that is not in the list - ui-select

In this plunk I have a ui-select with a list containing names. I need to allow the user to enter a name from the list or a name that is not in the list.
If I try to enter a name not in the list, ui-select automatically repaces that value with the closest in the list.
Is there a way to a value not in the list?
HTML
<ui-select ng-model="ctrl.person.selected" theme="bootstrap">
<ui-select-match>{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

You should be able to use the tagging feature of ui-select to achieve what you want. Please see this Plunkr for a demo - if you type "Gary" into the box then tab out, it will populate with "Gary".
To use the tagging feature, you need to specify the attribute tagging and tagging-label as follows:
<ui-select ng-model="ctrl.person.selected" tagging="ctrl.tagTransform" tagging-label="false">
Because your model is an array of objects (not strings) then you will need to specify a function which will add the "new" item into the model:
vm.tagTransform = function(newTag) {
var item = {
name: newTag,
email: newTag+'#email.com',
age: 'unknown',
country: 'unknown'
};
return item;
};
Please note that the age and country have been set to "unknown" as we can't determine what they are by just typing the name in. The email also defaults to "NAME#email.com".
There is also a bug with ui-select where tagging-label needs to be set to false (for tagging to work in single select mode). This unfortunately means that you can't specify a tagging-label function to appear in the dropdown when you are typing (this does work in multiple select mode though).

Related

Include specific attribute or child-element text in breadcrumb rendering

Within the author it displays a breadcrumb, and I know you can modify its display to either some other static text or localisation, but I'm wondering if it's possible to dynamically show an attribute, or execute some other context-specific xpath dynamically.
As a test I can change the breadcrumb using the localisation editor variable ${i18n()}.
cc_config.xml
<elementRenderings platform="webapp">
<render element="num" as="${i18n(test)}" annotation="${i18n(test)}"/>
translation-cc.xml
<key value="test">
<comment></comment>
<val lang="en_US">Year</val>
"Year" is actually a num element.
However, trying any other variable, even 'more static' ones like ${cf} or ${tp} simply render the variable text literally, instead of evaluating it.
cc_config.xml
<elementRenderings platform="webapp">
<render element="paragraph" as="${xpath_eval(./#eId)}" annotation="${xpath_eval(./#eId)}"/>
<render element="p" as="${tp}" annotation="${tp}"/>
(paragraphs do have an eId attribute)
As you can see, I tried using annotation; but these tooltips also simply display the variable literally.
I also fiddled and tried a bunch of xpath stuff, like #eId/.#eId//#eId, but I think there's some restriction in using the Content Completion Configuration File with respect to editor variables.
So is the thinking right but I am doing something wrong, or is it not the right way but there is some other way to affect the breadcrumb? Maybe with the schema?
The element display names in cc_config.xml file do not support most of the editor variables. Most of them, like ${cf} (current file) and ${tp} (total number of pages) don't make sense to be used when rendering the name of an element.
The xpath_eval would make sense - the display name of an element may depend on its attributes (e.g. the #id attribute), it's index in the document (e.g. 'Section 3'), etc. We have a feature request registered for this case and I added your vote to it.
As a partial workaround you can use a JS API to compute the display name of the element based on the element original name and its attributes:
goog.events.listen(workspace, sync.api.Workspace.EventType.BEFORE_EDITOR_LOADED, function(e) {
e.options.elementNameEnhancer = function(elemName, attrs) {
var displayString = elemName;
var attr = attrs['id'];
if (attr) {
displayString += ' (#' + attr.attributeValue + ')';
}
return displayString;
};
});

use the same form for create and read operation

I have a master and a child component. The child component persists the data for the create mode as well as the edit mode. The child has a data section as follows which is being used when the component is in create mode
data() {
return {
title: '',
description: '',
organizer: '',
startdate: '',
enddate: '',
email: '',
phone: ''
}
},
and my inputs in create mode are as follows
<input type="text" placeholder="enter event title here" class="form-control" v-model="title">
In the edit mode, I am updating a prop value on the client as follows, which is
props:['currentevent']
The value of the currentevent is being passed from the master component to the child component and is also the value that is currently being edited.
so, the complete code for handling an input value looks like as follows
<input type="text" placeholder="enter event title here" class="form-control" v-if="currentevent" :value="currentevent.title">
<input type="text" placeholder="enter event title here" class="form-control" v-else v-model="title">
and in my save method (in the child component), I am checking if currentevent is empty or not. If it is empty then I trigger the add code otherwise, I trigger the update code.
Question : This works , but I have a large form and having to do this for each and every component is not a clean design . Can you please let me know what should I be doing ?
I totally appreciate your predicament. The best way to handle form data is to make it create/update agnostic. Here's what I'd recommend you try:
Instead of maintaining all the data fields as disparate properties, contain them in a single object, in this case I'm calling it eventObj for clarity:
data () {
return {
eventObj: {}
}
}
Then in your markup you'd reference them via the object:
<input type="text" placeholder="..." class="form-control" v-model="eventObj.title">
You'd then need to define a prop for passing in the data (as an object) from the parent component if you are editing:
props: {
currentevent: Object
}
And then you'd just need to map the incoming prop to the child component's data:
created() {
Object.assign(this.eventObj, this.currentevent || {})
}
Now when your inputs like <input v-model="eventObj.title"> are processed, if there is a saved title (that was passed in with currentevent) the field will be prepopulated with it, otherwise it will be blank.
I think this should help you in the right direction toward solving the complexity you're trying to figure out. There are other logistical issues involved with this kind of stuff in general, but I won't drone on. :)
The issue I see is you want to remove the v-if/else in the form. I will recommend here is keep your local data of child to be in sync with the props passed and only use local variable in the form.
One way to do this can be put a watcher on props and whenever props changes, update local variables and only use those variables in form.
watch: {
currentevent: function(newVal){
title = newVal.title,\
description = newVal.description
...
}
}

How can you pass a Blaze Template to the Column Title in Aldeed:Tabular?

In the column I'm displaying YES/NO values as red/green Fontawsome icons. I'd like the column heading to also be the same icon, and not text work.
The aldeed:tabular allows Blaze templates to be passed when displaying a cells contents.... but is there a setting to pass Blaze template to the column title.
For cells the syntax is
{
tmpl: Meteor.isClient && Template.blazeTemplateName
}
but for the title setting it doesn't seem to handle templates..the docs only have an example with a string
{ title: 'columnNameToDisplay' }
And when you try setting the title to a blaze template you get
TypeError: col.sTitle.replace is not a function
which clearly means its expecting text and not a template.
Any ideas how to also change the title display to a blaze template.
Figured it out....you can pass HTML to the title.
{
title: '<i class="fa fa-globe fa-fw" aria-hidden="true"></i>',
}

can I bind a value to a part of an object from elsewhere in the model?

I have a model that has (for example) shopping cart item IDs and a list of available items to add to the cart. It's a fake example.
table#items
tbody
tr(ng-repeat="item in cart.items")
td.hide {{item.id}}
td {{availableItems.where(id: item.id).first.name}} <-- pseudo code
td ({{item.shippingType}})
What I'd like to do is bind the second cell to the name of the item from the list of available items, rather than cluttering up the model by having that value in 2 places. Would I use a filter function to do this? The docs are very simplistic. How can I pass item.id to the filter function?
I've gotten closer with this in the markup:
td {{availableDatasources | filter: itemNameById}}
and this in the scope:
$scope.itemNameById= function(item) {
lodash($scope.availableItems).find({
id: item.id
}).name;
};
Only problem is I don't get a name back (even though that's what the filter seems to be returning) -- I get all of the available items. It's hitting the filter code but the filter seems to not be filtering.
Honestly, I'm not sure this is worthwhile. Your cart is probably just an array of references to objects in your availableItems array, so you're not really repeating anything.
But if I wanted to do as you say, I would include underscore.js in my project.
http://underscorejs.org/
Then in the controller, I would add underscore to the scope:
$scope._ = _;
In my template I would write
td {{_.findWhere(availableItems, {id: item.id}).name}}
Or, more likely, I would add a more specific function to my scope:
$scope.productFromId =
function(id) { return _.findWhere($scope.availableItems, {id: id});};
And then in the template:
td {{productFromId(item.id).name}}

How to get row id or parameter from dataurl in jqgrid to create a dynamic select list to edit row

I have an ASP.NET website with a C# back-end that uses Jqgrid.
I want users to be able to select an item in the grid to edit. One of the fields that can be edited will be presented to users as a drop-down selection list with only valid options for that user.
For example, let's say I have a grid displaying persons. If "person1" is edited, the user may be able to choose "blue" or "red" from the selection list for this item, but if "person2" is edited the user may only choose "yellow" or "green" from the selection list.
I want to dynamically populate the selection list based on what person/row is selected.
I have editoptions and dataurl set on the specific field as follows:
editoptions: { dataUrl: 'FetchData.aspx' }
Yet, I somehow need some kind of parameter to pass to the FetchData.aspx page so that it can do its background-checking on the specific person and create the correct list for the specific "person".
I was hoping I could pass the rowid or itemname or something as follows to identify the specific row/item that is selected:
editoptions: { dataUrl: 'FetchData.aspx?selecteditem=' + Id }
How can I pass a parameter so that I can create the correct list of items for the specific item? There are countless similar questions on the web, but I haven't been able to find a conclusive answer...
I have solved the problem by adding the following onSelectRow function (note that 'Id' in the below code represents the name of a column I want to pass as parameter. It can be any column name in your grid) :
onSelectRow: function (id) {
var temp = $("#list").getRowData(id)['Id']
$("#list").setColProp('mySelectListColumnName', { editoptions: { dataUrl:'FetchData.aspx?selecteditem=' + temp });
}
The above helps to pass the parameter. However, this alone is not sufficient - it allows FetchData.aspx to receive a parameter and customize the html response accordingly, but it only fetches the values once from the dataUrl - so it doesn't refresh when I select different rows.
To solve this, I have also added the following statement:
jQuery.extend(jQuery.jgrid.edit, { recreateForm: true });
This ensures that the edit form will be recreated every time that edit is clicked, which is what I want since the edit form will be slightly different for each row. Works perfectly. Hope this helps someone out there - there is a maze of options out there and this is the simplest I've seen. And it works.
Add this to your defaults:
jQuery.extend(jQuery.jgrid.edit, { recreateForm: true });
and this to your grid parameters:
ajaxSelectOptions: {
data: {
selecteditem: function() { return $('#list').jqGrid('getGridParam', 'selrow'); } }
}

Resources