Polymer core-selector - using the selected data - polymer

I am building a custom element to read files in a given directory (e.g. ini files from a /config directory) and display them as a list wrapped in a core-selector. The user can then select a file from the list.
All works fine except reading the selected data seems clumsy. The relevant code is:
<div>
<core-selector on-core-activate={{getFileSelected}} selected="">
<template repeat="{{file in files}}">
<rnc-commandfilelist>
<span class="rnc-fileindex">{{file.index}}</span>
<span class="rnc-filename">{{file.commandFileName}}</span>
</rnc-commandfilelist>
</template>
</core-selector>
</div>
</template>
<script>
Polymer('rnc-getscaffoldini', {
matchstring: ".ini",
configurationdirectory: "configuration",
getFileSelected: function (e, detail, sender) {
var fileSelected = detail.item.children[1].innerText;
console.log(fileSelected);
var fullPath = this.configurationdirectory + "/" + fileSelected;
this.setAttribute('selectedfilename', fileSelected);
this.setAttribute('selectedfullpathname', fullPath);
}
});
</script>
The code line:
var fileSelected = detail.item.children[1].innerText;
gets the selected file name okay. Is there a better way of getting the selected data fields back?

The selectedModel property published by core-selector refers to the selected data model. The repeat syntax here repeat="{{file in files}}"> means that each item has a data model that contains the scope data plus a property called file. That means for each item, the selected file is stored in the selector as selectedModel.file. You can use binding to access the data.
So, if you do:
<core-selector selectedModel="{{selectedModel}}">
then you can have:
selectedModelChanged: function() {
// this.selectedModel.file refers to the particular file that is selected
}
--
Fwiw, you could also structure it this way:
<template repeat="{{files}}">
<rnc-commandfilelist>
<span class="rnc-fileindex">{{index}}</span>
Because of the different repeat syntax, now the data model is simply the file record, so:
<core-selector selectedModel="{{selectedFile}}">
then you can have:
selectedFileChanged: function() {
// this.selectedFile refers to the particular file that is selected
}
http://jsbin.com/putecu/1/edit

Related

Polymer 2 update paper-listbox item

I have a collection of objects which have a name property.
For editing these objects I have a paper-listbox that lists the names of the objects and lets the user select one of the objects by name. Next to the listbox I have a form with paper-inputs etc to edit properties of the selected object. One of the paper-inputs binds to the name property of the selected object so the user can change the name.
My problem is that the changes to the name property are not reflected to the listbox. How do I update the listbox after the user has changed the name?
I confirmed that the name change actually happens because when I change to another object and back to the previous one the changed name is still there.
The problem is just that the listbox does not update.
I tried something like:
this.notifyPath("myObjects")
but that doesn't do anything.
The paper-listbox is created like this
<paper-listbox selected="{{selectedObjectIndex}}">
<template is="dom-repeat" items="[[myObjects]]">
<paper-item>[[item.name]]</paper-item>
</template>
</paper-listbox>
selectedObjectIndex has an observer that sets the selected object
selectedPageIndexChanged(newValue, oldValue) {
...
this.selectedObject = this.myObjects[this.selectedObjectIndex];
}
Here below some working and not working examples. (I tried to illustrate the code at codepen as show DEMO, here also part of the code in order to quick inspection.
<paper-listbox selected="{{selectedObjectIndex}}">
<template is="dom-repeat" items="{{myObjects}}" >
<paper-item>[[index]].[[item.name]]</paper-item> <br/>
</template>
</paper-listbox>
<paper-input value="{{selectedObject.name}}" ></paper-input>
....
selectedObjectIndex:{
type:Number,
observer:'selectedPageIndexChanged'
}
}}
static get observers() {return ['nameValueChanged(selectedObject.name)']}
constructor() {
super();
}
ready() {
super.ready();
}
selectedPageIndexChanged(newValue, oldValue) {
this.selectedObject = this.myObjects[newValue];
}
nameValueChanged(name){
//this.myObjects[this.selectedObjectIndex].name = name;
//this.set('myObjects.' + this.selectedObjectIndex + ".name", name ) // --> Does not work
this.set('myObjects.' + this.selectedObjectIndex, { name: name} ) // --> Works
// this.splice('myObjects', this.selectedObjectIndex,1, this.selectedObject) -- Does not work
//this.notifyPath('myObjects.' + this.selectedObjectIndex + ".name") // -- works (with one of above)
}
}
The above signed Does not work lines changes the object but not observable at dom-repeat

Grouped context menu items in a Chrome extension

