Capturar error VUE

Capturar error en vue, insertar este script al final del body.

<script>
    
    Vue.config.errorHandler = (err, vm, info) => {
        // err: error trace
        // vm: component in which error occured
        // info: Vue specific error information such as lifecycle hooks, events etc.

        console.log("data+");
        console.log(vm.$data);
        console.log("error");
        console.error(err);
        console.error("info:"+info);

        //bugsnagClient.notify(err);
    };
</script>

Añadir nuevo sitio en homestead

Primero agregamos la nueva dirección en host

nano /etc/hosts

Segundo Después en el archivo agregamos una linea para el nuevo dominio en local

192.168.10.10  nuevositio.test

Al terminar de editar ctrl+x , tecla “Y” y yes para salvar los cambios

Tercero editamos el archivo de homestead con el comando

homestead edit

En el archivo, agregamos: la ruta a su carpeta en loca, la ruta en la maquina virtual, a que dominio apunta y la nueva BD en caso necesitemos, en folders,sites y databases, las rutas pueden variar dependiendo del proyecto

---
ip: "192.168.10.10"
memory: 2048
cpus: 1
provider: virtualbox
ssl: true
authorize: ~/id_rsa.pub

keys:
    - ~/id_rsa

folders:
    - map: ~/Documents/Proyectos/nuevositio/web
      to: /home/vagrant/nuevositio/web
    
sites:
    - map: nuevositio.test
      to: /home/vagrant/nuevositio/web/nuevositio/public
   
databases:
    - nuevositio_sb

variables:
    - key: APP_ENV
      value: local

Cuarto, actualizamos la configuración, con el comando

homestead halt && homestead up --provision

Con eso el sitio debió quedar agregado, y si colocamos nuevositio.test en el navegador cargara la web.

Laravel clear

Comandos para resetear rutas, variables de entorno, configuración, etc

composer dump-autoload
php artisan optimize
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

php artisan route:cache
php artisan config:cache

php artisan optimize

Cambiar valores de app a los componentes y viceversa

bidirectional reactive vue app to component
En este apunte intento poder comunicar desde la app al componente y viceversa.

Creamos el componente my-component.vue

<template>
    <div class="div_component">
        <input 
        v-model="message" 
        @keyup="changeMessage">
        <p>Message en componente es: {{message}}</p>
    <div>
</template>

<script>
module.exports = {
    data: function () {
        return {
          message: this.level
        }
      },
    ready:function(){},
    methods: {
        changeMessage: function(){
            console.log(this.message);
            this.$emit('change-message',this.message);
        }
    },
    props: 
    {
        level: {
            required: true
        }
    }
}
</script>

<style>
.div_component {
    background-color: rgb(204, 24, 114);
    padding: 10px;
    margin: 10px;
}
</style>

Donde level, es la propiedad que asignará el padre al componente, y con ese valor iniciamos el valor de message, que será la variable reactiva, después en <template> relacionamos v-model y escuchamos cuando cambie con @keyup.

La parte importante para transmitir los cambios al padre es la función $emit, donde el primer parámetro ‘change-message’ debe coincidir con el nombre que indiquemos al insertar el componente.

Definimos index.html

<!doctype html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/http-vue-loader"></script>
    <style>
        .div_padre{
            background: blueviolet;
            padding: 10px;
        }
    </style>
  </head>

  <body>
    <div id="my-app">
      <div class="div_padre">
        <!--
        -->
        <input
        @keyup="updateMessageChild"  
        v-model="message"></input> 
        <my-component 
        ref="componentChild" 
        :level="message" 
        @change-message="message = $event"></my-component>
      </div>
        
    </div>

    <script src="app.js" ></script>
  </body>
</html>

En index.html agregamos el componente my-component, indicamos ref, para tener una referencia desde app y poder actualizarlo, :level, es para indicar el dato inicial y @change-message, es para escuchar cuando el componente llame a $emit.

Ahora definimos app.js

new Vue({
    el: '#my-app',
    components: {
         'my-component': httpVueLoader('./my-component.vue')
    },
    data () {
        return {
             message:"ejemplo"
            }
        },
    methods: {
        
        updateMessageChild:function(){
            this.$refs.componentChild.message = this.message;
        }
        
    }
});

