I'm trying to render ag-grid with a fullWidth template, that is actually a directive.
gridOptions: {
fullWidthCellRenderer: function(node) {
var el = angular.element('<div />');
el[0].innerHTML = '<div directive="node.data"></div>';
var tpl = $compile(el)($scope);
return tpl[0];
}
The directive expects a model (from 'directive' attribute) but gets undefined.
I'm guessing there are scope issues here, and I do not want to stringify my data in the html template.
How can I pass the data object into my directive?
Thank you
I managed to solve this in 2 ways:
first is to create a child scope from $scope with `$scope.$new()', assign the node variable to it, and compile to template with it.
second, probably better, is to return the element with no $compile and use
angularCompileRows: true in gridOptions.
Related
In the affected application is a responsive table whose ColumnListItems are added via JavaScript code. Now the lines should be highlighted by the highlighting mechanism depending on their state. The first idea was to control the whole thing via a normal controller function. I quickly discarded the idea, since the formatter is intended for such cases. So I created the appropriate Formatter function and referenced it in the JavaScript code. The call seems to work without errors, because the "console.log" is triggered in each case. Also the transfer of fixed values is possible without problems. However, the values I would have to transfer are located within customData of each line...
No matter how I try to form the path I get an "undefined" or "null" output.
I have already tried the following paths:
"/edited"
"/customData/edited"
"mAggregations/customData/0/mProperties/value"
"/mAggregations/items/0/mAggregations/customData/0/mProperties/value"
The code from Controller.js (with consciously differently indicated paths):
var colListItem = new sap.m.ColumnListItem({
highlight: {
parts: [{
path: "/mAggregations/items/0/mAggregations/customData/0/mProperties/value"
}, {
path: "/edited"
}],
formatter: Formatter.setIndication
},
cells: [oItems]
});
// first parameter to pass while runtime to the formatter
colListItem.data("editable", false);
// second paramter for the formatter function
colListItem.data("edited", false);
oTable.addItem(colListItem);
The code from Formatter.js:
setIndication: function (bEditable, bEdited) {
var sReturn;
if (bEditable && bEdited) {
// list item is in edit mode and edited
sReturn = "Error";
} else if (bEditable || bEdited) {
// list item is in edit mode or edited
sReturn = "Success";
} else {
sReturn = "None";
}
return sReturn;
}
The goal would also be for the formatter to automatically use the value of the model in order to avoid its own implementation of a listener, etc.
I hope one of you has a good/new idea that might bring me a solution :)
Many thanks in advance!
You cannot bind against the customData. Because the customData is located in the element, it is like a property.
Thats why you defined it here on colListItem: colListItem.data("key", value)
You only can bind against a model.
So I see three solutions
Store the information in a separate local JSON model whereof you can speficy your binding path to supply the values to your formatter
Do not supply the information via a binding path to the formatter, but read a model/object/array from a global variable in the controller holding the information via this (=controller) in formatter function
Store the information in the customData of each element and access the element reference in the formatter function via this(=ColumnListItem).data().
Passing the context to the formatter similar to this formatter: [Formatter.setIndication, colListItem]
Cons of 1. and 2: you need a key for a respective lookup in the other model or object.
From what I understand I would solve it with solution 3.
I created my own viewhelper for getting all Information of an Image with the UID.
I get all the Information, if I print the array in the viewhelper, but im not able to create a new Fluid variable with all these information, to work with in the Template.
I tried to create the new variable with:
$this->view->assign('sliderItems', $sliderItems);
But I receive „Call to a member function assign() on null“.
I render the Image with:
public function render() {
/* MY RENDER STUFF */
And then I will submit the array with:
$this->view->assign('sliderItems', $sliderItems);
}
How can I solve this, to get an access with fluid?
You can use the templateVariableContainer for that:
$this->templateVariableContainer->add('key', 'value');
If you use the viewhelper in a loop, you'll have to remove the variable after render:
$this->templateVariableContainer->add('key', 'value');
$output = $this->renderChildren();
$this->templateVariableContainer->remove('key');
return $output;
If you use your viewhelper in your template, the variable will be available in your fluid template.
<vendor:viewhelper>
{key}
</vendor:viewhelper>
In my template I want check whether an entity has a relation to another one. Meaning one object is in an attached Object Storage of another one.
In the controller I can simply call:
if ($product->getCategory()->offsetExists($category) {
print 'In category ' . $category->getName();
}
But I can't figure out the correct syntax in the template. I tried those without luck (both evaluate to true everytime):
<f:if condition="{product.category.offsetExists(category)}">true</f:if>
<f:if condition="{product.category.offsetExists({category})}">true</f:if>
Is this even possible within the template?
You can only access properties via Getter from Fluid with no parameters, but you can implement an own ViewHelper to check that. As parameters you can use your Product and the Category. Then you can call your ViewHelper from Fluid this way:
<vh:checkOffset product="{product}" category="{category}" />
or inline
{vh:checkOffset(product: product, category: category)}
Within your ViewHelper you can check this in the way you've done it in your Controller:
public function render($product, $category){
return ($product->getCategory()->offsetExists($category));
}
Additionally to sretuer's answer, I'll only mention that you can create VH which will display block conditionally like:
File typo3conf/ext/your_ext/ViewHelpers/CheckOffsetViewHelper.php
<?php
namespace VENDORNAME\YourExt\ViewHelpers;
class CheckOffsetViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
public function render() {
return ($product->getCategory()->offsetExists($category))
? $this->renderChildren()
: '';
}
}
?>
So you can use it in the view:
{namespace vh=VENDORNAME\YourExt\ViewHelpers}
<vh:checkOffset product="{product}" category="{category}" >
Display this only if product is in category
</vh:checkOffset>
Of course you need to fix VENDORNAME and YourExt according to your extension, can be found at the beginning of every controller, model, repository etc.
You may consider https://fluidtypo3.org/viewhelpers/vhs/master/Condition/Iterator/ContainsViewHelper.html which is designed for creating conditions in Fluid that check if an array or Iterator contains another object and works exactly like f:if regarding then and else arguments and f:then and f:else child nodes.
I'm using Knockout with jQuery and jQuery templates. Assume that I have a template which expects a person object
<script type="text/html" id="person_template">
<tr><td>Forename</td><td><input type="textbox" data-bind="value:FORENAME" /></td></tr>
<tr><td>Surname</td><td><input type="textbox" data-bind="value: SURNAME"/></td></tr>
</script>
Now, if I pass an object with just a FORENAME to this template, I will get an error:
SURNAME is not defined error
I tried to create a custom binding in Knockout, but the error is thrown before it even gets there.
If I fill in these empty fields before passing the object to the template, I know everything will work out, but I would like to have the solution in my template rather than in my javascript.
Does anyone know a method that might help for situations like these?
This is a bit challenging, because you are within a template. While preparing the template, KO accesses the variable (well, actually it is accessed in jQuery Templates by a function that KO built).
One option is to pass your property as a string to a custom binding and make sure that it is initialized.
It would be like:
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
var value = valueAccessor();
if (!context[value]) {
context[value] = ko.observable();
}
var realValueAccessor = function() {
return context[value];
}
//call the real value binding
ko.bindingHandlers.value.init(element, realValueAccessor, allBindingsAccessor, context);
},
update: function (element, valueAccessor, allBindingsAccessor, context) {
var realValueAccessor = function() {
return context[valueAccessor()];
}
//call the real value binding
ko.bindingHandlers.value.update(element, realValueAccessor);
}
}
So, this would validate that your object has the field, if it does not it creates a new observable for that field. Then, it hands it off to the real value binding.
A very similar (but less verbose) alternative to this would be to have the binding ensure that the field is there and then rewrite the binding attribute to use the real value binding. Something like:
//Another option: rewrite binding after making sure that it is initialized
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
var value = valueAccessor();
if (!context[value]) {
context[value] = ko.observable();
}
$(element).attr("data-bind", "value: " + value);
ko.applyBindings(context, element);
}
}
Both of these assume that the field that you are passing is directly off of the object that is the context of your template (so, it wouldn't work if you passed something with global scope like 'viewModel.someProperty').
Here is a working sample with both options: http://jsfiddle.net/rniemeyer/dFSeB/
I would rather not pass the field as a string, but there is not really a good way around it that I see.
You'll be better off ensuring that the object passed to the template has all the parameters set in. If they are not then you can add default values but putting all this logic in the template is going against the MVVM pattern. The templates (like Views in mvc) are not supposed to contain any logic IMO.
I have 2 main questions.
Does extending things like Object count?
What is DOM wrapping?
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
After reading that article I couldn't find anything about DOM wrapping, and no specification and what exactly is and isn't DOM extension.
No, Object is specified as part of the Javascript language, while the DOM is an API only relevant in a browser environment and is used to "access and update the content, structure and style of documents" (W3C).
However, one of the reasons provided in that article arguing against the extension of DOM objects still applies to extending native types such as Object - namely the chance of collisions.
Wrapping an object refers to creating a new object that references the original, but providing additional functionality through the new, wrapper object.
For example, rather than extending a DOM Element object with a cross-browser addClass function like this:
var element = document.getElementById('someId');
element.addClass = function (className) {
...
};
You can instead define a wrapper function:
var ElementWrapper = function (element) {
this.element = element;
};
And add the function to its prototype:
ElementWrapper.prototype.addClass = function (className) {
...
};
And "wrap" elements like this:
var element = document.getElementById('someId');
var wrapped = new ElementWrapper(element);
wrapped.addClass('someClass');