I have the following data structure (sample):
Folder1
- Folder2
- Snip1
- Snip2
- Folder3
- Snip3
- Snip4
- Folder4
- Snip5
There is no limit on nesting of folders inside one another
I need to allow the user to insert a particular snip whenever he/she right-clicks on a textarea. Now, I can either flatten the above data to:
- Snip1
- Snip2
- Snip3
- Snip4
- Snip5
to simply create context menu entries. But, I want better user experience so I will prefer something like:
to simulate the folder structure in the actual data.
Question: Is it possible to generate such type of context menu items' structure though Chrome extensions?
I have looked at the docs, but found nothing useful.
UPDATE: For anyone interested in the code, here's it:
// this is the top most folder
this.createCtxMenuEntry = function(parentId){
this.list.forEach(function(object, index){
var id = chrome.contextMenus.create({
contexts: ["editable"],
id: // generateUniqueIDYourself
title: object.name,
parentId: parentId
});
if(object is folder) object.createCtxMenuEntry(id);
});
};
The trick is to use the parentId property.
yes, it's possible to create dynamically (based on user data and changes) nested context menu with chrome extension
I created exactly the same thing with my V7 Notes extension (inserting notes in text fields)
example:
first you need to create main item and then recursively (I guess you have folder structure) create sub-folders appended to that main item
var OriginalArrayData = [....]; //or whatever you have
chrome.contextMenus.create({
title : "MAIN title",
id: "main_ID", //call it whatever you like, but it needs to be unique
type : "normal",
contexts : ["editable"],
},function() {
buildTree(OriginalArrayData, 'main_ID'); //pass original array and main_ID in first call
});
function buildTree(a, b) {
for (var i=0, l=a.length; i<l; i++) { //loop trough passed data
var notId = a[i].id, //create random unique ID for new items, I'm using id's from my notes
notText = a[i].text; //create item title, I'm using text from my notes
chrome.contextMenus.create({ //create CTX item
id: notId,//for ID use previously created
parentId: b,//for parent ID use second passed argument in function
title: notText,//for title use previously creted text (or whatever)
type: "normal",
contexts: ["editable"]
});
if (a[i].list) buildTree(a[i].list, notId);//if folder, recursively call the same function
}
}
on the last line, whenever your loop runs into a folder, you need to call your buildTree function, but this time you need to pass data/array from within that folder, and that folder ID so that it can be used as parent ID for further children
When function finishes traversing trough sub folders, it returns one step UP where it was entering those sub-loops
So, to summ up:
- create main item
- in callback create recursive function to loop all your data and create/append those new items to main item
- if you have folder, that folder now becomes main item and all it's children will be append to it...and so on
- this way context menu will follow folders structure of your data as deep as they are
I think this is the easiest and lightest way to create dynamic folder structure on context menus
When some of the data changes, you need to clean context menu and create it all over again... (or update it if you know which to target)

Polymer - Updating Element

I'm learning Polymer. One item that is challenging me is updating the item of an array. I wish there was a CDN for Polymer so I could put together a fiddle. For now though, I have an element defined like this:
my-element.html
<dom-module id="my-element">
<button on-click="onLoadData">Load Data</button>
<button on-click="onTest1Click">Test 1</button>
<button on-click="onTest2Click">Test 2</button>
<template is="dom-repeat" items="[[ data ]]" as="element">
<div><span>[[ element.id ]]</span> - <span>[[ element.status ]]</span></div>
<template is="dom-repeat" items="[[ element.children ]]" as="child">
<div> <span>[[ child.id ]]</span> - <span>[[ child.status ]]</span></div>
</template>
</template>
</template>
<script>
Polymer({
is: 'my-element',
properties: {
data: {
type: Array,
value: function() {
return [];
}
}
},
onLoadData: function() {
// Generate some dummy data for the sake of illustration.
for (var i=1; i<=3; i++) {
var element = {
id: i,
state: 'Initialized',
description: ''
};
element.children = [];
for (var j=1; j<=5; j++) {
var child = {
id: i + '-' + j,
state: 'Initialized',
description: ''
}
element.children.push(child);
}
data.push(element);
}
},
// Setting an individual property value works
onTest1Click: function() {
this.set('data.0.children.0.state', 'data set');
},
// Setting an entire object value does NOT work.
onTest2Click: function() {
var c = this.data[0].children[0];
c.state = 'data set';
this.set('data.0.children.0', c);
}
});
</script>
</dom-module>
For some reason, if I update the property value of an array element (as shown in onTest1Click), the UI is updated properly. However, if I update an entire element (as shown in onTest2Click), the UI does NOT get updated. In my real problem, I'm updating multiple properties on an element. For that reason, I'm trying to update an array element and not just a property. Am I doing something wrong or misunderstanding something? Or, am I going to have to update each property value individually?
If you want to mutate an array, rather than just an object in an array (such as swapping out an entire element in an array), there are array mutation methods similar to this.set.
For example, this.splice('data.0.children', 0, 1, c) will remove the current item at the 0 index of the child array and replace it with a new one, which is what it appears you're trying to do. There's also this.shift, this.unshift, this.push and this.pop. These are all similar to their Array prototype counterparts.
One thing to note is that in your example, you're also not actually swapping out the entire object. When you grab the element from the array, mutate a field, and try and replace it with itself, you're not actually replacing it, so that doesn't actually trigger an update. And since the mutation of the field was done outside of Polymer's notification system, that also doesn't trigger an update. If you replace the item with an actual different object, it will work using splice.
https://jsbin.com/rapomiyaga/1/edit?html,output (This is a modified snapshot of Günter Zöchbauer's jsbin)
If you're not making a copy of the object/a completely new object, you'll want to update each field individually through this.set.
Yes, you are going to need to update each property value individually. When you call set, Polymer will go to the given path and check if the value has changed. If the value is an object it will compare the references (and not the subproperties). Since the object reference has not changed, it will not update the UI.