En app.js, el método updateMessageChild sirve para escuchar los cambios de message y poder actualizar el componente con this.$ref.componentChild donde componentChild es el nombre de la referencia al componente que indicamos por medio de ref.

Se debería comportar así

Código:
*Lo modifique un poco porque codesandbox mostraba errores al código original.

link: https://27rog.csb.app/

Notas:

Revisando básicamente, vue recomienda que cualquier paso de valor entre componentes sea definido y no directo, es posible pero altamente no recomendable.

No he profundizado mucho con este ejemplo, pero para otros casos mas complejos, considerar las limitantes descritas en la documentación https://es.vuejs.org/v2/guide/reactivity.html

Otro articulo interesante que estoy revisando:
https://alligator.io/vuejs/component-communication/

Iconos SVG free, caso de ejemplo: iconos8.es

Como otras páginas de iconos, si quieres de un mayor tamaño tienes que pagar, por ejemplo en https://iconos8.es/, buscamos un icono y al descargarlo aparecen las opciones, donde indica pagar por 480px o si queremos en svg o pdf

Pero podemos obtener SVG desde la misma pagina y después exportarlo en el formato o dimensión que queramos desde alguna herramienta que trabaje con imágenes.

Primero, abrimos el inspector y buscamos el src del formato src=”data:image/svg+xml;base64.. , en caso de no aparecer, lo editamos.

Segundo, copias y pegas en la url y guardar el archivo como .svg .

Tercero, lo insertamos en la herramienta y en ella ya podemos exportarla en el tamaño y formato que necesitemos.

En caso encontremos la etiqueta <svg>, guardamos la etiqueta en un archivo .svg y lo insertamos en el editor, este caso use Adobe XD.

DateTimePickerDialog desde un fragment

Librería DateTime Picker

https://github.com/Kunzisoft/Android-SwitchDateTimePicker

Kunzisoft/Android-SwitchDateTimePicker

//INI DATE
compile 'com.github.Kunzisoft:Android-SwitchDateTimePicker:2.0'
//FIN DATE

// Initialize
        SwitchDateTimeDialogFragment dateTimeDialogFragment = SwitchDateTimeDialogFragment.newInstance(
                "Cuando",
                "Aceptar",
                "Cancelar"
        );

        Calendar current = Calendar.getInstance();
        //minimo 3 horas adelante
        current.add(Calendar.HOUR,3);
        Date min = current.getTime();
// Assign values
        dateTimeDialogFragment.startAtCalendarView();
        dateTimeDialogFragment.set24HoursMode(false);
        //dateTimeDialogFragment.setMinimumDateTime(min);

        //maximo 1 semana adelante
        current.add(Calendar.DAY_OF_MONTH,7);

        dateTimeDialogFragment.setMaximumDateTime(current.getTime());

        //si no tiene fecha guardada, mostrar la actual sino la que guardo
        dateTimeDialogFragment.setDefaultDateTime(dateCuando==null?min:dateCuando);

// Define new day and month format
        try {
            dateTimeDialogFragment.setSimpleDateMonthAndDayFormat(new SimpleDateFormat("dd MMMM", Locale.getDefault()));
        } catch (SwitchDateTimeDialogFragment.SimpleDateMonthAndDayFormatException e) {
            Log.e("ee", e.getMessage());
        }

// Set listener
        dateTimeDialogFragment.setOnButtonClickListener(new SwitchDateTimeDialogFragment.OnButtonClickListener() {
            @Override
            public void onPositiveButtonClick(Date date) {
                // Date is get on positive button click
                // Do something
                dateCuando = date;
                Calendar datePicker = Calendar.getInstance();
                datePicker.setTime(date);


                final String selectedDate = datePicker.get(Calendar.DAY_OF_MONTH) + " / " + (datePicker.get(Calendar.MONTH) +1) + " / " + datePicker.get(Calendar.YEAR) +" "+datePicker.get(Calendar.HOUR) +":"+datePicker.get(Calendar.MINUTE)+" "+(datePicker.get(Calendar.AM_PM)==0?"pm":"am") ;
                etCuandoValue.setText(selectedDate);
            }

            @Override
            public void onNegativeButtonClick(Date date) {
                // Date is get on negative button click
            }
        });

// Show
        dateTimeDialogFragment.show(getFragmentManager(), "dialog_time");

