Permalink
Browse files

feature: Components attached() function will be called after Componen…

…t is added to the DOM, better differentiation between Widget + Component
  • Loading branch information...
MikeMitterer committed Jul 21, 2015
1 parent 5461a30 commit 16d7acba7102c8cf03e5a14fa4d49908b4207110
View
@@ -34,6 +34,7 @@ import 'package:di/di.dart' as di;
part "src/core/annotations.dart";
part "src/core/interfaces.dart";
part "src/core/utils.dart";
part "src/core/MdlComponentHandler.dart";
part "src/core/MdlConfig.dart";
View
@@ -43,6 +43,18 @@ class DataAttribute {
static _DataValue forValue(final dynamic value) {
return new _DataValue(value);
}
static _DataValue forAttribute(final dom.HtmlElement element,final String name) {
Validate.notNull(element,"Element for attribute $name must not be null");
Validate.notNull(name,"Attribute-Name for $element must not be null");
Validate.notBlank(name,"Attribute-Name for $element must not be blank");
if(!element.attributes.containsKey(name)) {
throw new ArgumentError("$element has not attribute '$name'");
}
final String value = element.attributes[name];
return new _DataValue(value);
}
}
class _DataValue {
@@ -74,6 +86,13 @@ class _DataValue {
return false;
}
String asString() {
if(_value is String) {
return _value;
}
return _value.toString();
}
}
class ElementProperties {
@@ -19,7 +19,26 @@
part of mdlapplication;
///
/**
* A Component with this interface can handle "Data-Requests" from its child (MdlComponent)
* For "ScopeAware-Components" make sure you create the Scope in [attached] this guaranties that
* the component is in the DOM and has all it's necessary parent-elements.
* Initialisation should also be done in [attached]!
*
* Implementation:
* class MySampleComponent extends MdlComponent implements ScopeAware {
* Scope scope;
*
* MySampleComponent() {
* }
*
* @override
* void attached() {
* scope = new Scope(this,mdlParentScope(this));
* _init();
* }
* }
*/
abstract class ScopeAware {
Scope get scope;
}
}
@@ -83,10 +83,13 @@ class DomRenderer {
//element.append(child);
parent.insertAdjacentElement("beforeEnd",child);
callAttached(child);
parent.classes.remove(_cssClasses.LOADING);
parent.classes.add(_cssClasses.LOADED);
completer.complete(child);
});
});
@@ -135,10 +138,13 @@ class DomRenderer {
parent.insertAdjacentElement("beforeEnd",child);
}
callAttached(child);
parent.classes.remove(_cssClasses.LOADING);
parent.classes.add(_cssClasses.LOADED);
completer.complete(child);
});
});
@@ -156,7 +162,7 @@ class DomRenderer {
//- private -----------------------------------------------------------------------------------
dom.NodeValidator _validator() {
dom.NodeValidator _validator() {
final dom.NodeValidator validator = new dom.NodeValidatorBuilder.common() // html5 + Templating
..allowNavigation()
..allowImages()
@@ -167,6 +173,8 @@ class DomRenderer {
return validator;
}
}
class _AllowAllAttributesNodeValidator implements dom.NodeValidator {
@@ -19,81 +19,6 @@
part of mdlcore;
const String MDL_WIDGET_PROPERTY = "mdlwidget";
const String MDL_RIPPLE_PROPERTY = "mdlripple";
class WrongComponentTypeException implements Exception {
factory WrongComponentTypeException([var message]) => new Exception(message);
}
/**
* [element] The element where a MDL-Component is registered
*
* Returns the upgraded MDL-Component. If {element} is null it returns a null-MDLComponent
* [type] The requested type. If [type] is null it returns the first available type.
*
* If [type] is null and if there are more than one types available it throws and error!
*
* Sample:
* static MaterialAccordion widget(final dom.HtmlElement element) =>
* mdlComponent(MaterialAccordion,element) as MaterialAccordion;
*/
MdlComponent mdlComponent(final dom.HtmlElement element,final Type type) {
//final Logger _logger = new Logger('mdlcore.mdlComponent');
if(element == null) {
return element as MdlComponent;
}
var jsElement = new JsObject.fromBrowserObject(element);
void _listNames(var jsElement) {
final Logger _logger = new Logger('mdlcore.mdlComponent._listNames');
final List<String> componentsForElement = (jsElement[MDL_WIDGET_PROPERTY] as String).split(",");
componentsForElement.forEach((final String name) {
_logger.warning("Registered Component $name for $element");
});
}
// If element has not MDL_WIDGET_PROPERTY it is not a MDLComponent
if (!jsElement.hasProperty(MDL_WIDGET_PROPERTY)) {
String id = "<not set>";
if(element.id != null && element.id.isNotEmpty) {
id = element.id;
}
throw "$element is not a MdlComponent!!! (ID: $id)";
}
String typeAsString;
if(type != null) {
typeAsString = type.toString();
//_logger.fine("Looking for $typeAsString!");
} else {
// If there is not "type" but more then one components - throw exception!
final List<String> componentsForElement = (jsElement[MDL_WIDGET_PROPERTY] as String).split(",");
if(componentsForElement.length > 1) {
throw new WrongComponentTypeException("$element has more than one components registered. ($componentsForElement)\n"
"Please specify the requested type.\n"
"Usually this is a 'MdlComponent.parent' problem...");
}
typeAsString = componentsForElement.first;
}
// OK we found the right type - return the component
if(jsElement.hasProperty(typeAsString)) {
//_logger.fine("Found $typeAsString");
return (jsElement[typeAsString] as MdlComponent);
}
// Show the available names
_listNames(jsElement);
throw "$element is not a ${typeAsString}-Component!!!\n(ID: ${element.id}, class: ${element.classes})\n"
"These components are available: ${jsElement[MDL_WIDGET_PROPERTY] as String}";
}
abstract class MdlComponent {
final Logger _logger = new Logger('mdlcore.MdlComponent');
@@ -165,6 +90,12 @@ abstract class MdlComponent {
MdlComponent get parent => _getMdlParent(element);
/// Called after [DomRenderer] has added this component to the DOM or
/// if [MdlComponentHandler] hast upgrade the component and it's already in the DOM!
void attached() {
// _logger.info("${this} attached!");
}
//- private -----------------------------------------------------------------------------------
MdlComponent _getMdlParent(final dom.HtmlElement element) {
@@ -19,6 +19,15 @@
part of mdlcore;
/// Thrown if you try to register more than one widget per element
/// Multiple components per element are allowed but not multiple widgets!
class MultipleWidgetException implements Exception {
factory MultipleWidgetException([var message]) => new Exception(message);
}
const String _MDL_WIDGET_PROPERTY = "mdlwidget";
const String _MDL_COMPONENT_PROPERTY = "mdlcomponent";
class _RootContext { const _RootContext(); }
/// Store strings for class names defined by this component that are used in Dart.
@@ -315,9 +324,6 @@ class MdlComponentHandler {
Validate.notNull(queryBaseElement);
Validate.notNull(config);
// final List<Future> futureUpgrade = new List<Future>();
// final Future future = new Future(() {
/// Check if {config.selector} is either the class-name or the tag name of {baseElement}
/// If so - upgrade
void _upgradeBaseElementIfSelectorFits(final dom.Element baseElement) {
@@ -347,12 +353,8 @@ class MdlComponentHandler {
_upgradeElement(element, config);
// futureUpgrade.add(_upgradeElement(element, config));
});
// });
// futureUpgrade.add(future);
// Future.wait(futureUpgrade);
}
/**
@@ -376,6 +378,19 @@ class MdlComponentHandler {
return _hasRepeatTemplate(element.parent);
}
/// Check if element is already in DOM (assume that if it finds a 'body' it is in DOM)
/// Is not the case for dynamically added components ([DomRenderer])
bool _isInDom(final dom.Element element) {
if(element.parent != null) {
if(element.parent.tagName.toLowerCase() == "body") {
return true;
}
return _isInDom(element.parent);
}
return false;
}
if (( !element.attributes.containsKey(_DATA_KEY) ||
element.attributes[_DATA_KEY].contains(config.classAsString) == false) && !_hasRepeatTemplate(element)) {
@@ -399,30 +414,45 @@ class MdlComponentHandler {
// Makes it possible to query for the main element in this component.
var jsElement = new JsObject.fromBrowserObject(component.hub);
/// Every Widget is a Component but not every Component is a Widget
void _registerWidget() {
if(jsElement.hasProperty(_MDL_WIDGET_PROPERTY)) {
final String name = jsElement[_MDL_WIDGET_PROPERTY];
throw new MultipleWidgetException("There is already a widget registered for $element, Type: $name!\n"
"Only one widget per element is allowed!");
}
// Store the widget-name as String (registration comes a few lines below)
jsElement[_MDL_WIDGET_PROPERTY] = config.classAsString;
}
if(config.isWidget) {
_registerWidget();
}
/// remember all the registered components in _MDL_COMPONENT_PROPERTY
/// Widget names are stored as comma separated list
/// Every Widget is a Component but not every Component is a Widget
void _registerComponent() {
// Add first element if property is not available
if(!jsElement.hasProperty(_MDL_COMPONENT_PROPERTY)) {
jsElement[_MDL_COMPONENT_PROPERTY] = config.classAsString;
}
// remember all the registered components in MDL_WIDGET_PROPERTY
if(jsElement.hasProperty(MDL_WIDGET_PROPERTY)) {
final List<String> componentsForElement = (jsElement[MDL_WIDGET_PROPERTY] as String).split(",");
if(!componentsForElement.contains(config.classAsString)) {
componentsForElement.add(config.classAsString);
jsElement[MDL_WIDGET_PROPERTY] = componentsForElement.join(",");
}
// MDL/JS wants to go this route - not sure if it makes sense!!!!
if(componentsForElement.length > 1) {
_logger.warning("$element has more than one components. ($componentsForElement)\n"
"This can lead into problems with 'MdlComponent.parent'...");
}
} else {
jsElement[MDL_WIDGET_PROPERTY] = config.classAsString;
final List<String> componentsForElement = (jsElement[_MDL_COMPONENT_PROPERTY] as String).split(",");
if(!componentsForElement.contains(config.classAsString)) {
componentsForElement.add(config.classAsString);
jsElement[_MDL_COMPONENT_PROPERTY] = componentsForElement.join(",");
}
// register the component
// register the component with it's name. It makes no difference if the component is a widget or not
jsElement[config.classAsString] = component;
}
} else {
jsElement[MDL_RIPPLE_PROPERTY] = component;
_registerComponent();
if(_isInDom(element)) {
callAttached(element);
}
}
@@ -448,25 +478,23 @@ class MdlComponentHandler {
var jsElement = new JsObject.fromBrowserObject(element);
MdlComponent component;
if(jsElement.hasProperty(MDL_RIPPLE_PROPERTY)) {
component = jsElement[MDL_RIPPLE_PROPERTY] as MdlComponent;
component.downgrade();
if(jsElement.hasProperty(_MDL_COMPONENT_PROPERTY)) {
jsElement.deleteProperty(MDL_RIPPLE_PROPERTY);
}
if(jsElement.hasProperty(MDL_WIDGET_PROPERTY)) {
final List<String> componentsForElement = (jsElement[MDL_WIDGET_PROPERTY] as String).split(",");
final List<String> componentsForElement = (jsElement[_MDL_COMPONENT_PROPERTY] as String).split(",");
componentsForElement.forEach((final String componentName) {
component = jsElement[componentName] as MdlComponent;
component.downgrade();
jsElement.deleteProperty(componentName);
});
jsElement.deleteProperty(MDL_WIDGET_PROPERTY);
jsElement.deleteProperty(_MDL_COMPONENT_PROPERTY);
}
if(jsElement.hasProperty(_MDL_WIDGET_PROPERTY)) {
// Component is already downgraded (All MdlComponents are listed in _MDL_COMPONENT_PROPERTY)
jsElement.deleteProperty(_MDL_WIDGET_PROPERTY);
}
// doesn't mater if it is a widget or a ripple...
Oops, something went wrong.

0 comments on commit 16d7acb

Please sign in to comment.