angularjs save rendered values in html in a variable

I hope someone can help me with this, It's a strange question maybe as I didn't find an answer online.
I call the database and retrieve a list (in json) of items.
Then in angularjs,I render this list by extracting relevant pieces of data(name,age,etc) and show it properly in a table as a list of rows.
I have then an edit button that takes me to another page where I want to put a dropdown list.
What I want to know if is possible to add to that dropdown list the rendered list I previously created in my previous page.
is it possible to save the previously rendered list in a variable and then use that variable in the dropdown?
thank you
You could store the list within a controller and make this data availablte to this dropdown, I think.
Instead of trying to query for the list, add the list to the template, get the list from the template and render somewhere else, I'd suggest query for the list, save the list in a service , and then when you want to use that list again, get it from the service. Something like:
service:
var services = angular.module('services');
services.factory('getListService',['$http',function($http){
var getListOfStuff = function(){
//call to database
return //your json
};
var extractNameAgeEtc = function(){
var myListOfStuff = //get list of stuff from $http or database
var myListOfNameAgeEtc = //make a list of tuples or {name,age,etc} objects
return myListOfNameAgeEtc;
};
return {
extractNameAgeEtc : extractNameAgeEtc
};
}]);
controllers:
angular.module('controllers',['services']);
var controllersModule = angular.module('controllers');
controllersModule.controller('tableRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your table rendering template, probably with ng-repeat
$scope.MyTableValue = getListService.extractNameAgeEtc();
}]);
controllersModule.controller('dropdownRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your dropdown rendering template, probably with ng-repeat
$scope.MyDropDownValue = getListService.extractNameAgeEtc();
}]);

dynamically get options of a dropdown menu in angular

The main problem is that I have a dropdown menu whose options should be updated dynamically.
The workflow is as follows:
I have an input element connected to an ng-model called toSubmit that when longer than 3 characters should fire an http.get call to fetch the list that should populate the dropdown menu.
So this list will change everytime the toSubmit variable changes. Let's call this list database (in the controller it is $scope.database.
What I am trying right now is a very simple solution that doesn't work most probably because the html DOM that contains the dropdown list is loaded at the very beginning and does not keep track of the changes in the options.
In my controller I have the following part which watches over toSubmit:
$scope.toSubmit = '';
$scope.$watch('toSubmit',function(query){
if (query.length >= 3){
getQueryDatabases.companyNameService({'field':'name','query':query,'numberOfHits':'10'},'CIK').prom.then(
function(dataObject){
$scope.database = dataObject;
// dataObject.forEach(function(item){
// $scope.databaseString.push(item.cik + ' ' + item.companyName);
});
});
}
});
And my html looks like the following:
<label for="nameCompany">Name:</label>
<input type="text" ng-model="toSubmit"></input>
<select ng-model="database" ng-options="line in database"></select>
Now my take was take by binding database with ng-Model I would get the result but I am most likely wrong. Can someone please help me?
I recommend you to use select2 that'll handle things like limiting input before server request and have great look and extendibility.
You need to add angular-ui-select2 to your project.
Here is code for you:
Html:
<input class='form-control' data-ng-model='position.company' data-ng-required data-placeholder='Company:' data-ui-select2='employerSelect2Options' id='company_name' type='hidden'>
JavaScript:
$scope.employerSelect2Options = {
minimumInputLength: 2,
query: function (query) {
var _query = query;
var companies = Restangular.all('companies').getList({query: query.term});
companies.then(function(data) {
var results = {results: []};
_.each(data, function(element, index, list) {
results.results.push({id: element.id, text: element.name});
})
if(!_.contains(_.map(data, function(element){ return element.name; }), _query.term)) {
results.results.push({id: _query.term , text: 'Create company "' + _query.term + '"'});
}
_query.callback(results);
})
}
};
My example also contains logic for add "create company" if zero results returned. In this case position.company will contain text of non found company name in id field and you can check it on server side and create one before assigning id.
This logic in
if(!_.contains
condition.