Se vera así

TODO: Mejorable, utilitario que abstrae toda la construcción con los casos típicos

Aumentar memoria PHP para comandos Laravel

Es memoria en general, en mi caso pasa cuando ejecuto comandos laravel procesando archivos grandes

Error

Warning: proc_open(): fork failed – Cannot allocate memory in phar:///usr/local/bin/composer/vendor/symfony/console/Application.php on line 952

Solución

sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
sudo /sbin/mkswap /var/swap.1
sudo /sbin/swapon /var/swap.1

Listado paginado con RecyclerView y SwipeRefreshLayout

Se implementa un recycler view que carga de forma paginada, y refresca cuando es arrastrado hacia abajo.

EN el XML se resalta las lineas importantes, las demás son solo diseño o si se quiere mostrar un mensaje cuando no hay resultados.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <LinearLayout
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"

        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/pick_conductor_in_name"

            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPersonName"
            android:text="Nombre de conductor" />


        <LinearLayout
            android:id="@+id/pick_conductor_layout_list_empty"

            android:layout_width="match_parent"

            android:layout_height="0dp"
            android:gravity="center"
            android:orientation="vertical">

            <TextView
                android:id="@+id/pick_conductor_tv_list_empty"

                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fontFamily="sans-serif-light"
                android:gravity="center"
                android:paddingStart="20dp"
                android:paddingEnd="20dp"
                android:text="No se encontró resultados"
                android:textSize="20sp" />
        </LinearLayout>
        
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/pick_conductor_swipe"

            android:layout_width="match_parent"

            android:layout_height="0dp"
            android:visibility="gone">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/pick_conductor_rv"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </android.support.v4.widget.SwipeRefreshLayout>

    </LinearLayout>
</android.support.constraint.ConstraintLayout>

Para el listado, lo típico es usar el viewholder y enlazar, en este caso, se usara un view, que lo usara el adapter y el modelo del objeto.

/**
 * Created by Javier Solis (solis.unmsm@gmail.com)  on 5/11/20
 */
public class PickConductorModel {
    public int id;
    public String name;
}
....

/**
 * Created by Javier Solis (solis.unmsm@gmail.com)  on 5/11/20
 */
public class PickConductorItemView extends LinearLayout implements GenericRecyclerViewRow<PickConductorModel> {

    @BindView(R.id.pick_conductor_item_name)
    TextView tvName;

    /**....Constructors...**/

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ButterKnife.bind(this,getRootView());
    }


    @Override
    public void showData(PickConductorModel item) {
        //setear valores 
        UtilTextView.setObjectValueOrNull(tvName,item.name);
    }


}
<?xml version="1.0" encoding="utf-8"?>
<!--ITEM VIEW-->
<com.ejemplo.presentation.itemview.PickConductorItemView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:padding="10dp">


                <TextView android:id="@+id/pick_conductor_item_name"
                    android:textSize="12sp"
                    android:text="--"
                    android:textStyle="bold" android:layout_width="wrap_content"
                    android:layout_height="wrap_content" >
                </TextView>

</com.ejemplo.presentation.itemview.PickConductorItemView>

Finalmente se instancia la clase Loader con los datos necesario para su construcción:

...

/**
 * Created by Javier Solis (solis.unmsm@gmail.com)  on 5/11/20
 */
public class PickConductorDialog extends DialogFragment {

    @BindView(R.id.pick_conductor_in_name)
    EditText inNameConductor;

    //@BindView(R.id.pick_conductor_rv)
    //RecyclerView rvConductores;

    LoaderListPaginate loaderListPaginate;

     //OPTIONAL
    @OnTextChanged(R.id.pick_conductor_in_name)
    public void onChangeNameConductor(){
        if(inNameConductor.getText().toString().length()>3){
            //search
        }
        else{
            //clean list
        }
    }

    public static PickConductorDialog newInstance() {
        PickConductorDialog frag = new PickConductorDialog();
        Bundle args = new Bundle();
        frag.setArguments(args);
        return frag;
    }


    private OnPickClick pickClick;
    public void setPickClick(OnPickClick pickClick) {
        this.pickClick = pickClick;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.dialog_pick_conductor, null);


        ButterKnife.bind(this,view);
        //setCancelable(false);

        builder.setView(view);
        Dialog dialog = builder.create();

        dialog.getWindow().setBackgroundDrawable(
                new ColorDrawable(Color.WHITE));


        configLoadesList(view);
        return dialog;

    }

    private void configLoadesList(View v) {
        loaderListPaginate = LoaderListPaginate.newInstante(
                this,
                v,
                R.id.pick_conductor_rv,
                R.id.pick_conductor_swipe,
                R.id.pick_conductor_layout_list_empty,
                R.layout.pick_conductor_item,
                new LoaderListPaginate.ILoaderPaginate() {
                    @Override
                    public TaskListGeneral makeTask(int currentPage) {
                        return new ConductorListPickerTask(null,makeRequest(currentPage));
                    }

                    @Override
                    public void onItemClick(int position) {
                        DtoConductor dto = (DtoConductor) loaderListPaginate.task.listDtoSource.get(position);
                        //AppNavigation.launchAlimentacionDetail(dto.getAlimentacion__id());
                    }
                },
                //Mensajes cuando inicia, falla y exito
                R.string.conductor_process_list__init,
                R.string.conductor_process_list__fail,
                R.string.conductor_process_list__ok

        );

        loaderListPaginate.loadList();
    }

    private DtoConductorListPickerRequest makeRequest(int currentPage) {
        DtoConductorListPickerRequest request = new DtoConductorListPickerRequest();
        request.page = currentPage;
       //incialmente se envía el nombre vacío
        request.setConductor__nombre("");
        return  request;
    }


    public interface OnPickClick{
        void onPick(int id,String name);
    }
}
Bug

Para el parser de listado, causo un error con el tipo generico tree ( java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to DtoConductor ) a la clase del listado, hace el cast obteniendo el class a partir del tipo

...
public ArrayList<DtoConductor> cast(List<DtoConductor> listDtos){
        Gson gson= new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
                .create();
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Date.class, new DateTypeDeserializer());
        gson=gsonBuilder.create();
        Type typeMyType = new TypeToken<ArrayList<DtoConductor>>(){}.getType();
        ArrayList<DtoConductor> myObject = gson.fromJson(gson.toJson(listDtos), typeMyType);

        return myObject;
    }
...

La clase DateTypeDeserializer, es para pasar los string con formato de fecha en tipo Date en el parseo.

/**
 * by Javier Solis @JavierTwiteando
 * Github http://bit.ly/onGithub
 */


import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

public class DateTypeDeserializer implements JsonDeserializer<Date> {
    private static final String[] DATE_FORMATS = new String[]{"yyyy-MM-dd HH:mm:ss"};
    private static final String[] DATE_FORMATS_ALL = new String[]{"yyyy-MM-dd\'T\'HH:mm:ssZ", "yyyy-MM-dd\'T\'HH:mm:ss", "yyyy-MM-dd", "EEE MMM dd HH:mm:ss z yyyy", "HH:mm:ss", "yyyy-MM-dd\'T\'HH:mm:ss", "MM/dd/yyyy HH:mm:ss aaa", "yyyy-MM-dd\'T\'HH:mm:ss.SSSSSS", "yyyy-MM-dd\'T\'HH:mm:ss.SSSSSSS", "yyyy-MM-dd\'T\'HH:mm:ss.SSSSSSS\'Z\'", "MMM d\',\' yyyy H:mm:ss a"};

    public DateTypeDeserializer() {
    }

    public Date deserialize(JsonElement jsonElement, Type typeOF, JsonDeserializationContext context) throws JsonParseException {
        String[] var4 = DATE_FORMATS;
        int var5 = var4.length;
        int var6 = 0;

        while(var6 < var5) {
            String format = var4[var6];

            try {
                return (new SimpleDateFormat(format, Locale.US)).parse(jsonElement.getAsString());
            } catch (ParseException var9) {
                ++var6;
            }
        }

        throw new JsonParseException("Unparseable date: \"" + jsonElement.getAsString() + "\". Supported formats: \n" + Arrays.toString(DATE_FORMATS));
    }
}

Como resultado se ve así:

**NOTA: Se oculto el texto de nombre del conductor, en el dialogo picker conductor.

Mejorable, creando un view con un layout empty por defecto, con posibilidad de editar y definir lo mínimo, se ahorrar el la construcción y errores por las referencias.